Re: What's the "right" way to do openmp-style parallelism?

2015-09-09 Thread Russel Winder via Digitalmars-d-learn
On Tue, 2015-09-08 at 07:33 +, Dominikus Dittes Scherkl via
Digitalmars-d-learn wrote:
> On Tuesday, 8 September 2015 at 05:50:30 UTC, Russel Winder wrote:
> > void main() {
> >   immutable imax = 10;
> >   immutable jmax = 10;
> >   float[imax][jmax] x;
> >   foreach(int j; 1..jmax){
> > foreach(int i, ref item; parallel(x[j-1])){
> >   x[j][i] = complicatedFunction(i, item);
> > }
> >   }
> > }
> > 
> > (though sadly, this doesn't compile for a reason I can't fathom 
> > instantly)
> Hmm. Shouldn't you instead parallel the outer loop?

Can't do that because it is a pipeline: the current computation is
input to the next one. As far as I can tell there is no way the code as
presented can be parallelized in the general case. If there were some
guarantees on complicatedFunction, then it is a different game.

-- 
Russel.
=
Dr Russel Winder  t: +44 20 7585 2200   voip: sip:russel.win...@ekiga.net
41 Buckmaster Roadm: +44 7770 465 077   xmpp: rus...@winder.org.uk
London SW11 1EN, UK   w: www.russel.org.uk  skype: russel_winder



signature.asc
Description: This is a digitally signed message part


Re: What's the "right" way to do openmp-style parallelism?

2015-09-08 Thread Dominikus Dittes Scherkl via Digitalmars-d-learn

On Tuesday, 8 September 2015 at 05:50:30 UTC, Russel Winder wrote:

void main() {
  immutable imax = 10;
  immutable jmax = 10;
  float[imax][jmax] x;
  foreach(int j; 1..jmax){
foreach(int i, ref item; parallel(x[j-1])){
  x[j][i] = complicatedFunction(i, item);
}
  }
}

(though sadly, this doesn't compile for a reason I can't fathom 
instantly)

Hmm. Shouldn't you instead parallel the outer loop?



Re: What's the "right" way to do openmp-style parallelism?

2015-09-07 Thread Russel Winder via Digitalmars-d-learn
On Mon, 2015-09-07 at 02:56 +, Charles via Digitalmars-d-learn
wrote:
> […]
> 
> x = float[imax][jmax]; //x is about 8 GB of floats
> for(j = 0; j < jmax; j++){
> //create some local variables.
>  for(i = 0; i < imax; i++){
>  x[j][i] = complicatedFunction(i, x[j-1], other, local, 
> variables);
>  }
> }

So as to run things through a compiler, I expanded the code fragment
to:

float complicatedFunction(int i, float[] x) pure {
  return 0.0;
}

void main() {
  immutable imax = 10;
  immutable jmax = 10;
  float[imax][jmax] x;
  for(int j = 1; j < jmax; j++){
for(int i = 0; i < imax; i++){
  x[j][i] = complicatedFunction(i, x[j-1]);
}
  }
}

Hopefully this is an accurate representation of the original code. Note
the change in the j iteration since j-1 is being used as an index. Of
course I immediately wanted to change this to:

float complicatedFunction(int i, float[] x) pure {
  return 0.0;
}

void main() {
  immutable imax = 10;
  immutable jmax = 10;
  float[imax][jmax] x;
  foreach(int j; 1..jmax){
foreach(int i; 0..imax){
  x[j][i] = complicatedFunction(i, x[j-1]);
}
  }
}

Hopefully this is not now wrong as a representation of the original
problem.

> In C, I'd just stick a #pragma omp parallel for around the inner 
> loop (since the outer loop obviously can't be parallelized).

I would hope you meant C++ , not C, there. ;-)

I am not sure OpenMP would work to parallelize your C++ (or C) code.

Given that complicatedFunction has access to the whole of x[j-1] there
could be coupling between x[j-1][m] and x[j-1][n] in the function which
would lead to potentially different results being computed in the
sequential and parallel cases. This is not a C/C++/D thing this is a
data coupling thing.

So although Meta suggested a parallel foreach (the D equivalent of
OpenMP parallel for pragma), something along the lines:

import std.parallelism: parallel;

float complicatedFunction(int i, float[] x) pure {
  return 0.0;
}

void main() {
  immutable imax = 10;
  immutable jmax = 10;
  float[imax][jmax] x;
  foreach(int j; 1..jmax){
foreach(int i, ref item; parallel(x[j-1])){
  x[j][i] = complicatedFunction(i, item);
}
  }
}

(though sadly, this doesn't compile for a reason I can't fathom
instantly) this brings into stark relieve the fact that there is a
potential coupling between x[j-1][m] and x[j-1][n] which means
enforcing parallelism here will almost certainly result in the wrong
values being calculated.

This is a standard pipeline describable with a map, something along the
lines of:

import std.algorithm: map;

float complicatedFunction(int i, float[] x) pure {
  return 0.0;
}

void main() {
  immutable imax = 10;
  immutable jmax = 10;
  float[imax][jmax] x;
  foreach(int j; 1..jmax){
x[j] = map!(a => complicatedFunction(a, x[j-1]))(x[j-1]);
  }
}

(but this also has a compilation error, which I hope someone can fix…)

This is the step prior to using parallel map, but cast in this way
highlights that in order to then be parallelized at all in any way,
complicatedFunction must have no couplings between x[j-1][m] and x[j
-1][n].

(I am guessing this is some form of cellular automaton or some Markov
process problem?)

> How should I go about this in D? I want to avoid copying data 
> around if it's possible since these arrays are huge.

Indeed. With C, C++, D, (Go, Rust,…) you have to use references (aka
pointers) and hope you do not get any ownership problems. It might be
interesting to see whether a language such as Haskell, which has copy
semantics but optimizes as much as it can away, would fare with this.

-- 
Russel.
=
Dr Russel Winder  t: +44 20 7585 2200   voip: sip:russel.win...@ekiga.net
41 Buckmaster Roadm: +44 7770 465 077   xmpp: rus...@winder.org.uk
London SW11 1EN, UK   w: www.russel.org.uk  skype: russel_winder



signature.asc
Description: This is a digitally signed message part


Re: What's the "right" way to do openmp-style parallelism?

2015-09-06 Thread Meta via Digitalmars-d-learn

On Monday, 7 September 2015 at 02:56:04 UTC, Charles wrote:

Friends,

I have a program that would be pretty easy to parallelize with 
an openmp pragra in C. I'd like to avoid the performance cost 
of using message passing, and the shared qualifier seems like 
it's enforcing guarantees I don't need. Essentially, I have


x = float[imax][jmax]; //x is about 8 GB of floats
for(j = 0; j < jmax; j++){
//create some local variables.
for(i = 0; i < imax; i++){
x[j][i] = complicatedFunction(i, x[j-1], other, local, 
variables);

}
}

In C, I'd just stick a #pragma omp parallel for around the 
inner loop (since the outer loop obviously can't be 
parallelized).


How should I go about this in D? I want to avoid copying data 
around if it's possible since these arrays are huge.


Cheers,
Charles.


I believe this is what you want: 
http://dlang.org/phobos/std_parallelism.html#.parallel.


I believe that all you would need to change is to have your inner 
loop become:


foreach(i, ref f; x[j].parallel)
{
f = complicatedFUnction(i, x[j-1], etc...);
}

Don't quote me on that, though, as I'm not very experienced with 
std.parallelism.


What's the "right" way to do openmp-style parallelism?

2015-09-06 Thread Charles via Digitalmars-d-learn

Friends,

I have a program that would be pretty easy to parallelize with an 
openmp pragra in C. I'd like to avoid the performance cost of 
using message passing, and the shared qualifier seems like it's 
enforcing guarantees I don't need. Essentially, I have


