mirror of
https://github.com/dlang/phobos.git
synced 2025-05-06 19:16:13 +03:00
Merge pull request #5951 from acehreli/TaskPool_fold
Fix Issue 18096 - Add fold() to std.parallelism merged-on-behalf-of: Sebastian Wilzbach <sebi.wilzbach@gmail.com>
This commit is contained in:
commit
fa0a6192a1
3 changed files with 196 additions and 11 deletions
31
changelog/std-parallelism-fold.dd
Normal file
31
changelog/std-parallelism-fold.dd
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
`fold` is added to `std.parallelism.TaskPool`
|
||||||
|
|
||||||
|
$(REF fold, std, parallelism, TaskPool) is functionally equivalent to
|
||||||
|
$(REF_ALTTEXT `reduce`, reduce, std, parallelism, TaskPool) except the range
|
||||||
|
parameter comes first and there is no need to use $(REF_ALTTEXT
|
||||||
|
`tuple`,tuple,std,typecons) for multiple seeds.
|
||||||
|
|
||||||
|
---
|
||||||
|
static int adder(int a, int b)
|
||||||
|
{
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
static int multiplier(int a, int b)
|
||||||
|
{
|
||||||
|
return a * b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just the range
|
||||||
|
auto x = taskPool.fold!adder([1, 2, 3, 4]);
|
||||||
|
assert(x == 10);
|
||||||
|
|
||||||
|
// The range and the seeds (0 and 1 below; also note multiple
|
||||||
|
// functions in this example)
|
||||||
|
auto y = taskPool.fold!(adder, multiplier)([1, 2, 3, 4], 0, 1);
|
||||||
|
assert(y[0] == 10);
|
||||||
|
assert(y[1] == 24);
|
||||||
|
|
||||||
|
// The range, the seed (0), and the work unit size (20)
|
||||||
|
auto z = taskPool.fold!adder([1, 2, 3, 4], 0, 20);
|
||||||
|
assert(z == 10);
|
||||||
|
---
|
|
@ -2747,9 +2747,9 @@ Params:
|
||||||
See_Also:
|
See_Also:
|
||||||
$(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function))
|
$(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function))
|
||||||
|
|
||||||
$(LREF fold) is functionally equivalent to $(LREF _reduce) with the argument order reversed,
|
$(LREF fold) is functionally equivalent to $(LREF _reduce) with the argument
|
||||||
and without the need to use $(LREF tuple) for multiple seeds. This makes it easier
|
order reversed, and without the need to use $(REF_ALTTEXT $(D tuple),tuple,std,typecons)
|
||||||
to use in UFCS chains.
|
for multiple seeds. This makes it easier to use in UFCS chains.
|
||||||
|
|
||||||
$(LREF sum) is similar to `reduce!((a, b) => a + b)` that offers
|
$(LREF sum) is similar to `reduce!((a, b) => a + b)` that offers
|
||||||
pairwise summing of floating point numbers.
|
pairwise summing of floating point numbers.
|
||||||
|
@ -3206,8 +3206,9 @@ See_Also:
|
||||||
$(LREF sum) is similar to `fold!((a, b) => a + b)` that offers
|
$(LREF sum) is similar to `fold!((a, b) => a + b)` that offers
|
||||||
precise summing of floating point numbers.
|
precise summing of floating point numbers.
|
||||||
|
|
||||||
This is functionally equivalent to $(LREF reduce) with the argument order reversed,
|
This is functionally equivalent to $(LREF reduce) with the argument order
|
||||||
and without the need to use $(LREF tuple) for multiple seeds.
|
reversed, and without the need to use $(REF_ALTTEXT $(D tuple),tuple,std,typecons)
|
||||||
|
for multiple seeds.
|
||||||
+/
|
+/
|
||||||
template fold(fun...)
|
template fold(fun...)
|
||||||
if (fun.length >= 1)
|
if (fun.length >= 1)
|
||||||
|
|
|
@ -2421,12 +2421,15 @@ public:
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
Parallel reduce on a random access range. Except as otherwise noted,
|
Parallel reduce on a random access range. Except as otherwise noted,
|
||||||
usage is similar to $(REF _reduce, std,algorithm,iteration). This
|
usage is similar to $(REF _reduce, std,algorithm,iteration). There is
|
||||||
function works by splitting the range to be reduced into work units,
|
also $(LREF fold) which does the same thing with a different parameter
|
||||||
which are slices to be reduced in parallel. Once the results from all
|
order.
|
||||||
work units are computed, a final serial reduction is performed on these
|
|
||||||
results to compute the final answer. Therefore, care must be taken to
|
This function works by splitting the range to be reduced into work
|
||||||
choose the seed value appropriately.
|
units, which are slices to be reduced in parallel. Once the results
|
||||||
|
from all work units are computed, a final serial reduction is performed
|
||||||
|
on these results to compute the final answer. Therefore, care must be
|
||||||
|
taken to choose the seed value appropriately.
|
||||||
|
|
||||||
Because the reduction is being performed in parallel, $(D functions)
|
Because the reduction is being performed in parallel, $(D functions)
|
||||||
must be associative. For notational simplicity, let # be an
|
must be associative. For notational simplicity, let # be an
|
||||||
|
@ -2502,6 +2505,12 @@ public:
|
||||||
After this function is finished executing, any exceptions thrown
|
After this function is finished executing, any exceptions thrown
|
||||||
are chained together via $(D Throwable.next) and rethrown. The chaining
|
are chained together via $(D Throwable.next) and rethrown. The chaining
|
||||||
order is non-deterministic.
|
order is non-deterministic.
|
||||||
|
|
||||||
|
See_Also:
|
||||||
|
|
||||||
|
$(LREF fold) is functionally equivalent to $(LREF _reduce) except the
|
||||||
|
range parameter comes first and there is no need to use
|
||||||
|
$(REF_ALTTEXT $(D tuple),tuple,std,typecons) for multiple seeds.
|
||||||
*/
|
*/
|
||||||
auto reduce(Args...)(Args args)
|
auto reduce(Args...)(Args args)
|
||||||
{
|
{
|
||||||
|
@ -2806,6 +2815,128 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
template fold(functions...)
|
||||||
|
{
|
||||||
|
/** Implements the homonym function (also known as `accumulate`, `compress`,
|
||||||
|
`inject`, or `foldl`) present in various programming languages of
|
||||||
|
functional flavor.
|
||||||
|
|
||||||
|
`fold` is functionally equivalent to $(LREF reduce) except the range
|
||||||
|
parameter comes first and there is no need to use $(REF_ALTTEXT
|
||||||
|
`tuple`,tuple,std,typecons) for multiple seeds.
|
||||||
|
|
||||||
|
There may be one or more callable entities (`functions` argument) to
|
||||||
|
apply.
|
||||||
|
|
||||||
|
Params:
|
||||||
|
args = Just the range to _fold over; or the range and one seed
|
||||||
|
per function; or the range, one seed per function, and
|
||||||
|
the work unit size
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The accumulated result as a single value for single function and
|
||||||
|
as a tuple of values for multiple functions
|
||||||
|
|
||||||
|
See_Also:
|
||||||
|
Similar to $(REF _fold, std,algorithm,iteration), `fold` is a wrapper around $(LREF reduce).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
---
|
||||||
|
static int adder(int a, int b)
|
||||||
|
{
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
static int multiplier(int a, int b)
|
||||||
|
{
|
||||||
|
return a * b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just the range
|
||||||
|
auto x = taskPool.fold!adder([1, 2, 3, 4]);
|
||||||
|
assert(x == 10);
|
||||||
|
|
||||||
|
// The range and the seeds (0 and 1 below; also note multiple
|
||||||
|
// functions in this example)
|
||||||
|
auto y = taskPool.fold!(adder, multiplier)([1, 2, 3, 4], 0, 1);
|
||||||
|
assert(y[0] == 10);
|
||||||
|
assert(y[1] == 24);
|
||||||
|
|
||||||
|
// The range, the seed (0), and the work unit size (20)
|
||||||
|
auto z = taskPool.fold!adder([1, 2, 3, 4], 0, 20);
|
||||||
|
assert(z == 10);
|
||||||
|
---
|
||||||
|
*/
|
||||||
|
auto fold(Args...)(Args args)
|
||||||
|
{
|
||||||
|
static assert(isInputRange!(Args[0]), "First argument must be an InputRange");
|
||||||
|
|
||||||
|
alias range = args[0];
|
||||||
|
|
||||||
|
static if (Args.length == 1)
|
||||||
|
{
|
||||||
|
// Just the range
|
||||||
|
return reduce!functions(range);
|
||||||
|
}
|
||||||
|
else static if (Args.length == 1 + functions.length ||
|
||||||
|
Args.length == 1 + functions.length + 1)
|
||||||
|
{
|
||||||
|
static if (functions.length == 1)
|
||||||
|
{
|
||||||
|
alias seeds = args[1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto seeds()
|
||||||
|
{
|
||||||
|
import std.typecons : tuple;
|
||||||
|
return tuple(args[1 .. functions.length+1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static if (Args.length == 1 + functions.length)
|
||||||
|
{
|
||||||
|
// The range and the seeds
|
||||||
|
return reduce!functions(seeds, range);
|
||||||
|
}
|
||||||
|
else static if (Args.length == 1 + functions.length + 1)
|
||||||
|
{
|
||||||
|
// The range, the seeds, and the work unit size
|
||||||
|
static assert(isIntegral!(Args[$-1]), "Work unit size must be an integral type");
|
||||||
|
return reduce!functions(seeds, range, args[$-1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
import std.conv : text;
|
||||||
|
static assert(0, "Invalid number of arguments (" ~ Args.length.text ~ "): Should be an input range, "
|
||||||
|
~ functions.length.text ~ " optional seed(s), and an optional work unit size.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test is not included in the documentation because even though these
|
||||||
|
// examples are for the inner fold() template, with their current location,
|
||||||
|
// they would appear under the outer one. (We can't move this inside the
|
||||||
|
// outer fold() template because then dmd runs out of memory possibly due to
|
||||||
|
// recursive template instantiation, which is surprisingly not caught.)
|
||||||
|
@system unittest
|
||||||
|
{
|
||||||
|
// Just the range
|
||||||
|
auto x = taskPool.fold!"a + b"([1, 2, 3, 4]);
|
||||||
|
assert(x == 10);
|
||||||
|
|
||||||
|
// The range and the seeds (0 and 1 below; also note multiple
|
||||||
|
// functions in this example)
|
||||||
|
auto y = taskPool.fold!("a + b", "a * b")([1, 2, 3, 4], 0, 1);
|
||||||
|
assert(y[0] == 10);
|
||||||
|
assert(y[1] == 24);
|
||||||
|
|
||||||
|
// The range, the seed (0), and the work unit size (20)
|
||||||
|
auto z = taskPool.fold!"a + b"([1, 2, 3, 4], 0, 20);
|
||||||
|
assert(z == 10);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Gets the index of the current thread relative to this $(D TaskPool). Any
|
Gets the index of the current thread relative to this $(D TaskPool). Any
|
||||||
thread not in this pool will receive an index of 0. The worker threads in
|
thread not in this pool will receive an index of 0. The worker threads in
|
||||||
|
@ -3338,6 +3469,28 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@system unittest
|
||||||
|
{
|
||||||
|
import std.algorithm.iteration : sum;
|
||||||
|
import std.range : iota;
|
||||||
|
import std.typecons : tuple;
|
||||||
|
|
||||||
|
enum N = 100;
|
||||||
|
auto r = iota(1, N + 1);
|
||||||
|
const expected = r.sum();
|
||||||
|
|
||||||
|
// Just the range
|
||||||
|
assert(taskPool.fold!"a + b"(r) == expected);
|
||||||
|
|
||||||
|
// Range and seeds
|
||||||
|
assert(taskPool.fold!"a + b"(r, 0) == expected);
|
||||||
|
assert(taskPool.fold!("a + b", "a + b")(r, 0, 0) == tuple(expected, expected));
|
||||||
|
|
||||||
|
// Range, seeds, and work unit size
|
||||||
|
assert(taskPool.fold!"a + b"(r, 0, 42) == expected);
|
||||||
|
assert(taskPool.fold!("a + b", "a + b")(r, 0, 0, 42) == tuple(expected, expected));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns a lazily initialized global instantiation of $(D TaskPool).
|
Returns a lazily initialized global instantiation of $(D TaskPool).
|
||||||
This function can safely be called concurrently from multiple non-worker
|
This function can safely be called concurrently from multiple non-worker
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue