Revamped concurrency API
Fawzi Mohamed
fmohamed at mac.com
Mon Oct 12 15:21:37 PDT 2009
>> If anyone has ideas, suggestions, and code to help defining a new
>> concurrency API for D, please post.
I think that the correct way to handle concurrency is through a
library, D is flexible enough, so that a library can be used, and then
one can even write special handlers for special cases.
In blip (http://dsource.org/project/blip) I actually did exactly that,
in D 1.0 for two reasons:
1) I need 64 bits
2) concurrency is complex, and I had a hard time already with bugs in
the stable branch, furthermore the possibility of breakage in very
sensitive code that I don't want to touch too often by language changes
was not appealing to me.
That said there is a feature that would help much D 1.0, and that I
would really love to have.
I know that D 1.0 is frozen and stuff... but I will try to ask all the same...
real closures from delegates when you put a "new" in front of them
new delegate(ref int x){x+=y}
would create a closure (allocating y).
I know that D 2.0 has basically that by default (and scope to get the
previous behavior), but as I said D 2.0 is not yet an option for me (I
want to work on my projects, I think I am already doing enough for the
community), so I thought that asking for a feature that does not break
existing D 1.0 code and is in some way already implemented in D 2.0
could be worth trying :)
The other D 2.0 features are nice, I do like structure
constructors/destructors, post blits, template constraints, const...,
well shared I am not so sure about, but anyway all of them are not
needed for concurrency.
Now about the concurrency infrastructure, here I will discuss SMP
parallelization, there is also a more coarse grained parallelization
that needs another approach (MPI and agent based model, for now I just
wrapped mpi and serialization in a way that could be implemented
directly on tcp, and allow to easily communicate arbitrary objects).
The concurrency has two sides one is the user/programmer side and the
other the efficient realization, I will discuss the user level api, as
I realized it in Blip, which is optimized for recursive operations that
have to be finished (i.e computations to be performed, not to simulate
concurrent systems).
The idea is to separate each tasks in chunks that are as small as
possible, while still being large enough so that the switching time is
small wrt. to the computing time.
This subdivision typically is not directly dependent on the number of
processors
----
Task is a class the represents a task, it has a string name (for
debugging purposes) and can be initialized with a delegate, a function,
a fiber or a generator (in two flavors).
There are some optimizations to make allocation cheaper.
a task can spawn subtasks, and it "knows" how many subtasks there are
executing.
a task is considered finished when the task is complete and all its
subtasks are completed
you can append operations to be executed after a task has finished executing.
you can wait for a task to finish (but try avoiding it, addint the task
to the onFinish of the task is much more efficient).
a task has a given level, subtasks have level+1, and tasks that cannot
have subtasks have a very high level (int.max/2).
a new task can be submitted with
t.submit()
or t.submitYield()
submitYield submits the current task and possibly stops the current
one, this together with the fact that tasks with higher level are
executed before tasks with lower level means that execution is
preferentially a depth first reduction which minimizes the number of
suspended tasks (I have also task stealing that steals preferentially
the tasks with lowest level, but that part is not yet really used).
other important operations are delay and resubmitDelayed that allow one
to delay a task, for example when you have to do i/o and you use a
method like epoll you can delay the current task, add the file handler
to the one to control, and execute resubmitDelayed when that handler
has new data.
executeNow is useful to execute a task synchronously.
That is basically the core that one has to know, the library is
actually much more rich, but for a typical usage this is enough.
With it one can write code like:
import blip.parallel.WorkManager;
class SubProblem{
void execute(){
if(problem is large){
SubProblem firstHalf=...;
Task("subproblem1",&firstHalf.execute).autorelease.submitYield();
SubProblem secondHalf=...;
Task("subproblem2",&secondHalf.execute).autorelease.submitYield();
} else {
direct solver
}
}
}
solveProblem(){
if(problemIsLarge){
SubProblem wholeProblem=...;
Task("solveProblem",&wholeProblem.execute).executeNow(default);
} else {
direct solver
}
}
just to show a very basic divide and conquer approach
More information about the Digitalmars-d
mailing list