x = float[imax][jmax]; //x is about 8 GB of floats
for(j = 0; j < jmax; j++){
//create some local variables.
for(i = 0; i < imax; i++){
x[j][i] = complicatedFunction(i, x[j-1], other, local, 
variables);

}
}

In C, I'd just stick a #pragma omp parallel for around the inner 
loop (since the outer loop obviously can't be parallelized).


How should I go about this in D? I want to avoid copying data 
around if it's possible since these arrays are huge.


Cheers,
Charles.


Data parallel programming with OpenMP and Cilk Plus

2013-09-19 Thread Paul Jurczak
I would like to know if there are any obvious show-stoppers with 
using OpenMP and Cilk Plus with D through C interface? In the 
simplest case I have an array float[N] and I want to pass it to C 
function, which will modify it using OpenMP and Cilk Plus.


Re: openMP

2012-10-05 Thread Pragma Tix

On Thursday, 4 October 2012 at 18:34:29 UTC, dsimcha wrote:



I don't have time to look at these right now, but I'll 
definitely look at them sometime soon.  Thanks for the info.


You will finds this interesting too, a code snippet from Daniel
Keep.

http://www.dsource.org/projects/scrapple/browser/trunk/future/future.d



Re: openMP

2012-10-05 Thread David Nadlinger

On Friday, 5 October 2012 at 10:26:57 UTC, Pragma Tix wrote:

You will finds this interesting too, a code snippet from Daniel
Keep.

http://www.dsource.org/projects/scrapple/browser/trunk/future/future.d


The code only allows you to do something equivalent to »auto t = 
std.parallelism.task!dg(); t.executeInNewThread()«. So no, I 
don't think David, being the author of std.parallelism, would 
find it interesting… ;)


David


Re: openMP

2012-10-04 Thread David Nadlinger

On Wednesday, 3 October 2012 at 23:02:25 UTC, dsimcha wrote:
So the process which creates the future is a Task that 
executes in a different thread than the caller?  And an 
alternative way that a value might become available in the 
future is e.g. if it's being retrieved from some slow I/O 
process like a database or network?


Yes.


For example, let's say you are writing a function which 
computes a complex database query from its parameters and then 
submits it to your query manager/connection pool/… for 
asynchronous execution. You cannot use std.parallelism.Task in 
this case, because there is no way of expressing the process 
which retrieves the result as a delegate running inside a 
TaskPool.


Ok, I'm confused here.  Why can't the process that retrieves 
the result be expressed as a delegate running in a TaskPool or 
a new thread?


Because you already have a system in place for managing these 
tasks, which is separate from std.parallelism. A reason for this 
could be that you are using a third-party library like libevent. 
Another could be that the type of workload requires additional 
problem knowledge of the scheduler so that different tasks don't 
tread on each others's toes (for example communicating with some 
servers via a pool of sockets, where you can handle several 
concurrent requests to different servers, but can't have two task 
read/write to the same socket at the same time, because you'd 
just send garbage).


Really, this issue is just about extensibility and/or 
flexibility. The design of std.parallelism.Task assumes that all 
values which becomes available at some point in the future are 
the product of a process for which a TaskPool is a suitable 
scheduler. C++ has std::future separate from std::promise, C# has 
Task vs. TaskCompletionSource, etc.



The second problem with std.parallelism.Task is that your only 
choice is polling (or blocking, for that matter). Yes, 
callbacks are a hairy thing to do if you can't be sure what 
thread they are executed on, but not having them severely 
limits the power of your abstraction, especially if you are 
dealing with non-CPU-bound tasks (as many of today's modern 
use cases are).


I'm a little confused about how the callbacks would be used 
here.
 Is the idea that some callback would be called when the task 
is finished?  Would it be called in the worker thread or the 
thread that submitted the task to the pool?  Can you provide a 
use case?


