|
import std.range; |
|
import std.traits; |
|
import std.algorithm; |
|
import std.functional; |
|
import std.typecons; |
|
|
|
private void doNotOptimizeAway(T)(auto ref T t) |
|
{ |
|
import core.thread : getpid; |
|
import std.stdio : writeln; |
|
if(getpid() == 1) { |
|
writeln(*cast(char*)&t); |
|
} |
|
assert(t[0] == 0); |
|
assert(t[1] == 99_999); |
|
} |
|
|
|
auto minmaxElement(alias map = "a", alias selector = "a < b", Range)(Range r) |
|
if (isInputRange!Range && !isInfinite!Range) |
|
in |
|
{ |
|
assert(!r.empty, "r is an empty range"); |
|
} |
|
body |
|
{ |
|
alias Element = ElementType!Range; |
|
Unqual!Element seed = r.front; |
|
r.popFront(); |
|
return minmaxElement!(map, selector)(r, seed, seed); |
|
} |
|
|
|
/// ditto |
|
auto minmaxElement(alias map = "a", alias selector = "a < b", Range, |
|
RangeElementType = ElementType!Range) |
|
(Range r, RangeElementType minSeed, RangeElementType maxSeed) |
|
if (isInputRange!Range && !isInfinite!Range && |
|
!is(CommonType!(ElementType!Range, RangeElementType) == void)) |
|
{ |
|
alias mapFun = unaryFun!map; |
|
alias selectorFun = binaryFun!selector; |
|
|
|
alias Element = ElementType!Range; |
|
alias CommonElement = CommonType!(Element, RangeElementType); |
|
alias MapType = Unqual!(typeof(mapFun(CommonElement.init))); |
|
|
|
Unqual!CommonElement minElement = minSeed; |
|
Unqual!CommonElement maxElement = maxSeed; |
|
|
|
MapType minElementMapped = mapFun(minElement); |
|
MapType maxElementMapped = mapFun(maxElement); |
|
|
|
alias minmaxTuple = Tuple!(Unqual!CommonElement, "min", Unqual!CommonElement, "max"); |
|
|
|
static if (isRandomAccessRange!Range && hasLength!Range) |
|
{ |
|
foreach (const i; 0 .. r.length) |
|
{ |
|
MapType mapElement = mapFun(r[i]); |
|
if (selectorFun(mapElement, minElementMapped)) |
|
{ |
|
minElement = r[i]; |
|
minElementMapped = mapElement; |
|
} |
|
if (selectorFun(maxElementMapped, mapElement)) |
|
{ |
|
maxElement = r[i]; |
|
maxElementMapped = mapElement; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
while (!r.empty) |
|
{ |
|
MapType mapElement = mapFun(r.front); |
|
if (selectorFun(mapElement, minElementMapped)) |
|
{ |
|
minElement = r.front; |
|
minElementMapped = mapElement; |
|
} |
|
if (selectorFun(maxElementMapped, mapElement)) |
|
{ |
|
maxElement = r.front; |
|
maxElementMapped = mapElement; |
|
} |
|
r.popFront(); |
|
} |
|
} |
|
return minmaxTuple(minElement, maxElement); |
|
} |
|
|
|
auto minmaxElementNoMap(alias selector = "a < b", Range)(Range r) |
|
if (isInputRange!Range && !isInfinite!Range) |
|
in |
|
{ |
|
assert(!r.empty, "r is an empty range"); |
|
} |
|
body |
|
{ |
|
alias Element = ElementType!Range; |
|
Unqual!Element seed = r.front; |
|
r.popFront(); |
|
return minmaxElementNoMap!(selector)(r, seed, seed); |
|
} |
|
|
|
auto minmaxElementNoMap(alias selector = "a < b", Range, |
|
RangeElementType = ElementType!Range) |
|
(Range r, RangeElementType minSeed, RangeElementType maxSeed) |
|
if (isInputRange!Range && !isInfinite!Range && |
|
!is(CommonType!(ElementType!Range, RangeElementType) == void)) |
|
{ |
|
alias selectorFun = binaryFun!selector; |
|
|
|
alias Element = ElementType!Range; |
|
alias CommonElement = CommonType!(Element, RangeElementType); |
|
|
|
Unqual!CommonElement minElement = minSeed; |
|
Unqual!CommonElement maxElement = maxSeed; |
|
|
|
alias minmaxTuple = Tuple!(Unqual!CommonElement, "min", Unqual!CommonElement, "max"); |
|
|
|
static if (isRandomAccessRange!Range && hasLength!Range) |
|
{ |
|
foreach (const i; 0 .. r.length) |
|
{ |
|
if (selectorFun(r[i], minElement)) |
|
{ |
|
minElement = r[i]; |
|
} |
|
if (selectorFun(maxElement, r[i])) |
|
{ |
|
maxElement = r[i]; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
while (!r.empty) |
|
{ |
|
if (selectorFun(r.front, minElement)) |
|
{ |
|
minElement = r.front; |
|
} |
|
if (selectorFun(maxElement, r.front)) |
|
{ |
|
maxElement = r.front; |
|
} |
|
r.popFront(); |
|
} |
|
} |
|
return minmaxTuple(minElement, maxElement); |
|
} |
|
|
|
void main() { |
|
import std.datetime; |
|
import std.stdio; |
|
import std.array; |
|
import std.conv; |
|
import std.random; |
|
auto arr = iota(100_000).array; |
|
arr.randomShuffle; |
|
enum benchmarkType = "map"; |
|
static if (benchmarkType == "map") |
|
{ |
|
alias m = (a) => a + a; |
|
auto bench = benchmark!( |
|
{ doNotOptimizeAway({ |
|
return arr.reduce!( |
|
(a, b) => min(m(a), m(b)), |
|
(a, b) => max(m(a), m(b)), |
|
); |
|
}());}, |
|
{ doNotOptimizeAway({ |
|
static struct MinMaxTuple(T) { |
|
T min, max, minMap, maxMap; |
|
T opIndex(size_t i) { return i ? max : min; } |
|
} |
|
return arr[1..$].fold!((a, b) { |
|
auto c = m(b); |
|
if (c < a.minMap) |
|
{ |
|
a.min = b; |
|
a.minMap = c; |
|
} |
|
if (c > a.maxMap) |
|
{ |
|
a.max = b; |
|
a.maxMap = c; |
|
} |
|
return a; |
|
})(MinMaxTuple!size_t(arr[0], arr[0], m(arr[0]), m(arr[0]))); |
|
}());}, |
|
{ doNotOptimizeAway({ |
|
return arr.minmaxElement!m; |
|
}());}, |
|
)(100_000); |
|
string[] names = ["reduce!(min,max)", "fold.minMax", "minmaxElement"]; |
|
} |
|
else |
|
{ |
|
auto bench = benchmark!( |
|
{ doNotOptimizeAway({ |
|
return arr.reduce!(min, max); |
|
}());}, |
|
{ doNotOptimizeAway({ |
|
return arr.minmaxElement; |
|
}());}, |
|
{ doNotOptimizeAway({ |
|
return arr.minmaxElementNoMap; |
|
}());}, |
|
)(100_000); |
|
string[] names = ["reduce!(min,max)", "minmaxElement", "minmaxElementNoMap"]; |
|
} |
|
|
|
foreach(j,r;bench) |
|
writefln("%-15s = %s", names[j], r.to!Duration); |
|
} |