Re: parallelism with delegate
On Friday, 22 September 2023 at 04:24:19 UTC, Vitaliy Fadeev wrote: able ? how to use correctly? ```d import std.parallelism; auto async_task = task!fn( args ); // error // Error: no property `opCall` for type `app.A`, did you mean `new A`? async_task.executeInNewThread(); ``` where ```d auto a = new A(); auto fn = &a.download; class A { void fn( string url ) { // DO } } ``` Playground: https://run.dlang.io/is/HvhtoP gist: https://gist.github.com/run-dlang/218b69e1afd79e5944ea10aa7ca61e1b Also check out std.concurrency
Re: parallelism with delegate
On Thursday, September 21, 2023 10:33:44 PM MDT Vitaliy Fadeev via Digitalmars-d-learn wrote: > On Friday, 22 September 2023 at 04:24:19 UTC, Vitaliy Fadeev > > wrote: > > ... > > Skip this thread. I see solution. > > How to delete missed posts on this forum ? This forum is esentially just a web client for some D-specific newsgroups (and a number of folks access it via either the newsgroup interface or its associated mailing list rather than through the web interface). So, you can't edit or remove posts. Admins can remove spam from the newsgroup (and thus the forum), but that's pretty much it, and even then, that doesn't remove it from the mailing list, because you can't get an e-mail back once it's been sent. So, once you send something to the forum, it's out there forever. - Jonathan M Davis
Re: parallelism with delegate
On Friday, 22 September 2023 at 04:33:44 UTC, Vitaliy Fadeev wrote: On Friday, 22 September 2023 at 04:24:19 UTC, Vitaliy Fadeev wrote: ... Skip this thread. I see solution. How to delete missed posts on this forum ? It's there forever, you have to live with that error ;) See https://forum.dlang.org/help#about
Re: parallelism with delegate
On Friday, 22 September 2023 at 04:24:19 UTC, Vitaliy Fadeev wrote: ... Skip this thread. I see solution. How to delete missed posts on this forum ?
parallelism with delegate
able ? how to use correctly? ```d import std.parallelism; auto async_task = task!fn( args ); // error // Error: no property `opCall` for type `app.A`, did you mean `new A`? async_task.executeInNewThread(); ``` where ```d auto a = new A(); auto fn = &a.download; class A { void fn( string url ) { // DO } } ``` Playground: https://run.dlang.io/is/HvhtoP gist: https://gist.github.com/run-dlang/218b69e1afd79e5944ea10aa7ca61e1b
Re: Recommendation for parallelism with nested for loops?
On 20.08.22 12:28, Christian Köstlin wrote: On 19.08.22 03:49, Shriramana Sharma wrote: Hello. I want to parallelize a computation which has two for loops, one nested within another. All inner-loop-param+outer-loop-param combinations can be computed independent of one another. As I suspected, [https://forum.dlang.org/post/xysyidbkjdinclmrx...@forum.dlang.org](this forum post) says that only one loop can be parallelized. Will it be an error or inefficient or useless if I try to do both? Also, what is the best way to do parallelism in such a situation? You could also do a custom range that makes a one-dimensional range (aka iota out of your nested loops) and then process this with parallel. Another way (more similar to Ali's solution) would be to write the nested loops as you have them, but collect the parameters for the work in an array or something and then process this array in parallel. Kind regards, Christian Actually phobos brings already all that is required (especially the cartesian product): ```d import std; void doSomething(int i, string s) { writeln("%s-%s".format(i, s)); } void main() { foreach (t; cartesianProduct(iota(1, 4), ["abc", "def", "ghi"]).parallel) { doSomething(t.expand); } } ``` kind regards, Christian
Re: Recommendation for parallelism with nested for loops?
On 19.08.22 03:49, Shriramana Sharma wrote: Hello. I want to parallelize a computation which has two for loops, one nested within another. All inner-loop-param+outer-loop-param combinations can be computed independent of one another. As I suspected, [https://forum.dlang.org/post/xysyidbkjdinclmrx...@forum.dlang.org](this forum post) says that only one loop can be parallelized. Will it be an error or inefficient or useless if I try to do both? Also, what is the best way to do parallelism in such a situation? You could also do a custom range that makes a one-dimensional range (aka iota out of your nested loops) and then process this with parallel. Another way (more similar to Ali's solution) would be to write the nested loops as you have them, but collect the parameters for the work in an array or something and then process this array in parallel. Kind regards, Christian
Re: Recommendation for parallelism with nested for loops?
On Friday, 19 August 2022 at 02:02:57 UTC, Adam D Ruppe wrote: Even if they aren't equal, you'll get decent benefit from parallel on the outer one alone, but not as good since the work won't be balanced. Unless there's some kind of blocking going on in D's implementation, if the number of passes on the outer loop is large enough relative to the number of cores, applying parallel to the outer loop is the best you can do - uneven amounts of work on the inner loop will get spread out across cores. There are always counterexamples, but the ratio of passes to cores needs to be pretty low for further optimizations to have any chance to help.
Re: Recommendation for parallelism with nested for loops?
On 8/18/22 18:49, Shriramana Sharma wrote: > Hello. I want to parallelize a computation which has two for loops An option is to add tasks individually but I am not sure how wise doing this and I don't know how to determine whether all tasks are completed. In any case, Roy Margalit's DConf 2022 presentation is very much on topic. :) http://dconf.org/2022/index.html#roym And the following program cannot show any benefit because the tasks are so short. import std.stdio; import std.parallelism; import std.conv; enum I = 1_000; enum J = 1_000; void main() { auto results = new int[I * J]; // In case you want a new TaskPool: // auto tp = new TaskPool(totalCPUs); // (And use tp. below instead of taskPool.) foreach (i; 0 .. I) { foreach (j; 0 .. J) { taskPool.put(task!foo(i, j, results)); } } // WARNING: I am not sure whether one can trust the results are // ready yet. (?) // // parallel() does call yieldForce() on each task but we don't seem // to have that option for tasks that are .put() into the pool. (?) enum toPrint = 10; writeln(results[0..toPrint]); writeln("[...]"); writeln(results[$-toPrint..$]); } void foo(size_t i, size_t j, int[] results) { results[i * J + j] = to!int(i * J + j); } Ali
Re: Recommendation for parallelism with nested for loops?
On Friday, 19 August 2022 at 01:49:43 UTC, Shriramana Sharma wrote: Also, what is the best way to do parallelism in such a situation? If the inner loops are about the same size for each pass of the outer loop, you can just simply parallel on the outer loop and get the same benefit. Even if they aren't equal, you'll get decent benefit from parallel on the outer one alone, but not as good since the work won't be balanced. Still, that's what I'd try.
Recommendation for parallelism with nested for loops?
Hello. I want to parallelize a computation which has two for loops, one nested within another. All inner-loop-param+outer-loop-param combinations can be computed independent of one another. As I suspected, [https://forum.dlang.org/post/xysyidbkjdinclmrx...@forum.dlang.org](this forum post) says that only one loop can be parallelized. Will it be an error or inefficient or useless if I try to do both? Also, what is the best way to do parallelism in such a situation?
Re: need `this` on trying to use class method with parallelism Task! or task!
On Sunday, 14 November 2021 at 16:55:24 UTC, Alexey wrote: Remove "!"
Re: need `this` on trying to use class method with parallelism Task! or task!
On Sunday, 14 November 2021 at 17:42:18 UTC, Imperatorn wrote: Try task(&threadFunc) instead of task!(&threadFunc) worked! huge thanks!
Re: need `this` on trying to use class method with parallelism Task! or task!
On Sunday, 14 November 2021 at 16:55:24 UTC, Alexey wrote: On Sunday, 14 November 2021 at 16:40:58 UTC, Andrey Zherikov wrote: Just do `auto t1 = task(&threadFunc)` in `threadCreator` then. Error: value of `this` is not known at compile time ```D import std.stdio; import std.parallelism; class TC { void threadFunc() { import core.thread; import core.time; scope (exit) writeln("threadFunc done"); core.thread.osthread.Thread.getThis.sleep(msecs(2000)); } void threadCreator() { scope (exit) writeln("threadCreator done"); auto t1 = task!(&threadFunc); t1.executeInNewThread(); } } void main() { scope (exit) writeln("main done"); auto tc = new TC; tc.threadCreator(); } ``` Try task(&threadFunc) instead of task!(&threadFunc)
Re: need `this` on trying to use class method with parallelism Task! or task!
On Sunday, 14 November 2021 at 16:40:58 UTC, Andrey Zherikov wrote: Just do `auto t1 = task(&threadFunc)` in `threadCreator` then. Error: value of `this` is not known at compile time ```D import std.stdio; import std.parallelism; class TC { void threadFunc() { import core.thread; import core.time; scope (exit) writeln("threadFunc done"); core.thread.osthread.Thread.getThis.sleep(msecs(2000)); } void threadCreator() { scope (exit) writeln("threadCreator done"); auto t1 = task!(&threadFunc); t1.executeInNewThread(); } } void main() { scope (exit) writeln("main done"); auto tc = new TC; tc.threadCreator(); } ```
Re: need `this` on trying to use class method with parallelism Task! or task!
On Sunday, 14 November 2021 at 14:41:21 UTC, Alexey wrote: On Sunday, 14 November 2021 at 14:24:00 UTC, Andrey Zherikov wrote: On Sunday, 14 November 2021 at 12:01:36 UTC, Alexey wrote: You just need two changes: - make `threadFunc` function static: `static void threadFunc()` - use `task` instead of `Task`: `auto t1 = task!threadFunc;` I need `this` to be available inside of threadFunc Just do `auto t1 = task(&threadFunc)` in `threadCreator` then.
Re: need `this` on trying to use class method with parallelism Task! or task!
On Sunday, 14 November 2021 at 14:24:00 UTC, Andrey Zherikov wrote: On Sunday, 14 November 2021 at 12:01:36 UTC, Alexey wrote: You just need two changes: - make `threadFunc` function static: `static void threadFunc()` - use `task` instead of `Task`: `auto t1 = task!threadFunc;` I need `this` to be available inside of threadFunc
Re: need `this` on trying to use class method with parallelism Task! or task!
On Sunday, 14 November 2021 at 12:01:36 UTC, Alexey wrote: You just need two changes: - make `threadFunc` function static: `static void threadFunc()` - use `task` instead of `Task`: `auto t1 = task!threadFunc;`
Re: need `this` on trying to use class method with parallelism Task! or task!
On Sunday, 14 November 2021 at 12:01:36 UTC, Alexey wrote: /home/animuspexus/dlang/d_v2.098.0/dmd/generated/linux/release/64/../../../../../phobos/std/parallelism.d(436): Error: need `this` for `threadFunc` of type `void()` Go example for contrast. Ideally, I'd like something similar; ```Go package main import ( "fmt" "time" ) type TC struct { } func (self *TC) threadFunc() { defer fmt.Println("threadFunc done") time.Sleep(time.Duration(time.Second * 2)) } func (self *TC) threadCreator() { defer fmt.Println("threadCreator done") go self.threadFunc() } func main() { defer fmt.Println("main done") tc := new(TC) tc.threadCreator() } ```
Re: need `this` on trying to use class method with parallelism Task! or task!
On Sunday, 14 November 2021 at 12:01:36 UTC, Alexey wrote: auto t1 = Task!threadFunc; if this line rewritten as `auto t1 = Task!(TC.threadFunc, this);` ```text /home/animuspexus/dlang/d_v2.098.0/dmd/generated/linux/release/64/../../../../../phobos/std/parallelism.d(451): Error: tuple `Args` is used as a type /home/animuspexus/dlang/d_v2.098.0/dmd/generated/linux/release/64/../../../../../phobos/std/parallelism.d(533): Error: tuple `Args` is used as a type /home/animuspexus/dlang/d_v2.098.0/dmd/generated/linux/release/64/../../../../../druntime/import/core/internal/traits.d(191): Error: template instance `pred!(this)` does not match template declaration `isAssignable(Lhs, Rhs = Lhs)` /home/animuspexus/dlang/d_v2.098.0/dmd/generated/linux/release/64/../../../../../phobos/std/meta.d(842): Error: template instance `t.TC.threadCreator.allSat!(isAssignable, this)` error instantiating /home/animuspexus/dlang/d_v2.098.0/dmd/generated/linux/release/64/../../../../../phobos/std/parallelism.d(541): instantiated from here: `allSatisfy!(isAssignable, this)` ./t.d(22):instantiated from here: `Task!(threadFunc, this)` ```
need `this` on trying to use class method with parallelism Task! or task!
/home/animuspexus/dlang/d_v2.098.0/dmd/generated/linux/release/64/../../../../../phobos/std/parallelism.d(436): Error: need `this` for `threadFunc` of type `void()` ./t.d(22): Error: template instance `std.parallelism.Task!(threadFunc)` error instantiating thou documentation says `a function (or delegate or other callable)` ```D import std.stdio; import std.parallelism; class TC { void threadFunc() { import core.thread; import core.time; scope(exit) writeln("threadFunc done"); core.thread.osthread.Thread.getThis.sleep(msecs(2000)); } void threadCreator() { scope(exit) writeln("threadCreator done"); auto t1 = Task!threadFunc; t1.executeInNewThread(); } } void main() { scope(exit) writeln("main done"); auto tc = new TC; tc.threadCreator(); } ```
Re: parallelism
On Sunday, 28 January 2018 at 04:44:23 UTC, thedeemon wrote: On Saturday, 27 January 2018 at 20:49:43 UTC, Arun Chandrasekaran wrote: But really I'm not sure why you want static foreach here I was just trying to see if static foreach can be used here, but well, you showed that it's not required. I just got intrigued to see the error on labeled break. Thanks anyway.
Re: parallelism
On Saturday, 27 January 2018 at 20:49:43 UTC, Arun Chandrasekaran wrote: Error: must use labeled break within static foreach Just follow the compiler suggestion: void main(string[] args) { auto op = Operation.a; foreach (_; 0 .. args.length) { ops: final switch (op) { static foreach(e; EnumMembers!Operation) { case e: mixin(e.to!string ~ "();"); break ops; } } } } Here 'ops' is a label that we use to tell break what exactly it breaks. But really I'm not sure why you want static foreach here, a simple foreach is fine, it gets unrolled statically here just like static one.
Re: parallelism
On Saturday, 27 January 2018 at 17:54:53 UTC, thedeemon wrote: On Saturday, 27 January 2018 at 11:19:37 UTC, Arun Chandrasekaran wrote: Simplified test case that still errors: You got really close here. Here's a working version: enum Operation { a, b } import std.traits, std.conv, std.stdio; void main(string[] args) { auto op = Operation.a; foreach (_; 0 .. args.length) { final switch (op) { foreach(e; EnumMembers!Operation) { case e: mixin(e.to!string ~ "();"); break; } } } } void a() { writeln("A!"); } void b() { writeln("B!"); } Thanks, that did the trick. How to use break inside a static foreach? Changing the above foreach each to static gives the following error: Error: must use labeled break within static foreach
Re: parallelism
On Saturday, 27 January 2018 at 11:19:37 UTC, Arun Chandrasekaran wrote: Simplified test case that still errors: You got really close here. Here's a working version: enum Operation { a, b } import std.traits, std.conv, std.stdio; void main(string[] args) { auto op = Operation.a; foreach (_; 0 .. args.length) { final switch (op) { foreach(e; EnumMembers!Operation) { case e: mixin(e.to!string ~ "();"); break; } } } } void a() { writeln("A!"); } void b() { writeln("B!"); }
Re: parallelism
On Saturday, 27 January 2018 at 10:49:45 UTC, Arun Chandrasekaran wrote: On Saturday, 27 January 2018 at 10:38:25 UTC, Nicholas Wilson wrote: ... [snip] Simplified test case that still errors: ``` enum Operation { a, b } import std.traits; import std.conv; void main(string[] args) { foreach (_; 0 .. args.length) { Operation operation; switch (operation) { static foreach(e; EnumMembers!Operation) { case e: mixin(to!string(e))(); break; } } } } void a() {} void b() {} ```
Re: parallelism
On Saturday, 27 January 2018 at 10:38:25 UTC, Nicholas Wilson wrote: On Saturday, 27 January 2018 at 10:28:10 UTC, Arun Chandrasekaran wrote: ``` import std.parallelism; auto pool = new TaskPool(options.threadCount); foreach (_; 0 .. options.iterationCount) { switch (options.operation) { static foreach(e; EnumMembers!Operation) { case e: pool.put(task!e(options)); break; } pool.finish(); ``` Does that do the trick? No it doesn't. ``` /usr/include/dmd/phobos/std/parallelism.d(507,34): Error: function expected before (), not cast(Operation)0 of type Operation /usr/include/dmd/phobos/std/parallelism.d(835,16): Error: template instance std.parallelism.Task!(cast(Operation)0, Options) error instantiating src/app.d(159,32):instantiated from here: task!(cast(Operation)0, Options) src/app.d(160,17): Error: must use labeled break within static foreach ### and so on till the end of enum ```
Re: parallelism
On Saturday, 27 January 2018 at 10:28:10 UTC, Arun Chandrasekaran wrote: ``` import std.parallelism; auto pool = new TaskPool(options.threadCount); foreach (_; 0 .. options.iterationCount) { switch (options.operation) { static foreach(e; EnumMembers!Operation) { case e: pool.put(task!e(options)); break; } pool.finish(); ``` Does that do the trick?
Re: parallelism
On Saturday, 27 January 2018 at 10:28:10 UTC, Arun Chandrasekaran wrote: Hi All, Is there a way to rewrite this [...] Damn! The subject should've been something else.. naming is surely hard..
parallelism
Hi All, Is there a way to rewrite this ``` import std.parallelism; auto pool = new TaskPool(options.threadCount); foreach (_; 0 .. options.iterationCount) { switch (options.operation) { case Operation.a: pool.put(task!a(options)); break; case Operation.b: pool.put(task!b(options)); break; case Operation.c: pool.put(task!c(options)); break; case Operation.d: pool.put(task!d(options)); break; /// and so on. } pool.finish(); ``` into this? ``` import std.parallelism; import std.conv: to; auto pool = new TaskPool(options.threadCount); foreach (_; 0 .. options.iterationCount) { pool.put(task!(to!string(options.operation))(options)); // this line errs. } pool.finish(); ``` -- Arun
Re: Fold in Parallelism
On Friday, 22 December 2017 at 00:18:40 UTC, Seb wrote: On Friday, 22 December 2017 at 00:12:45 UTC, Vino wrote: On Thursday, 21 December 2017 at 06:31:52 UTC, Ali Çehreli wrote: [...] Hi Ali, Thank you very much, the pull request is in open state, so can you please let me know when can we can test the same. From, Vino.B It will take a couple of days for this pull request to reach a ready form and to be approved. The best way to help it to move forward is to review it yourself or at least vote for it on GitHub ;-) Once merged it will appear on dmd-nightly on the next day, but I'm not sure why you depend on his pull request? Can't you just use the code directly? Hi Seb, I am fine waiting for a couple of days for this pull request to reach to teh ready form and approved. Sure will vote on GItHub. From, Vino.B
Re: Fold in Parallelism
On Friday, 22 December 2017 at 00:12:45 UTC, Vino wrote: On Thursday, 21 December 2017 at 06:31:52 UTC, Ali Çehreli wrote: On 12/19/2017 02:32 AM, Vino wrote: > even though it is a simple code copy+paste The change was a little more complicated than my naive adaptation from std.algorithm.fold. Here is the pull request: https://github.com/dlang/phobos/pull/5951 Ali Hi Ali, Thank you very much, the pull request is in open state, so can you please let me know when can we can test the same. From, Vino.B It will take a couple of days for this pull request to reach a ready form and to be approved. The best way to help it to move forward is to review it yourself or at least vote for it on GitHub ;-) Once merged it will appear on dmd-nightly on the next day, but I'm not sure why you depend on his pull request? Can't you just use the code directly?
Re: Fold in Parallelism
On Thursday, 21 December 2017 at 06:31:52 UTC, Ali Çehreli wrote: On 12/19/2017 02:32 AM, Vino wrote: > even though it is a simple code copy+paste The change was a little more complicated than my naive adaptation from std.algorithm.fold. Here is the pull request: https://github.com/dlang/phobos/pull/5951 Ali Hi Ali, Thank you very much, the pull request is in open state, so can you please let me know when can we can test the same. From, Vino.B
Re: Fold in Parallelism
On Wed, 2017-12-20 at 22:31 -0800, Ali Çehreli via Digitalmars-d-learn wrote: > On 12/19/2017 02:32 AM, Vino wrote: > > > even though it is a simple code copy+paste > > The change was a little more complicated than my naive adaptation > from > std.algorithm.fold. Here is the pull request: > >https://github.com/dlang/phobos/pull/5951 > > Ali Thanks for doing this. Having consistency is a good thing. -- Russel. === Dr Russel Winder t: +44 20 7585 2200 41 Buckmaster Roadm: +44 7770 465 077 London SW11 1EN, UK w: www.russel.org.uk signature.asc Description: This is a digitally signed message part
Re: Fold in Parallelism
On 12/19/2017 02:32 AM, Vino wrote: > even though it is a simple code copy+paste The change was a little more complicated than my naive adaptation from std.algorithm.fold. Here is the pull request: https://github.com/dlang/phobos/pull/5951 Ali
Re: Fold in Parallelism
On Monday, 18 December 2017 at 20:53:28 UTC, Russel Winder wrote: Ali, Shouldn't this be a pull request for std.parallelism to be extended? If the function is in std.algorithm, then people should not have to write it for themselves in std.parallelism. On Mon, 2017-12-18 at 11:01 -0800, Ali Çehreli via Digitalmars-d-learn wrote: [...] […] [...] Hi Ali, Sorry, I would like to echo the statement from Russel, as an user(newbie) I personally do not want to fiddle around the libraries even though it is a simple code copy+paste, as this might cause some serious issue in case if anything went wrong, so i would like to leave it to expert's like you to help us. If possible can we added this to the next release (78). From, Vino.B.
Re: Fold in Parallelism
Ali, Shouldn't this be a pull request for std.parallelism to be extended? If the function is in std.algorithm, then people should not have to write it for themselves in std.parallelism. On Mon, 2017-12-18 at 11:01 -0800, Ali Çehreli via Digitalmars-d-learn wrote: > […] > > Hi Ali, > > > >Thank you very much, may we know if possible as to when this > > would be > > added. > > > > From, > > Vino.B > > The implementation is almost a copy+paste from std.algorithm.fold. > You > can simply copy the following fold template to your project and > start > using it: > > import std.parallelism; > import std.stdio; > > int adder(int result, int element) { > return result + element; > } > > template fold(fun...) > if (fun.length >= 1) > { > import std.parallelism : TaskPool; > auto fold(R, S...)(TaskPool t, R r, S seed) > { > static if (S.length < 2) > { > return t.reduce!fun(seed, r); > } > else > { > import std.typecons : tuple; > return t.reduce!fun(tuple(seed), r); > } > } > } > > unittest { > import std.range; > > const N = 10_000; > const expected = N * (N - 1) / 2; > > foreach (numThreads; 1 .. 100) { > auto t = new TaskPool(numThreads); > const result = t.fold!adder(N.iota); > assert(result == expected); > t.finish(); > } > } > > void main() { > } > > If you want to provide an explicit seed value, good luck making sense > of > how it affects the final result. :) (The documentation of > TaskPool.reduce explains it but I find it too involved to > understand.) > > Ali -- Russel. === Dr Russel Winder t: +44 20 7585 2200 41 Buckmaster Roadm: +44 7770 465 077 London SW11 1EN, UK w: www.russel.org.uk signature.asc Description: This is a digitally signed message part
Re: Fold in Parallelism
On 12/18/2017 02:18 AM, Vino wrote: On Sunday, 17 December 2017 at 20:00:53 UTC, Ali Çehreli wrote: On 12/17/2017 08:11 AM, Vino wrote: > As per the document form std.parallelism it states that we can use > taskPool.reduce so can we use the same for fold (taskPool.fold) as > basically both are same with slight variation on seed values, if > possible can can we define the same in the below lines fold has only been added to std.algorithm. Opened an enhancement request: https://issues.dlang.org/show_bug.cgi?id=18096 Ali Hi Ali, Thank you very much, may we know if possible as to when this would be added. From, Vino.B The implementation is almost a copy+paste from std.algorithm.fold. You can simply copy the following fold template to your project and start using it: import std.parallelism; import std.stdio; int adder(int result, int element) { return result + element; } template fold(fun...) if (fun.length >= 1) { import std.parallelism : TaskPool; auto fold(R, S...)(TaskPool t, R r, S seed) { static if (S.length < 2) { return t.reduce!fun(seed, r); } else { import std.typecons : tuple; return t.reduce!fun(tuple(seed), r); } } } unittest { import std.range; const N = 10_000; const expected = N * (N - 1) / 2; foreach (numThreads; 1 .. 100) { auto t = new TaskPool(numThreads); const result = t.fold!adder(N.iota); assert(result == expected); t.finish(); } } void main() { } If you want to provide an explicit seed value, good luck making sense of how it affects the final result. :) (The documentation of TaskPool.reduce explains it but I find it too involved to understand.) Ali
Re: Fold in Parallelism
On Sunday, 17 December 2017 at 20:00:53 UTC, Ali Çehreli wrote: On 12/17/2017 08:11 AM, Vino wrote: > As per the document form std.parallelism it states that we can use > taskPool.reduce so can we use the same for fold (taskPool.fold) as > basically both are same with slight variation on seed values, if > possible can can we define the same in the below lines fold has only been added to std.algorithm. Opened an enhancement request: https://issues.dlang.org/show_bug.cgi?id=18096 Ali Hi Ali, Thank you very much, may we know if possible as to when this would be added. From, Vino.B
Re: Fold in Parallelism
On 12/17/2017 08:11 AM, Vino wrote: > As per the document form std.parallelism it states that we can use > taskPool.reduce so can we use the same for fold (taskPool.fold) as > basically both are same with slight variation on seed values, if > possible can can we define the same in the below lines fold has only been added to std.algorithm. Opened an enhancement request: https://issues.dlang.org/show_bug.cgi?id=18096 Ali
Fold in Parallelism
HI All, As per the document form std.parallelism it states that we can use taskPool.reduce so can we use the same for fold (taskPool.fold) as basically both are same with slight variation on seed values, if possible can can we define the same in the below lines Tried the below but getting an error auto SdFiles = Array!ulong(dirEntries("C:\\TEMP\\BACKUP", SpanMode.depth).map!(a => a.size).taskPool.fold!((a,b) => a + b) (x))[]; Error : Srvnscleaner.d(89): Error: function std.parallelism.taskPool () is not callable using argument types (MapResult!(__lambda2, DirIterator)) auto SdFiles = Array!ulong(dirEntries("C:\\TEMP\\BACKUP", SpanMode.depth).map!(a => a.size).fold!((a,b) => a + b) (x)).taskPool[]; Error : Size.d(89): Error: function std.parallelism.taskPool () is not callable using argument types (Array!ulong) From, Vino.B
Re: What's the "right" way to do openmp-style parallelism?
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?
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?
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?
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?
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.
Re: 2D array parallelism
On Sunday, 26 January 2014 at 20:34:27 UTC, bearophile wrote: Do you know that module level variables in D are thread-local? If you don't what that, you have to use __gshared. Bye, bearophile Perfect, thanks! If anyone wants to make things easy on stupid people like me, using a module level variable inside a parallel foreach like I was could generate a compiler warning. Not that I'm smart enough to implement something like that. :-) Andrew
Re: 2D array parallelism
Andrew Klaassen: This happens with dmd, ldc2 and gdc, so I assume it's something I'm doing wrong rather than a bug. What's the explanation? What am I doing wrong? Do you know that module level variables in D are thread-local? If you don't what that, you have to use __gshared. Bye, bearophile
2D array parallelism
I've got some code which sets values in a 2D array. Each value is independent, so I'm using std.parallelism to set the values a row at a time. When my variables are class variables it seems to work (though it's hard to tell, given that the effect is intermittent), but when I use module-level variables, all the values in some rows are occasionally left as all nan on a multicore machine. Here's a minimal illustration of what I'm seeing: import io = std.stdio; import pll = std.parallelism; long width; long height; double[][] payoffs; void calculate() { foreach (y, ref row; pll.taskPool.parallel(payoffs)) { io.writef("%s ", y); for (auto x=0; xnan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan nan This happens with dmd, ldc2 and gdc, so I assume it's something I'm doing wrong rather than a bug. What's the explanation? What am I doing wrong? Thanks. Andrew
Re: Multiple while-loops parallelism
On 01/17/2014 01:28 PM, Kelet wrote: > create a Task[2]. Once the Task is created, use the executeInNewThread > function. You can use yieldForce to wait on it. That's what I thought initially as well but then I realized that it can be even simpler than that: import std.stdio; import std.parallelism; void main() { // All of these loops can be free-standing functions as well long sum1 = 0; auto loop1 = { foreach (number; 0 .. 100) { sum1 += number; } }; long sum2 = 0; auto loop2 = { foreach (number; 0 .. 100) { sum2 += number; } }; foreach (loop; [ loop1, loop2 ].parallel) { loop(); } writefln("sum1: %s, sum2: %s", sum1, sum2); } Ali
Re: Multiple while-loops parallelism
On Friday, 17 January 2014 at 21:07:46 UTC, Mineko wrote: Today I'm asking a more theoretical question, since I can't quite grasp this one too well. Let's say I want 3 while-loops running in parallel, without getting in the way of each other, how would I do that? With std.parallel of course, but that's where I get confused, perhaps someone could enlighten me? Hi, std.parallel You mean std.parallelism. Assuming your code is thread safe[1], you would have each of these while loops in a delegate or function, and create a Task[2]. Once the Task is created, use the executeInNewThread function. You can use yieldForce to wait on it. However, if you don't *really* need while loops, take a look at creating a TaskPool and using a parallel foreach, reduce, map, etc. They are also in std.parallelism. However, it's worth noting that there is also std.concurrency[3] which may be a better approach if your threads need to communicate. core.thread/core.atomic are useful if you need lower level control. If your code is not thread safe, look into synchronized statement, core.atomic, and possibly core.sync.* and make it thread safe. [1]: http://en.wikipedia.org/wiki/Thread_safety [2]: http://dlang.org/phobos/std_parallelism.html#.Task [3]: http://dlang.org/phobos/std_concurrency.html Regards, Kelet
Re: Multiple while-loops parallelism
On Friday, 17 January 2014 at 21:07:46 UTC, Mineko wrote: Let's say I want 3 while-loops running in parallel, without getting in the way of each other, how would I do that? On the same set of data? That's optimistic if one of the loops writes :) Otherwise, you could just create three tasks, one per loop.
Multiple while-loops parallelism
Today I'm asking a more theoretical question, since I can't quite grasp this one too well. Let's say I want 3 while-loops running in parallel, without getting in the way of each other, how would I do that? With std.parallel of course, but that's where I get confused, perhaps someone could enlighten me?
Re: Parallelism Map and Reduce
On 12/12/2012 05:47 AM, Zardoz wrote: > On Tuesday, 11 December 2012 at 17:50:31 UTC, Ali Çehreli wrote: >> On 12/11/2012 08:12 AM, Zardoz wrote: >> >> >> Could you please move MapIntegrator() to module-level. Then it should >> work. >> >> Ali > > I try it and now even with normal Map function give me errors with dmd ! > > public Entity MapIntegrator ( Entity me) { > me.Integrador3Orden (); > return me; > } > > void main() { > Entity[] objects; > ... > objects = array( map!MapIntegrator(objects) ); > ... > } > > With this error : > dmd -w -wi -version=SReduction simulator.d entity.d vector.d -ofreduceSim > simulator.d(194): Error: template std.algorithm.map!(MapIntegrator).map > does not match any function template declaration > /usr/include/dmd/phobos/std/algorithm.d(369): Error: template > std.algorithm.map!(MapIntegrator).map(Range) if > (isInputRange!(Unqual!(Range))) cannot deduce template function from > argument types !()(Entity[]) Strange. The following program works for me with dmd 2.060. It uses both the regular and parallel versions of map and reduce: import std.array; import std.algorithm; import std.parallelism; struct Entity { void Integrador3Orden() {} } public Entity MapIntegrator ( Entity me) { me.Integrador3Orden (); return me; } void main() { Entity[] objects; objects = array( map!MapIntegrator(objects) ); objects = array(taskPool.map!MapIntegrator(objects)); int[] acelByObjs; int reduced = reduce!"a + b"(0, acelByObjs); reduced = taskPool.reduce!"a + b"(0, acelByObjs); } Ali
Re: Parallelism Map and Reduce
On Tuesday, 11 December 2012 at 17:50:31 UTC, Ali Çehreli wrote: On 12/11/2012 08:12 AM, Zardoz wrote: Could you please move MapIntegrator() to module-level. Then it should work. Ali I try it and now even with normal Map function give me errors with dmd ! public Entity MapIntegrator ( Entity me) { me.Integrador3Orden (); return me; } void main() { Entity[] objects; ... objects = array( map!MapIntegrator(objects) ); ... } With this error : dmd -w -wi -version=SReduction simulator.d entity.d vector.d -ofreduceSim simulator.d(194): Error: template std.algorithm.map!(MapIntegrator).map does not match any function template declaration /usr/include/dmd/phobos/std/algorithm.d(369): Error: template std.algorithm.map!(MapIntegrator).map(Range) if (isInputRange!(Unqual!(Range))) cannot deduce template function from argument types !()(Entity[])
Re: Parallelism Map and Reduce
On Tuesday, 11 December 2012 at 15:22:49 UTC, Ali Çehreli wrote: That used to work a couple of dmd versions ago. I think it was a bug that it worked, so it stopped working after bug fixes. If I'm not mistaken this is actually related to a compiler implementation issue: Lambda's have a single pointer to store the context that they have been started in. When a lambda is a free-standing function (aka "module function" or "global function") then there is only the context to deal with. When the template is a member function (taskPool.map is) then there is also the object that the function is started on. The single pointer of the lambda is not sufficient to store both without big changes in the compiler. (I may be off with that description above. e.g. there may be two pointers when three are actually needed, etc.) I had to change following chapter after dmd's behavior had changed: http://ddili.org/ders/d.en/parallelism.html --- Quoting --- import std.parallelism; // ... double averageGrade(Student student) { return student.averageGrade; } // ... auto results = taskPool.map!averageGrade(students, 3); Note: The free-standing averageGrade() function above is needed due to a limitation that involves using local delegates with member function templates like TaskPool.map: auto results = taskPool.map!(a => a.averageGrade)(students, 3); // ← compilation ERROR -- As you see above, the solution is to use a function with taskPool.map, not a lambda. Ali I try to use a function instead of a lambda function I'm keep getting compiler errors. Code : Entity MapIntegrator (ref Entity me) { me.Integrador3Orden (iDeltaT); return me; } objects = array( taskPool.map!MapIntegrator(objects) ); With taskPool.Map I get this errors : simulator.d(196): Error: template instance map!(MapIntegrator) cannot use local 'MapIntegrator' as parameter to non-global template map(functions...) /usr/include/dmd/phobos/std/parallelism.d(1969): Error: function std.parallelism.TaskPool.map!(MapIntegrator).map!(Entity[]).map.Map.fillBuf cannot access frame of function D main /usr/include/dmd/phobos/std/parallelism.d(1974): Error: template instance amap!(MapIntegrator) cannot use local 'MapIntegrator' as parameter to non-global template amap(functions...) /usr/include/dmd/phobos/std/parallelism.d(1675): Error: function std.parallelism.TaskPool.amap!(MapIntegrator).amap!(Entity[],ulong,Entity[]).amap cannot access frame of function D main /usr/include/dmd/phobos/std/parallelism.d(1706): Error: function std.parallelism.TaskPool.amap!(MapIntegrator).amap!(Entity[],ulong,Entity[]).amap.doIt cannot access frame of function D main /usr/include/dmd/phobos/std/parallelism.d(1974): Error: template instance std.parallelism.TaskPool.amap!(MapIntegrator).amap!(Entity[],ulong,Entity[]) error instantiating simulator.d(196):instantiated from here: map!(Entity[]) simulator.d(196): Error: template instance std.parallelism.TaskPool.map!(MapIntegrator).map!(Entity[]) error instantiating make: *** [predSim] Error 1 But again, with std.algorthim Map it don give any error and works fine.
Re: Parallelism Map and Reduce
Ali Çehreli: The single pointer of the lambda is not sufficient to store both without big changes in the compiler. I think adding a heavier 3-word delegate is not too much hard to do. But it makes the language more complex, so so far Walter is not willing to introduce them. But in the end introducing them may become inevitable :-) I think such hypothetical 3-word delegates need to be discussed in the main D newsgroup. Bye, bearophile
Re: Parallelism Map and Reduce
On 12/11/2012 02:53 AM, Zardoz wrote: > auto acelByObjs = map!( (Entity o) { > Vector3 r = o.pos[0] - pos[0]; > return r * (o.mass / pow((r.sq_length + epsilon2), 1.5)); > } )(objects); > newAcel = reduce!("a + b")(acelByObjs); > > It works very well with the std.algorithm Map and Reduce but when I try > to use std.parallelism versions of it, parallel Map give me this > compilataion errors : > > entity.d(63): Error: template instance map!(delegate @system > Vector3(Entity o) > { > Vector3 r = o.pos[0LU].opBinary(this.pos[0LU]); > return r.opBinary(o.mass / pow(r.sq_length() + epsilon2,1.5)); > } > ) cannot use local '__lambda3' as parameter to non-global template > map(functions...) That used to work a couple of dmd versions ago. I think it was a bug that it worked, so it stopped working after bug fixes. If I'm not mistaken this is actually related to a compiler implementation issue: Lambda's have a single pointer to store the context that they have been started in. When a lambda is a free-standing function (aka "module function" or "global function") then there is only the context to deal with. When the template is a member function (taskPool.map is) then there is also the object that the function is started on. The single pointer of the lambda is not sufficient to store both without big changes in the compiler. (I may be off with that description above. e.g. there may be two pointers when three are actually needed, etc.) I had to change following chapter after dmd's behavior had changed: http://ddili.org/ders/d.en/parallelism.html --- Quoting --- import std.parallelism; // ... double averageGrade(Student student) { return student.averageGrade; } // ... auto results = taskPool.map!averageGrade(students, 3); Note: The free-standing averageGrade() function above is needed due to a limitation that involves using local delegates with member function templates like TaskPool.map: auto results = taskPool.map!(a => a.averageGrade)(students, 3); // ← compilation ERROR -- As you see above, the solution is to use a function with taskPool.map, not a lambda. Ali
parallelism with message passing
Hello, Im starting one process in the main thread who in turn is going to kick off another process but has no way of passing along main's Tid, hence the shared(Tid) nonsense. * This works in serial (i.e. change workUnitSize > x.length in mains foreach loop). * It also works when passing the tid directly into f.run(mainTid) with workUnitSize == 1 But I get a crash when attempting to run in parallel with a "shared" tid (see below). Any ideas how to make this work? Thanks, Josh // garbage: newb playing with threads module test; import core.thread; import std.concurrency, std.parallelism, std.stdio; shared(Tid) mainTid; struct Foo { void run() { Thread.sleep(dur!"seconds"(1)); writeln(cast(Tid)mainTid); send(cast(Tid)mainTid, true); } } void someAction(Tid tid) { mainTid = cast(shared(Tid))tid; } void main() { Foo[] x = [Foo(), Foo(), Foo()]; spawn( &someAction, thisTid ); foreach(f; taskPool.parallel(x, 1)) { f.run(); receiveTimeout(dur!"seconds"(3), (bool x) { writeln("received"); } ); } } // exception output: core.exception.AssertError@/usr/share/dmd/src/phobos/std/concurrency.d(1007): null this 5 test0x00010604a4ad _d_assert_msg + 69 6 test0x000106032523 bool std.concurrency.MessageBox.get!(core.time.Duration, void function(bool)*).get(scope core.time.Duration, scope void function(bool)*) + 83 7 test0x0001060324bb bool std.concurrency.receiveTimeout!(void function(bool)*).receiveTimeout(core.time.Duration, void function(bool)*) + 59 8 test0x00010602c5c7 void test.main().int __foreachbody1416(ref test.Foo) + 71 9 test0x0001060320f0 int std.parallelism.ParallelForeach!(test.Foo[]).ParallelForeach.opApply(scope int delegate(ref test.Foo)).void doIt() + 256 10 test0x0001060583f4 void std.parallelism.run!(void delegate()).run(void delegate()) + 20 11 test0x00010605800c void std.parallelism.__T4TaskS213std11parallelism3runTDFZvZ.Task.impl(void*) + 24 12 test0x000106056da3 void std.parallelism.AbstractTask.job() + 15 13 test0x000106056df9 void std.parallelism.TaskPool.doJob(std.parallelism.AbstractTask*) + 33 14 test0x000106056f20 void std.parallelism.TaskPool.workLoop() + 132 15 test0x00010603f915 void core.thread.Thread.run() + 49 16 test0x00010603ec5a thread_entryPoint + 334 17 libsystem_c.dylib 0x7fff9681f742 _pthread_start + 327 18 libsystem_c.dylib 0x7fff9680c181 thread_start + 13 19 ??? 0x 0x0 + 0