Maybe using the word callback was a bit misleading, but it 
callback would be invoked on the worker thread (or by whoever 
invokes the hypothetical Future.complete(result) method).


Probably most trivial use case would be to set a condition 
variable in it in order to implement a waitAny(Task[]) method, 
which waits until the first of a set of tasks is completed. Ever 
wanted to wait on multiple condition variables? Or used select() 
with multiple sockets? This is what I mean.


For more advanced/application-level use cases, just look at any 
use of ContinueWith in C#. std::future::then() is also proposed 
for C++, see e.g. 
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3327.pdf.


I didn't really read the the N3327 paper in detail, but from a 
brief look it seems to be a nice summary of what you might want 
to do with tasks/asynchronous results – I think you could find 
it an interesting read.


David


Re: openMP

2012-10-04 Thread dsimcha
Ok, I think I see where you're coming from here.  I've replied to 
some points below just to make sure and discuss possible 
solutions.


On Thursday, 4 October 2012 at 16:07:35 UTC, David Nadlinger 
wrote:

On Wednesday, 3 October 2012 at 23:02:25 UTC, dsimcha wrote:


Because you already have a system in place for managing these 
tasks, which is separate from std.parallelism. A reason for 
this could be that you are using a third-party library like 
libevent. Another could be that the type of workload requires 
additional problem knowledge of the scheduler so that different 
tasks don't tread on each others's toes (for example 
communicating with some servers via a pool of sockets, where 
you can handle several concurrent requests to different 
servers, but can't have two task read/write to the same socket 
at the same time, because you'd just send garbage).


Really, this issue is just about extensibility and/or 
flexibility. The design of std.parallelism.Task assumes that 
all values which becomes available at some point in the 
future are the product of a process for which a TaskPool is a 
suitable scheduler. C++ has std::future separate from 
std::promise, C# has Task vs. TaskCompletionSource, etc.


I'll look into these when I have more time, but I guess what it 
boils down to is the need to separate the **abstraction** of 
something that returns a value later (I'll call that 
**abstraction** futures) from the **implementation** provided by 
std.parallelism (I'll call this **implementation** tasks), which 
was designed only with CPU-bound tasks and multicore in mind.


On the other hand, I like std.parallelism's simplicity for 
handling its charter of CPU-bound problems and multicore 
parallelism.  Perhaps the solution is to define another Phobos 
module that models the **abstraction** of futures and provide an 
adapter of some kind to make std.parallelism tasks, which are a 
much lower-level concept, fit this model.  I don't think the 
**general abstraction** of a future should be defined in 
std.parallelism, though.  std.parallelism includes 
parallelism-oriented things besides tasks, e.g. parallel map, 
reduce, foreach.  Including a more abstract model of values that 
become available later would make its charter too unfocused.




Maybe using the word callback was a bit misleading, but it 
callback would be invoked on the worker thread (or by whoever 
invokes the hypothetical Future.complete(result) method).


Probably most trivial use case would be to set a condition 
variable in it in order to implement a waitAny(Task[]) method, 
which waits until the first of a set of tasks is completed. 
Ever wanted to wait on multiple condition variables? Or used 
select() with multiple sockets? This is what I mean.


Well, implementing something like ContinueWith or Future.complete 
for std.parallelism tasks would be trivial, and I see how waitAny 
could easily be implemented in terms of this.  I'm not sure I 
want to define an API for this in std.parallelism, though, until 
we have something like a std.future and the **abstraction** of a 
future is better-defined.




For more advanced/application-level use cases, just look at any 
use of ContinueWith in C#. std::future::then() is also proposed 
for C++, see e.g. 
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3327.pdf.


I didn't really read the the N3327 paper in detail, but from a 
brief look it seems to be a nice summary of what you might want 
to do with tasks/asynchronous results – I think you could 
find it an interesting read.


I don't have time to look at these right now, but I'll definitely 
look at them sometime soon.  Thanks for the info.




Re: openMP

2012-10-04 Thread dsimcha
On Thursday, 4 October 2012 at 16:07:35 UTC, David Nadlinger 
wrote:
For more advanced/application-level use cases, just look at any 
use of ContinueWith in C#. std::future::then() is also proposed 
for C++, see e.g. 
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3327.pdf.


I didn't really read the the N3327 paper in detail, but from a 
brief look it seems to be a nice summary of what you might want 
to do with tasks/asynchronous results – I think you could 
find it an interesting read.


David


Thanks for posting this.  It was an incredibly useful read for 
me!  Given that the code I write is generally compute-intensive, 
not I/O intensive, I'd never given much thought to the value of 
futures in I/O intensive code before this discussion.  I stand by 
what I said before:  Someone (not me because I'm not intimately 
familiar with the use cases; you might be qualified) should write 
a std.future module for Phobos that properly models the 
**abstraction** of a future.  It's only tangentially relevant to 
std.parallelism's charter, which includes both a special case of 
futures that's useful to SMP parallelism and other parallel 
computing constructs.  Then, we should define an adapter that 
allows std.parallelism Tasks to be modeled more abstractly as 
futures when necessary, once we've nailed down what the future 
**abstraction** should look like.


Re: openMP

2012-10-03 Thread Russel Winder
On Tue, 2012-10-02 at 23:13 +0200, Farmer wrote:
 And is there also a pragma omp critical analogon?

No. The D approach in std.parallelism is to offer explicit parallel
constructs that also work on a uniprocessor. The OpenMP approach is to
provide meta-data to allow the compiler to parallelize sequential code.

Although OpenMP is high-profile in the C, C++ and Fortran world, it's
raison d'être is to be able to use sequential code in a parallel
context. Now that C++ has made the jump to using futures and
asynchronous function calls as an integral part of the language, I think
we will see it drift away from the OpenMP style camp much more towards
the TBB style camp. I am not sure if TBB is the right framework, but a
drift twowards frameworks that are parallel first and sequential as a
special case does seem to be on the cards.

SO rather than working with annotation based systems such as OpenMP, I
think D is right to be using parallel map, parallel reduce as functions
rather than seeing explicit iterations. Functional languages have always
had this. Python, Groovy, Ruby, Clojure brought the ideas to mainstream
platforms, as did Scala and C++ (cf. std::foreach). Java 8 will take up
this approach. So library based iteration is with us as the tool for
abstracting away the details. Obviously we will still have explicit
iteration in imperative languages but significantly less than in the
past.

To summarize: OpenMP and parallelizing explicit iteration is backward
looking, library calls parallelizing using implicit iteration is the
future.

-- 
Russel.
=
Dr Russel Winder  t: +44 20 7585 2200   voip: sip:russel.win...@ekiga.net
41 Buckmaster Roadm: +44 7770 465 077   xmpp: rus...@winder.org.uk
London SW11 1EN, UK   w: www.russel.org.uk  skype: russel_winder


signature.asc
Description: This is a digitally signed message part


Re: openMP

2012-10-03 Thread Nick Sabalausky
On Wed, 03 Oct 2012 09:08:47 +0100
Russel Winder rus...@winder.org.uk wrote:
 Now that C++ has made the jump to using futures and
 asynchronous function calls as an integral part of the language,

Speaking of, do we have futures in D yet? IIRC, way back last time I
asked about it there was something that needed taken care of first,
though I don't remember what. If we don't have them ATM, is there
currently anything in the way of actually creating them?



Re: openMP

2012-10-03 Thread dsimcha
Unless we're using different terminology here, futures are just 
std.parallelism Tasks.


On Wednesday, 3 October 2012 at 10:17:41 UTC, Nick Sabalausky 
wrote:

On Wed, 03 Oct 2012 09:08:47 +0100
Russel Winder rus...@winder.org.uk wrote:

Now that C++ has made the jump to using futures and
asynchronous function calls as an integral part of the 
language,


Speaking of, do we have futures in D yet? IIRC, way back last 
time I
asked about it there was something that needed taken care of 
first,
though I don't remember what. If we don't have them ATM, is 
there

currently anything in the way of actually creating them?





Re: openMP

2012-10-03 Thread David Nadlinger

On Wednesday, 3 October 2012 at 14:10:57 UTC, dsimcha wrote:
Unless we're using different terminology here, futures are just 
std.parallelism Tasks.


No, std.parallelism.Tasks are not really futures – they offer a 
constrained [1] future interface, but couple this with the notion 
that a Task can be executed at some point on a TaskPool chosen by 
the user. Because of this, I had to implement my own futures for 
the Thrift async stuff, where I needed a future as a promise [2] 
by an invoked entity that it kicked off a background activity 
which will eventually return a value, but which the users can't 
»start« or choose to »execute it now«, as they can with Tasks.


If TaskPool had an execute() method which took a delegate to 
execute (or a »Task«, for that matter) and returned a new 
object which serves as a »handle« with wait()/get()/… 
methods, _that_ would (likely) be a future.


David


[1] Constrained in the sense that it is only meant for 
short-/synchronous-running tasks and thus e.g. offer no callback 
mechanism.


[2] Let's not get into splitting hairs regarding the exact 
meaning of »Future« vs. »Promise«, especially because C++11 
introduced a new interpretation to the mix.


Re: openMP

2012-10-03 Thread dsimcha
Ok, now I vaguely remember seeing stuff about futures in your 
Thrift code and wondering why it was there.  I'm a little big 
confused about what you want.  If I understand correctly, 
std.parallelism can already do it pretty easily, but maybe the 
docs need to be improved a little to make it obvious how to.


All you have to do is something like this:

auto createFuture() {
auto myTask = task!someFun();  // Returns a _pointer_ to a 
Task.

taskPool.put(myTask);  // Or myTask.executeInNewThread();

// A task created with task() can outlive the scope it was 
created in.
// A scoped task, created with scopedTask(), cannot.  This is 
safe,

// since myTask is NOT scoped and is a _pointer_ to a Task.
return myTask;
}

In this case myTask is already running using the execution 
resources specified in createFuture().  Does this do what you 
wanted?  If so, I'll clarify the documentation.  If not, please 
clarify what you needed and the relevant use cases so that I can 
fix std.parallelism.


On Wednesday, 3 October 2012 at 15:50:38 UTC, David Nadlinger 
wrote:

On Wednesday, 3 October 2012 at 14:10:57 UTC, dsimcha wrote:
Unless we're using different terminology here, futures are 
just std.parallelism Tasks.


No, std.parallelism.Tasks are not really futures – they offer 
a constrained [1] future interface, but couple this with the 
notion that a Task can be executed at some point on a TaskPool 
chosen by the user. Because of this, I had to implement my own 
futures for the Thrift async stuff, where I needed a future as 
a promise [2] by an invoked entity that it kicked off a 
background activity which will eventually return a value, but 
which the users can't »start« or choose to »execute it 
now«, as they can with Tasks.


If TaskPool had an execute() method which took a delegate to 
execute (or a »Task«, for that matter) and returned a new 
object which serves as a »handle« with wait()/get()/… 
methods, _that_ would (likely) be a future.


David


[1] Constrained in the sense that it is only meant for 
short-/synchronous-running tasks and thus e.g. offer no 
callback mechanism.


[2] Let's not get into splitting hairs regarding the exact 
meaning of »Future« vs. »Promise«, especially because C++11 
introduced a new interpretation to the mix.




Re: openMP

2012-10-03 Thread David Nadlinger

On Wednesday, 3 October 2012 at 19:42:07 UTC, dsimcha wrote:
If not, please clarify what you needed and the relevant use 
cases so that I can fix std.parallelism.


In my use case, conflating the notion of a future, i.e. a value 
that becomes available at some point in the future, with the 
process which creates that future makes no sense.


For example, let's say you are writing a function which computes 
a complex database query from its parameters and then submits it 
to your query manager/connection pool/… for asynchronous 
execution. You cannot use std.parallelism.Task in this case, 
because there is no way of expressing the process which retrieves 
the result as a delegate running inside a TaskPool.


Or, say you want to write an aggregator, combining the results 
of several futures together, again offering the same future 
interface (maybe an array of the original result types) to 
consumers. Again, there is no computation-bound part to that at 
all, which would make sense to run on a TaskPool – you are only 
waiting on the other tasks to finish.


The second problem with std.parallelism.Task is that your only 
choice is polling (or blocking, for that matter). Yes, callbacks 
are a hairy thing to do if you can't be sure what thread they are 
executed on, but not having them severely limits the power of 
your abstraction, especially if you are dealing with 
non-CPU-bound tasks (as many of today's modern use cases are).


For example, something my mentor asked to implement for Thrift 
during last year's GSoC was a feature which allows to send a 
request out to a pool of servers concurrently, returning the 
first one of the results (apparently, this mechanism is used as a 
sharding mechanism in some situations – if a server doesn't 
have the data, it simply ignores the request). How would you 
implement something like that as a function Task[] - Task? For 
what it's worth, Task in C# (which is quite universally praised 
for its take on the matter) also has a »ContinueWith« method 
which is really just a completion callback mechanism.


std.parallelism.Task is great for expressing local 
resource-intensive units of work (and fast!), but I think it is 
to rigid and specialized for that case to be generally useful.


David


Re: openMP

2012-10-03 Thread dsimcha
On Wednesday, 3 October 2012 at 21:02:07 UTC, David Nadlinger 
wrote:

On Wednesday, 3 October 2012 at 19:42:07 UTC, dsimcha wrote:
If not, please clarify what you needed and the relevant use 
cases so that I can fix std.parallelism.


In my use case, conflating the notion of a future, i.e. a value 
that becomes available at some point in the future, with the 
process which creates that future makes no sense.


So the process which creates the future is a Task that executes 
in a different thread than the caller?  And an alternative way 
that a value might become available in the future is e.g. if it's 
being retrieved from some slow I/O process like a database or 
network?




For example, let's say you are writing a function which 
computes a complex database query from its parameters and then 
submits it to your query manager/connection pool/… for 
asynchronous execution. You cannot use std.parallelism.Task in 
this case, because there is no way of expressing the process 
which retrieves the result as a delegate running inside a 
TaskPool.


Ok, I'm confused here.  Why can't the process that retrieves the 
result be expressed as a delegate running in a TaskPool or a new 
thread?




Or, say you want to write an aggregator, combining the 
results of several futures together, again offering the same 
future interface (maybe an array of the original result types) 
to consumers. Again, there is no computation-bound part to that 
at all, which would make sense to run on a TaskPool – you are 
only waiting on the other tasks to finish.


Maybe I'm just being naive since I don't understand the use 
cases, but why couldn't you just create an array of Task objects?




The second problem with std.parallelism.Task is that your only 
choice is polling (or blocking, for that matter). Yes, 
callbacks are a hairy thing to do if you can't be sure what 
thread they are executed on, but not having them severely 
limits the power of your abstraction, especially if you are 
dealing with non-CPU-bound tasks (as many of today's modern 
use cases are).


I'm a little confused about how the callbacks would be used here. 
 Is the idea that some callback would be called when the task is 
finished?  Would it be called in the worker thread or the thread 
that submitted the task to the pool?  Can you provide a use case?




For example, something my mentor asked to implement for Thrift 
during last year's GSoC was a feature which allows to send a 
request out to a pool of servers concurrently, returning the 
first one of the results (apparently, this mechanism is used as 
a sharding mechanism in some situations – if a server doesn't 
have the data, it simply ignores the request).


First one of the results == the result produced by the the 
first server to return anything?


How would you implement something like that as a function 
Task[] - Task? For what it's worth, Task in C# (which is quite 
universally praised for its take on the matter) also has a 
»ContinueWith« method which is really just a completion 
callback mechanism.


I'll look into ContinueWith and see if it's implementable in 
std.parallelism without breaking anything.




std.parallelism.Task is great for expressing local 
resource-intensive units of work (and fast!), but I think it is 
to rigid and specialized for that case to be generally useful.


Right.  I wrote std.parallelism with resource-intensive units of 
work in mind because that's the use case I was familiar with.  It 
was designed first and foremost to make using SMP parallelism 
_simple_.  In hindsight I might have erred to much on the side of 
making simple things simple vs. complicated things possible or 
over-specialized it and avoided solving the an important, more 
general problem.  I'll try to understand your use cases and see 
if they can be addressed without making simple things more 
complicated.


I think the best way you could help me understand what I've 
overlooked in std.parallelism's design is to give a quick n' 
dirty example of how an API that does what you want would be 
used.  Even more generally, any _concise, concrete_ use cases, 
even toy use cases, would be a huge help.


openMP

2012-10-02 Thread Farmer

Hi,
I am tempted to start D programming but for me it is crucrial to 
be able to parallelize for-loops as can be done with openMP for 
C/C++ (mainly @pragma omp parallel for, @pragma omp critical).
I have already seen the std.parallelism library but I'm unsure 
whether it can provide me with the same functionality.


Thanks


Re: openMP

2012-10-02 Thread Peter Alexander

On Tuesday, 2 October 2012 at 19:15:19 UTC, Farmer wrote:

Hi,
I am tempted to start D programming but for me it is crucrial 
to be able to parallelize for-loops as can be done with openMP 
for C/C++ (mainly @pragma omp parallel for, @pragma omp 
critical).
I have already seen the std.parallelism library but I'm unsure 
whether it can provide me with the same functionality.


Thanks


It can. Here's an example from the docs of parallelising a simple 
for loop:


auto logs = new double[10_000_000];
foreach(i, ref elem; taskPool.parallel(logs, 100))
{
elem = log(i + 1.0);
}

This creates a pool of workers that each perform 100 iterations 
of the loop body in parallel.


Re: openMP

2012-10-02 Thread Farmer

And is there also a pragma omp critical analogon?

On Tuesday, 2 October 2012 at 20:16:36 UTC, Peter Alexander wrote:

On Tuesday, 2 October 2012 at 19:15:19 UTC, Farmer wrote:

Hi,
I am tempted to start D programming but for me it is crucrial 
to be able to parallelize for-loops as can be done with openMP 
for C/C++ (mainly @pragma omp parallel for, @pragma omp 
critical).
I have already seen the std.parallelism library but I'm unsure 
whether it can provide me with the same functionality.


Thanks


It can. Here's an example from the docs of parallelising a 
simple for loop:


auto logs = new double[10_000_000];
foreach(i, ref elem; taskPool.parallel(logs, 100))
{
elem = log(i + 1.0);
}

This creates a pool of workers that each perform 100 iterations 
of the loop body in parallel.





Re: openMP

2012-10-02 Thread Peter Alexander

On Tuesday, 2 October 2012 at 21:13:33 UTC, Farmer wrote:

And is there also a pragma omp critical analogon?


For critical sections you could use a low-level mutex. I don't do 
much parallel stuff in D, so I don't know if this is the 
preferred way, but it's an option.


http://dlang.org/phobos/core_sync_mutex.html

import std.stdio;
import std.parallelism;
import std.math;
import core.sync.mutex;
void main()
{
auto logs = new double[1_000_000];
double x = 0.0;
Mutex m = new Mutex();
foreach(i, ref elem; taskPool.parallel(logs, 100))
{
elem = log(i + 1.0);
m.lock();
x += 1.0;
m.unlock();
}
}