Skip to content

Commit 0ff8bfe

Browse files
authored
Merge pull request twitter#591 from twitter/sritchie/tuple_performance
optimize Generated{Abstract,Product}Algebra with benchmarking
2 parents 87cbf25 + 83cb518 commit 0ff8bfe

File tree

10 files changed

+796
-304
lines changed

10 files changed

+796
-304
lines changed

CHANGES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* Deprecates broken group/ring for `Future`/`Try`: https://github.com/twitter/algebird/pull/584
1515
* Add `metricsLaws[T]` to `BaseProperties` in `algebird-test`: https://github.com/twitter/algebird/pull/584
1616
* Modify generated `Tuple2Monoid`, etc to extend `TupleNSemigroup`, giving subclasses access to efficient `sumOption`: https://github.com/twitter/algebird/pull/585
17+
* optimize `Generated{Abstract,Product}Algebra.sumOption` with benchmarking https://github.com/twitter/algebird/pull/591
1718

1819
### Version 0.12.2 ###
1920

@@ -36,7 +37,6 @@
3637
* Add `Batched[A]` type for efficient lazy addition: https://github.com/twitter/algebird/pull/530
3738
* Add a default `k` value for `Aggregator.approximatePercentile`: https://github.com/twitter/algebird/pull/531
3839

39-
4040
### Version 0.12.0 ###
4141

4242
* Implement an appendMonoid Aggregator factory which yields aggregators…: https://github.com/twitter/algebird/pull/501

algebird-benchmark/README.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
# algebird-benchmark
2+
13
[jmh](http://openjdk.java.net/projects/code-tools/jmh/)-based Benchmarks for Algebird data structures.
24

3-
# Usage
5+
## Usage
46

57
Run the following commands from the top-level Algebird directory:
68

@@ -10,19 +12,19 @@ Run the following commands from the top-level Algebird directory:
1012
Now you can run the following commands from within the sbt REPL:
1113

1214
# List available benchmarks
13-
> run -l
15+
> jmh:run -l
1416

1517
# Run a particular benchmark
16-
> run .*HLLBenchmark.*
18+
> jmh:run -t1 -f1 -wi 2 -i 3 .*AveragedValueBenchmark.*
1719

18-
# Run all benchmarks (apparently this is broken, see https://github.com/softprops/cappi/issues/1)
19-
> run .*
20+
# Run all benchmarks
21+
> jmh:run .*
2022

21-
You can find further details in the [sbt-jmh](https://github.com/ktoso/sbt-jmh) documentation, which is the sbt plugin
22-
we use to run the jmh benchmarks.
23+
These options tell JMH to run the benchmark with 1 thread (`-t1`), 1 fork (`-f1`), 2 warmup iterations and 3 real iterations. You can find further details in the [sbt-jmh](https://github.com/ktoso/sbt-jmh) documentation.
2324

2425
Example output for [CMSBenchmark](src/main/scala/com/twitter/algebird/benchmark/CMSBenchmark.scala):
2526

27+
```
2628
Running:
2729
3 Iterations
2830
3 Warmups per trial
@@ -68,3 +70,4 @@ Running:
6870
[info] CMSBenchmark.timePlusOfFirstHundredIntegersWithLongCms 0.0000001 0.005 0.2 2048 100 thrpt 3 1768.006 ± 2623.229 ops/s
6971
[info] CMSBenchmark.timePlusOfRandom2048BitNumbersWithBigIntCms 0.0000001 0.005 0.2 2048 100 thrpt 3 106.443 ± 201.605 ops/s
7072
[info] CMSBenchmark.timePlusOfRandom2048BitNumbersWithStringCms 0.0000001 0.005 0.2 2048 100 thrpt 3 107.031 ± 139.073 ops/s
73+
```
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.twitter.algebird
2+
package benchmark
3+
4+
import scala.util.Random
5+
import org.openjdk.jmh.annotations._
6+
import org.openjdk.jmh.infra.Blackhole
7+
8+
import scala.math._
9+
10+
object Tuple4Benchmark {
11+
type Long4 = (Long, Long, Long, Long)
12+
@State(Scope.Benchmark)
13+
class Tuple4State {
14+
/**
15+
* This monoid lives in `GeneratedAbstractAlgebra.scala`.
16+
*/
17+
val tupleMonoid: Monoid[Long4] = implicitly
18+
19+
/**
20+
* This monoid lives in `GeneratedProductAlgebra.scala`.
21+
*/
22+
val productMonoid: Monoid[Long4] =
23+
Monoid[Long4, Long, Long, Long, Long](Tuple4.apply, Tuple4.unapply)
24+
25+
@Param(Array("10000"))
26+
var numElements: Int = 0
27+
28+
var inputData: Seq[(Long, Long, Long, Long)] = _
29+
30+
private def randL: Long = Random.nextInt(1000).toLong
31+
32+
@Setup(Level.Trial)
33+
def setup(): Unit = {
34+
inputData = Seq.fill(numElements)((randL, randL, randL, randL))
35+
}
36+
}
37+
}
38+
39+
class Tuple4Benchmark {
40+
import Tuple4Benchmark._
41+
42+
@Benchmark
43+
def timeTuplePlus(state: Tuple4State, bh: Blackhole) =
44+
bh.consume(state.inputData.reduce(state.tupleMonoid.plus(_, _)))
45+
46+
@Benchmark
47+
def timeTupleSumOption(state: Tuple4State, bh: Blackhole) =
48+
bh.consume(state.tupleMonoid.sumOption(state.inputData))
49+
50+
@Benchmark
51+
def timeProductPlus(state: Tuple4State, bh: Blackhole) =
52+
bh.consume(state.inputData.reduce(state.productMonoid.plus(_, _)))
53+
54+
@Benchmark
55+
def timeProductSumOption(state: Tuple4State, bh: Blackhole) =
56+
bh.consume(state.productMonoid.sumOption(state.inputData))
57+
}

algebird-core/src/main/scala/com/twitter/algebird/BufferedOperation.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,19 @@ abstract class ArrayBufferedOperation[I, O](size: Int) extends Buffered[I, O] {
5353
def isFlushed = buffer.isEmpty
5454
}
5555

56+
object ArrayBufferedOperation {
57+
/**
58+
* Returns an ArrayBufferedOperation instance that internally uses
59+
* the `sumOption` implementation of the supplied Semigroup[T]
60+
*/
61+
def fromSumOption[T](size: Int)(implicit sg: Semigroup[T]): BufferedReduce[T] =
62+
new ArrayBufferedOperation[T, T](size) with BufferedReduce[T] {
63+
// calling `.get is okay because the interface guarantees a
64+
// non-empty sequence.
65+
def operate(items: Seq[T]) = sg.sumOption(items.iterator).get
66+
}
67+
}
68+
5669
/**
5770
* This never emits on put, you must call flush
5871
* designed to be use in the stackable pattern with ArrayBufferedOperation

0 commit comments

Comments
 (0)