View mode: basic / threaded / horizontal-split · Log in · Help
October 02, 2012
openMP
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
October 02, 2012
Re: openMP
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.
October 02, 2012
Re: openMP
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.
October 02, 2012
Re: openMP
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();
	}
}
October 03, 2012
Re: openMP
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.winder@ekiga.net
41 Buckmaster Road    m: +44 7770 465 077   xmpp: russel@winder.org.uk
London SW11 1EN, UK   w: www.russel.org.uk  skype: russel_winder
October 03, 2012
Re: openMP
On Wed, 03 Oct 2012 09:08:47 +0100
Russel Winder <russel@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?
October 03, 2012
Re: openMP
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 <russel@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?
October 03, 2012
Re: openMP
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.
October 03, 2012
Re: openMP
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.
October 03, 2012
Re: openMP
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
« First   ‹ Prev
1 2
Top | Discussion index | About this forum | D home