Skip to content

Commit

Permalink
Merge pull request #4069 from John-Colvin/patch-16
Browse files Browse the repository at this point in the history
faster pairwise summation
  • Loading branch information
DmitryOlshansky committed Apr 12, 2016
2 parents 6c0dbab + 678a511 commit 1d2e88c
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 12 deletions.
111 changes: 101 additions & 10 deletions std/algorithm/iteration.d
Expand Up @@ -3595,7 +3595,7 @@ private struct SplitterResult(alias isTerminator, Range)

private Range _input;
private size_t _end = 0;
static if(!fullSlicing)
static if (!fullSlicing)
private Range _next;

private void findTerminator()
Expand All @@ -3617,7 +3617,7 @@ private struct SplitterResult(alias isTerminator, Range)
this(Range input)
{
_input = input;
static if(!fullSlicing)
static if (!fullSlicing)
_next = _input.save;

if (!_input.empty)
Expand Down Expand Up @@ -3867,7 +3867,7 @@ if (isSomeChar!C)
{
import std.algorithm.comparison : equal;
import std.meta : AliasSeq;
foreach(S; AliasSeq!(string, wstring, dstring))
foreach (S; AliasSeq!(string, wstring, dstring))
{
import std.conv : to;
S a = " a bcd ef gh ";
Expand Down Expand Up @@ -4043,7 +4043,10 @@ if (isInputRange!R && !isInfinite!R && is(typeof(seed = seed + r.front)))
static if (isFloatingPoint!E)
{
static if (hasLength!R && hasSlicing!R)
{
if (r.empty) return seed;
return seed + sumPairwise!E(r);
}
else
return sumKahan!E(seed, r);
}
Expand All @@ -4054,16 +4057,97 @@ if (isInputRange!R && !isInfinite!R && is(typeof(seed = seed + r.front)))
}

// Pairwise summation http://en.wikipedia.org/wiki/Pairwise_summation
private auto sumPairwise(Result, R)(R r)
private auto sumPairwise(F, R)(R data)
if (isInputRange!R && !isInfinite!R)
{
static assert (isFloatingPoint!Result);
switch (r.length)
import core.bitop : bsf;
// Works for r with at least length < 2^^(64 + log2(16)), in keeping with the use of size_t
// elsewhere in std.algorithm and std.range on 64 bit platforms. The 16 in log2(16) comes
// from the manual unrolling in sumPairWise16
F[64] store = void;
size_t idx = 0;

auto collapseStore(T)(T k)
{
case 0: return cast(Result) 0;
case 1: return cast(Result) r.front;
case 2: return cast(Result) r[0] + cast(Result) r[1];
default: return sumPairwise!Result(r[0 .. $ / 2]) + sumPairwise!Result(r[$ / 2 .. $]);
auto lastToKeep = idx - cast(uint)bsf(k+1);
while (idx > lastToKeep)
{
store[idx - 1] += store[idx];
--idx;
}
}

static if (hasLength!R)
{
foreach (k; 0 .. data.length / 16)
{
static if (isRandomAccessRange!R && hasSlicing!R)
{
store[idx] = sumPairwise16!F(data);
data = data[16 .. $];
}
else store[idx] = sumPairwiseN!(16, false, F)(data);

collapseStore(k);
++idx;
}

size_t i = 0;
foreach (el; data)
{
store[idx] = el;
collapseStore(i);
++idx;
++i;
}
}
else
{
size_t k = 0;
while (!data.empty)
{
store[idx] = sumPairwiseN!(16, true, F)(data);
collapseStore(k);
++idx;
++k;
}
}

F s = store[idx - 1];
foreach_reverse (j; 0 .. idx - 1)
s += store[j];

return s;
}

private auto sumPairwise16(F, R)(R r)
if (isRandomAccessRange!R)
{
return (((cast(F)r[ 0] + r[ 1]) + (cast(F)r[ 2] + r[ 3]))
+ ((cast(F)r[ 4] + r[ 5]) + (cast(F)r[ 6] + r[ 7])))
+ (((cast(F)r[ 8] + r[ 9]) + (cast(F)r[10] + r[11]))
+ ((cast(F)r[12] + r[13]) + (cast(F)r[14] + r[15])));
}

private auto sumPair(bool needEmptyChecks, F, R)(ref R r)
if (isForwardRange!R && !isRandomAccessRange!R)
{
static if (needEmptyChecks) if (r.empty) return F(0);
F s0 = r.front;
r.popFront();
static if (needEmptyChecks) if (r.empty) return s0;
s0 += r.front;
r.popFront();
return s0;
}

private auto sumPairwiseN(size_t N, bool needEmptyChecks, F, R)(ref R r)
if (isForwardRange!R && !isRandomAccessRange!R)
{
static assert(!(N & (N-1))); //isPow2
static if (N == 2) return sumPair!(needEmptyChecks, F)(r);
else return sumPairwiseN!(N/2, needEmptyChecks, F)(r)
+ sumPairwiseN!(N/2, needEmptyChecks, F)(r);
}

// Kahan algo http://en.wikipedia.org/wiki/Kahan_summation_algorithm
Expand Down Expand Up @@ -4179,6 +4263,13 @@ unittest
assert(sb == (BigInt(ulong.max/2) * 10));
}

@safe pure nothrow @nogc unittest
{
import std.range;
foreach(n; iota(50))
assert(repeat(1.0, n).sum == n);
}

// uniq
/**
Lazily iterates unique consecutive elements of the given range (functionality
Expand Down
14 changes: 12 additions & 2 deletions std/range/package.d
Expand Up @@ -1847,9 +1847,9 @@ if (isInputRange!(Unqual!Range) &&
if (hasSlicing!R)
{
assert(i <= j, "Invalid slice bounds");
assert(j - i <= length, "Attempting to slice past the end of a "
assert(j <= length, "Attempting to slice past the end of a "
~ Take.stringof);
return source[i .. j - i];
return source[i .. j];
}
}
else static if (hasLength!R)
Expand Down Expand Up @@ -2060,6 +2060,16 @@ if (isInputRange!(Unqual!R) && (isInfinite!(Unqual!R) || !hasSlicing!(Unqual!R)
static assert(is(Take!(typeof(myRepeat))));
}

@safe nothrow @nogc unittest
{
//check for correct slicing of Take on an infinite range
import std.algorithm : equal;
foreach (start; 0 .. 4)
foreach (stop; start .. 4)
assert(iota(4).cycle.take(4)[start .. stop]
.equal(iota(start, stop)));
}

@safe unittest
{
// Check that one can declare variables of all Take types,
Expand Down

0 comments on commit 1d2e88c

Please sign in to comment.