1 /** 2 * ae.utils.parallelism 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <ae@cy.md> 12 */ 13 14 module ae.utils.parallelism; 15 16 import std.algorithm.mutation; 17 import std.algorithm.sorting; 18 import std.parallelism; 19 import std.range : chunks, iota; 20 21 // https://gist.github.com/63e139a16b9b278fb5d449ace611e7b8 22 23 /// Sort `r` using all CPU cores. 24 auto parallelSort(alias less = "a < b", R)(R r) 25 { 26 auto impl(size_t depth = 0)(R order) 27 { 28 static if (depth < 8) 29 if ((1L << depth) < totalCPUs) 30 foreach (chunk; order.chunks(order.length / 2 + 1).parallel(1)) 31 impl!(depth + 1)(chunk); 32 33 return order.sort!(less, SwapStrategy.stable, R); 34 } 35 return impl(r); 36 } 37 38 unittest 39 { 40 assert([3, 1, 2].parallelSort.release == [1, 2, 3]); 41 } 42 43 44 /// Parallel map. Like TaskPool.amap, but uses functors for 45 /// predicates instead of alias arguments, and as such does not have 46 /// the multiple-context problem. 47 /// https://forum.dlang.org/post/qnigarkuxxnqwdernhzv@forum.dlang.org 48 auto parallelEagerMap(R, Pred)(R input, Pred pred, size_t workUnitSize = 0) 49 { 50 if (workUnitSize == 0) 51 workUnitSize = taskPool.defaultWorkUnitSize(input.length); 52 alias RT = typeof(pred(input[0])); 53 auto result = new RT[input.length]; 54 foreach (i; input.length.iota.parallel(workUnitSize)) 55 result[i] = pred(input[i]); 56 return result; 57 } 58 59 unittest 60 { 61 assert([1, 2, 3].parallelEagerMap((int n) => n + 1) == [2, 3, 4]); 62 }