Speed up sequence splitting and zipping some more
authorDavid Feuer <David.Feuer@gmail.com>
Sat, 21 May 2016 06:01:50 +0000 (02:01 -0400)
committerDavid Feuer <David.Feuer@gmail.com>
Sat, 21 May 2016 21:34:30 +0000 (17:34 -0400)
commit8e4be55514dde91eb0cfbe548bafff21cfa6dd5e
treeb09a2310654273a79568ed2dd8ee21727c49cb25
parent383b237caccbdbc42917c0c2f6dc940ca2030c98
Speed up sequence splitting and zipping some more

Rewrite `splitAt`, `take`, and `drop` helper functions to build full results
instead of returning pieces, and to build their results eagerly, instead of
(unnecessarily) suspending them lazily. This has a major impact on performance.
GHC specialization can't help us, because we're now treating the top layer of
the tree differently in every helper. As a result, there's a lot of source
code, but that's our problem.

Benchmark results, compared to containers 0.5.7.1:

Old: benchmarking splitAt/append/10
time                 1.950 ms   (1.946 ms .. 1.954 ms)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 1.952 ms   (1.949 ms .. 1.958 ms)
std dev             53 12.41 μs   (7.154 μs .. 19.01 μs)

New: benchmarking splitAt/append/10
time                 1.056 ms   (1.050 ms .. 1.065 ms)
                     0.995 R²   (0.983 R² .. 1.000 R²)
mean                 1.073 ms   (1.057 ms .. 1.147 ms)
std dev              97.06 μs   (9.638 μs .. 221.7 μs)
variance introduced by outliers: 68% (severely inflated)

Old: benchmarking splitAt/append/100
time                 13.81 ms   (13.76 ms .. 13.84 ms)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 13.88 ms   (13.84 ms .. 13.95 ms)
std dev              119.1 μs   (48.84 μs .. 204.2 μs)

New: benchmarking splitAt/append/100
time                 8.028 ms   (8.014 ms .. 8.046 ms)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 8.041 ms   (8.029 ms .. 8.075 ms)
std dev              51.02 μs   (16.07 μs .. 94.69 μs)

Old: benchmarking splitAt/append/1000
time                 25.58 ms   (25.44 ms .. 25.75 ms)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 25. ms   (25.47 ms .. 25.63 ms)
std dev              184.0 μs   (128.7 μs .. 272.0 μs)

New: benchmarking splitAt/append/1000
time                 15.30 ms   (15.20 ms .. 15.41 ms)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 15.32 ms   (15.26 ms .. 15.45 ms)
std dev              190.0 μs   (89.60 μs .. 351.1 μs)

Old: benchmarking zip/ix10000/5000
time                 13.52 μs   (13.41 μs .. 13.77 μs)
                     0.996 R²   (0.987 R² .. 1.000 R²)
mean                 13.65 μs   (13.50 μs .. 14.19 μs)
std dev              882.1 ns   (174.4 ns .. 1.839 μs)
variance introduced by outliers: 72% (severely inflated)

New: benchmarking zip/ix10000/5000
time                 8.806 μs   (8.768 μs .. 8.857 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 8.787 μs   (8.766 μs .. 8.879 μs)
std dev              113.3 ns   (30.31 ns .. 244.0 ns)

Old: benchmarking zip/nf100
time                 19.99 μs   (19.96 μs .. 20.04 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 19.98 μs   (19.96 μs .. 20.00 μs)
std dev              64.04 ns   (34.52 ns .. 100.9 ns)

New: benchmarking zip/nf100
time                 13.19 μs   (13.15 μs .. 13.24 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 13.19 μs   (13.15 μs .. 13.28 μs)
std dev              157.8 ns   (86.36 ns .. 288.1 ns)

Old: benchmarking zip/nf10000
time                 2.578 ms   (2.567 ms .. 2.591 ms)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 2.584 ms   (2.574 ms .. 2.598 ms)
std dev              40.16 μs   (30.17 μs .. 57.04 μs)

New: benchmarking zip/nf10000
time                 1.768 ms   (1.764 ms .. 1.774 ms)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 1.778 ms   (1.772 ms .. 1.793 ms)
std dev              29.50 μs   (16.59 μs .. 56.72 μs)
.gitignore
Data/Sequence.hs