-
-
Save GotoFinal/2f057616f300045c7638bd11b250c20a to your computer and use it in GitHub Desktop.
For blog
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package dev.gotofinal; | |
import org.openjdk.jmh.annotations.*; | |
import org.openjdk.jmh.infra.Blackhole; | |
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.ThreadLocalRandom; | |
import java.util.concurrent.TimeUnit; | |
import java.util.function.Consumer; | |
import java.util.function.Supplier; | |
import java.util.stream.IntStream; | |
import java.util.stream.Stream; | |
@OutputTimeUnit(TimeUnit.MILLISECONDS) | |
@BenchmarkMode({Mode.SampleTime}) | |
//@BenchmarkMode({Mode.SampleTime, Mode.AverageTime}) | |
@Warmup(iterations = 5, time = 20, batchSize = 1) | |
@Measurement(iterations = 10, time = 10, batchSize = 1) | |
@Fork(value = 1, jvmArgs = {"-Xms2G", "-Xmx2G", "-XX:+AlwaysPreTouch"}) | |
@State(Scope.Thread) | |
public class AllocBenchmark { | |
private static final int ITERATIONS = 10000; | |
private World world; | |
private Supplier<Position> positionGenerator; | |
private Supplier<Position> noAllocPositionGenerator; | |
private PositionGenerator noHeapPositionGenerator; | |
@Setup | |
public void setup(Blackhole blackhole) { | |
world = new World(blackhole); | |
positionGenerator = () -> { | |
ThreadLocalRandom random = ThreadLocalRandom.current(); | |
double x = random.nextDouble(-10000000, 10000000); | |
double y = random.nextDouble(-10000000, 10000000); | |
double z = random.nextDouble(-10000000, 10000000); | |
return new Position(x, y, z); | |
}; | |
Position pos = new Position(0, 0, 0); | |
noAllocPositionGenerator = () -> { | |
ThreadLocalRandom random = ThreadLocalRandom.current(); | |
double x = random.nextDouble(-10000000, 10000000); | |
double y = random.nextDouble(-10000000, 10000000); | |
double z = random.nextDouble(-10000000, 10000000); | |
return pos.set(x, y, z); | |
}; | |
noHeapPositionGenerator = new PositionGenerator(); | |
} | |
@Benchmark | |
public void tick() throws Throwable { | |
Stream.generate(positionGenerator) | |
.limit(ITERATIONS) | |
.forEach(pos -> world.updateAt(pos)); | |
} | |
@Benchmark | |
@Threads(-1) | |
public void tick_threaded() throws Throwable { | |
Stream.generate(positionGenerator) | |
.limit(ITERATIONS) | |
.forEach(pos -> world.updateAt(pos)); | |
} | |
@Benchmark | |
@Threads(-1) | |
@Fork(value = 1, jvmArgs = {"-Xms20M", "-Xmx20M", "-XX:+AlwaysPreTouch"}) | |
public void tick_threaded20M() throws Throwable { | |
Stream.generate(positionGenerator) | |
.limit(ITERATIONS) | |
.forEach(pos -> world.updateAt(pos)); | |
} | |
@Benchmark | |
@Threads(-1) | |
@Fork(value = 1, jvmArgs = {"-Xms128M", "-Xmx128M", "-XX:+AlwaysPreTouch"}) | |
public void tick_threaded128M() throws Throwable { | |
Stream.generate(positionGenerator) | |
.limit(ITERATIONS) | |
.forEach(pos -> world.updateAt(pos)); | |
} | |
@Benchmark | |
public void tickNoAlloc() throws Throwable { | |
for (int i = 0; i < ITERATIONS; i++) { | |
world.updateAt_NoAlloc(noAllocPositionGenerator.get()); | |
} | |
} | |
@Benchmark | |
public void tickNoHeap() throws Throwable { | |
for (int i = 0; i < ITERATIONS; i++) { | |
world.updateAt_NoHeap(noHeapPositionGenerator.nextX(), noHeapPositionGenerator.nextY(), noHeapPositionGenerator.nextZ()); | |
} | |
} | |
@Benchmark | |
@Threads(-1) | |
public void tickNoHeap_threaded() throws Throwable { | |
IntStream.rangeClosed(0, ITERATIONS) | |
.forEach(pos -> world.updateAt_NoHeap(noHeapPositionGenerator.nextX(), noHeapPositionGenerator.nextY(), noHeapPositionGenerator.nextZ())); | |
} | |
@Fork(value = 1, jvmArgs = {"-Xms128M", "-Xmx128M", "-XX:+AlwaysPreTouch"}) | |
@Threads(-1) | |
@Benchmark | |
public void tickNoHeap_threaded128M() throws Throwable { | |
IntStream.rangeClosed(0, ITERATIONS) | |
.forEach(pos -> world.updateAt_NoHeap(noHeapPositionGenerator.nextX(), noHeapPositionGenerator.nextY(), noHeapPositionGenerator.nextZ())); | |
} | |
@Fork(value = 1, jvmArgs = {"-Xms20M", "-Xmx20M", "-XX:+AlwaysPreTouch"}) | |
@Threads(-1) | |
@Benchmark | |
public void tickNoHeap_threaded20M() throws Throwable { | |
IntStream.rangeClosed(0, ITERATIONS) | |
.forEach(pos -> world.updateAt_NoHeap(noHeapPositionGenerator.nextX(), noHeapPositionGenerator.nextY(), noHeapPositionGenerator.nextZ())); | |
} | |
public static void main(String[] args) throws RunnerException { | |
Options opt = new OptionsBuilder() | |
.include(AllocBenchmark.class.getName()) | |
.build(); | |
new Runner(opt).run(); | |
} | |
} | |
class PositionGenerator { | |
double nextX() { | |
ThreadLocalRandom random = ThreadLocalRandom.current(); | |
return random.nextDouble(-10000000, 10000000); | |
} | |
double nextY() { | |
ThreadLocalRandom random = ThreadLocalRandom.current(); | |
return random.nextDouble(-10000000, 10000000); | |
} | |
double nextZ() { | |
ThreadLocalRandom random = ThreadLocalRandom.current(); | |
return random.nextDouble(-10000000, 10000000); | |
} | |
} | |
class World { | |
private final Blackhole blackhole; | |
World(Blackhole blackhole) { | |
this.blackhole = blackhole; | |
} | |
public void updateAt(Position position) { | |
position.forAllNeighborsInRange(3, newPosition -> spawnMonsterIfNotPresent(newPosition)); // cube 7x7x7 | |
spawnChestIfNotPresent(position); | |
} | |
public void updateAt_NoAlloc(Position position) { | |
spawnChestIfNotPresent(position); | |
double posX = position.getX(); | |
double posY = position.getY(); | |
double posZ = position.getZ(); | |
for (int x = -3; x <= 3; x++) { | |
for (int y = -3; y <= 3; y++) { | |
for (int z = -3; z <= 3; z++) { | |
if (x == 0 && y == 0 && z == 0) continue; | |
spawnMonsterIfNotPresent(position.set(posX + x, posY + y, posZ + z)); | |
} | |
} | |
} | |
} | |
public void updateAt_NoHeap(double posX, double posY, double posZ) { | |
spawnChestIfNotPresent(posX, posY, posZ); | |
for (int x = -3; x <= 3; x++) { | |
for (int y = -3; y <= 3; y++) { | |
for (int z = -3; z <= 3; z++) { | |
if (x == 0 && y == 0 && z == 0) continue; | |
spawnMonsterIfNotPresent(posX + x, posY + y, posZ + z); | |
} | |
} | |
} | |
} | |
private void spawnMonsterIfNotPresent(Position position) { | |
blackhole.consume(position); | |
} | |
private void spawnChestIfNotPresent(Position position) { | |
blackhole.consume(position); | |
} | |
private void spawnMonsterIfNotPresent(double x, double y, double z) { | |
blackhole.consume(x + y + z); | |
} | |
private void spawnChestIfNotPresent(double x, double y, double z) { | |
blackhole.consume(x + y + z); | |
} | |
} | |
class Position { | |
private double x; | |
private double y; | |
private double z; | |
Position(double x, double y, double z) { | |
this.x = x; | |
this.y = y; | |
this.z = z; | |
} | |
public double getX() { | |
return x; | |
} | |
public double getY() { | |
return y; | |
} | |
public double getZ() { | |
return z; | |
} | |
public Position set(double x, double y, double z) { | |
this.x = x; | |
this.y = y; | |
this.z = z; | |
return this; | |
} | |
void forAllNeighborsInRange(int range, Consumer<Position> positionConsumer) { | |
IntStream.rangeClosed(-range, range).forEach(x -> { | |
IntStream.rangeClosed(-range, range).forEach(y -> { | |
IntStream.rangeClosed(-range, range).forEach(z -> { | |
if (x == 0 && y == 0 && z == 0) return; | |
positionConsumer.accept(new Position(this.x + x, this.y + y, this.z + z)); | |
}); | |
}); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment