Skip to content

Instantly share code, notes, and snippets.

@wilzbach
Last active March 5, 2017 05:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wilzbach/425c72f028d87b19848c651e3d5a7063 to your computer and use it in GitHub Desktop.
Save wilzbach/425c72f028d87b19848c651e3d5a7063 to your computer and use it in GitHub Desktop.

With mapping

> ldc -release -O5 -mcpu=native test.d && ./test
reduce!(min,max) = 16 secs, 745 ms, 280 μs, and 1 hnsec
fold.minMax     = 13 secs, 756 ms, 979 μs, and 4 hnsecs
minmaxElement   = 14 secs, 179 ms, 390 μs, and 1 hnsec

DMD with only 10K instead of 100K elements:

> dmd -release -noboundscheck -inline -O test.d && ./test
reduce!(min,max) = 24 secs, 714 ms, 778 μs, and 2 hnsecs
minmaxElement   = 1 sec, 265 ms, 808 μs, and 2 hnsecs

Mapping with alias m = (a) => a * a

> ldc -release -O5 -mcpu=native test.d && ./test
reduce!(min,max) = 25 secs, 262 ms, 508 μs, and 9 hnsecs
fold.minMax     = 15 secs, 106 ms, 617 μs, and 7 hnsecs
minmaxElement   = 14 secs, 807 ms, 857 μs, and 1 hnsec
./test  55.18s user 0.00s system 99% cpu 55.186 total

DMD with only 10K instead of 100K elements:

> dmd -release -noboundscheck -inline -O test.d && ./test
reduce!(min,max) = 11 secs, 117 ms, 859 μs, and 3 hnsecs
fold.minMax     = 24 secs, 107 ms, 105 μs, and 9 hnsecs
minmaxElement   = 1 sec, 278 ms, 684 μs, and 4 hnsecs

Without mapping

> ldc -release -O3 test.d && ./test
reduce!(min,max) = 1 sec, 297 ms, 376 μs, and 6 hnsecs
minmaxElement   = 13 secs, 311 ms, 938 μs, and 1 hnsec
minmaxElementNoMap = 1 sec, 288 ms, 746 μs, and 8 hnsecs
./test  18.04s user 0.02s system 99% cpu 18.067 total
> dmd -release -noboundscheck -inline -O test.d && ./test
reduce!(min,max) = 25 secs, 143 ms, 399 μs, and 1 hnsec
minmaxElement   = 12 secs, 611 ms, 64 μs, and 7 hnsecs
minmaxElementNoMap = 12 secs, 588 ms, 154 μs, and 5 hnsecs
./test  50.35s user 0.00s system 99% cpu 50.351 total
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);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment