package water;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.profile.StackProfiler;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
import water.fvec.Chunk;
import water.fvec.NewChunk;
/**
* Chunk access patterns benchmark
*/
@State(Scope.Thread)
//@Fork(value = 1, jvmArgsAppend = "-XX:+PrintCompilation")
@Fork(value = 1, jvmArgsAppend = "-Xmx12g")
@Warmup(iterations = 5)
@Measurement(iterations = 10)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class ChunkBench {
@Param({"1000", "10000"})
private int cols;
@Param({"1000", "100000"})
private int rows;
private Chunk[] chunks;
private double[][] raw;
@Benchmark
public double rawArrayRead() {
double sum = 0;
for (int col = 0; col < cols; ++col) {
for (int row = 0; row < rows; ++row) {
sum += raw[col][row];
}
}
return sum;
}
@Benchmark
public double rowsColsRead() {
double sum = 0;
for (int row = 0; row < rows; ++row) {
for (int col = 0; col < cols; ++col) {
sum += chunks[col].atd(row);
}
}
return sum;
}
@Benchmark
public double colsRowsRead() {
double sum = 0;
for (int col = 0; col < cols; ++col) {
for (int row = 0; row < rows; ++row) {
sum += chunks[col].atd(row);
}
}
return sum;
}
@Benchmark
public double colsRowsReadWithTypeDispatch() {
double sum = 0;
for (int col = 0; col < cols; ++col) {
sum += walkChunk(rows, chunks[col]);
}
return sum;
}
@Benchmark
public double colsRowsWithBulkRead() {
double sum = 0;
// Preallocate array for storing unpacked chunk data
double [] vals = new double[chunks[0]._len];
for (int col = 0; col < cols; ++col) {
sum += walkChunkBulk(rows, chunks[col], vals);
}
return sum;
}
@Benchmark
public double colsRowsReadWithFinalChunk() {
double sum = 0;
for (int col = 0; col < cols; ++col) {
final Chunk c = chunks[col];
for (int row = 0; row < rows; ++row) {
sum += c.atd(row);
}
}
return sum;
}
private static double walkChunk(int rows, final Chunk c) {
double sum =0;
for (int row = 0; row < rows; ++row) {
sum += c.atd(row);
}
return sum;
}
private static double walkChunkBulk(int rows, final Chunk c, double [] vals) {
double sum = 0;
c.getDoubles(vals, 0, c._len);
for (int i = 0; i < rows; ++i)
sum += vals[i];
return sum;
}
@Setup
public void setup() {
raw = new double[cols][rows];
for (int col = 0; col < cols; ++col) {
for (int row = 0; row < rows; ++row) {
raw[col][row] = get(col, row);
}
}
chunks = new Chunk[cols];
for (int col = 0; col < cols; ++col) {
chunks[col] = new NewChunk(raw[col]).compress();
}
}
private static double get(int j, int i) {
switch (j % 4) { // do 4 chunk types
case 0:
return i % 200; //C1NChunk - 1 byte integer
case 1:
return i % 500; //C2Chunk - 2 byte integer
case 2:
return i*Integer.MAX_VALUE;
case 3:
return i == 17 ? 1 : 0; //CX0Chunk - sparse
default:
throw H2O.unimpl();
}
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(ChunkBench.class.getSimpleName())
.addProfiler(StackProfiler.class)
// .addProfiler(GCProfiler.class)
.build();
new Runner(opt).run();
}
}