mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[benchmarks] jmh-benchmarks for map and array lookup
+ j.u.HashMap vs fastutils + array binary vs linear search GitOrigin-RevId: e8e0b97b9d7484d65b8b9fd41af9b7805a75bd26
This commit is contained in:
committed by
intellij-monorepo-bot
parent
5b375ff7a2
commit
6d78bbb7b9
@@ -0,0 +1,124 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij;
|
||||
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
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.Arrays;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
/**
|
||||
* Compare linear search vs binary search
|
||||
* On Apple M1 Max binary search starts outperforming linear search ~between array length 32 and 64
|
||||
*/
|
||||
@BenchmarkMode({Mode.AverageTime/*, Mode.SampleTime*/})
|
||||
@OutputTimeUnit(NANOSECONDS)
|
||||
@Warmup(iterations = 2, time = 5, timeUnit = SECONDS)
|
||||
@Measurement(iterations = 5, time = 5, timeUnit = SECONDS)
|
||||
@Fork(1)
|
||||
@Threads(1)
|
||||
public class ArrayLookupBenchmark {
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
public static class DataContext {
|
||||
@Param({"8", "16", "32", "64"})
|
||||
public int ARRAY_SIZE = 64;
|
||||
|
||||
public Integer[] objects;
|
||||
public int[] ints;
|
||||
|
||||
@Setup
|
||||
public void setup() throws Exception {
|
||||
objects = new Integer[ARRAY_SIZE];
|
||||
ints = new int[ARRAY_SIZE];
|
||||
ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
for (int i = 0; i < ARRAY_SIZE; i++) {
|
||||
int value = rnd.nextInt(Integer.MIN_VALUE, Integer.MAX_VALUE);
|
||||
ints[i] = value;
|
||||
objects[i] = value;
|
||||
}
|
||||
Arrays.sort(ints);
|
||||
Arrays.sort(objects);
|
||||
}
|
||||
|
||||
public Integer randomObjectKey() {
|
||||
return randomIntKey();
|
||||
}
|
||||
|
||||
public int randomIntKey() {
|
||||
//should be ~1/2 of array values
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object _baseline() {
|
||||
ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
return rnd.nextInt(0, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object lookupIntLinear(DataContext dataContext) {
|
||||
int keyToLookup = dataContext.randomIntKey();
|
||||
int[] ints = dataContext.ints;
|
||||
|
||||
for (int i = 0; i < ints.length; i++) {
|
||||
int key = ints[i];
|
||||
if (key > keyToLookup) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object lookupObjectLinear(DataContext dataContext) {
|
||||
Integer keyToLookup = dataContext.randomObjectKey();
|
||||
Integer[] objects = dataContext.objects;
|
||||
|
||||
for (int i = 0; i < objects.length; i++) {
|
||||
Integer key = objects[i];
|
||||
if (key.compareTo(keyToLookup) > 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@Benchmark
|
||||
public Object lookupIntBinary(DataContext dataContext) {
|
||||
int keyToLookup = dataContext.randomIntKey();
|
||||
|
||||
return Arrays.binarySearch(dataContext.ints, keyToLookup);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object lookupObjectBinary(DataContext dataContext) {
|
||||
Integer keyToLookup = dataContext.randomObjectKey();
|
||||
|
||||
return Arrays.binarySearch(dataContext.objects, keyToLookup);
|
||||
}
|
||||
|
||||
|
||||
public static void main(final String[] args) throws RunnerException {
|
||||
final Options opt = new OptionsBuilder()
|
||||
.jvmArgs()
|
||||
.forks(1)
|
||||
.threads(1)
|
||||
//.mode(Mode.SingleShotTime)
|
||||
//.warmupIterations(1000)
|
||||
//.warmupBatchSize(1000)
|
||||
//.measurementIterations(1000)
|
||||
.include("\\W" + ArrayLookupBenchmark.class.getSimpleName() + ".*")
|
||||
.build();
|
||||
|
||||
new Runner(opt).run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
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.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
/**
|
||||
* Benchmarks of {@link java.util.HashMap} vs {@link it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap}
|
||||
* ...it seems like HashMap consistently outperforms Object2ObjectOpenHashMap, even though I can't really get the reasons
|
||||
* TODO RC: add some simple HMap impl into the benchmarks
|
||||
*/
|
||||
@BenchmarkMode({Mode.AverageTime/*, Mode.SampleTime*/})
|
||||
@OutputTimeUnit(NANOSECONDS)
|
||||
@Warmup(iterations = 2, time = 5, timeUnit = SECONDS)
|
||||
@Measurement(iterations = 5, time = 5, timeUnit = SECONDS)
|
||||
@Fork(1)
|
||||
@Threads(1)
|
||||
public class MapImplBenchmark {
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
public static class DataContext {
|
||||
@Param({"1000", "10000", "500000"})
|
||||
public int KEYS_COUNT = 500_000;
|
||||
|
||||
public Object[] existingKeys;
|
||||
public Object[] nonExistingKeys;
|
||||
|
||||
@Setup
|
||||
public void setup() throws Exception {
|
||||
existingKeys = new Object[KEYS_COUNT];
|
||||
nonExistingKeys = new Object[KEYS_COUNT];
|
||||
ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
for (int i = 0; i < KEYS_COUNT; i++) {
|
||||
int value = rnd.nextInt(0, Integer.MAX_VALUE);
|
||||
existingKeys[i] = value;
|
||||
nonExistingKeys[i] = -1 - value;
|
||||
}
|
||||
}
|
||||
|
||||
public Object existingKey(ThreadLocalRandom rnd) {
|
||||
return existingKeys[rnd.nextInt(0, KEYS_COUNT)];
|
||||
}
|
||||
|
||||
public Object nonExistingKey(ThreadLocalRandom rnd) {
|
||||
return nonExistingKeys[rnd.nextInt(0, KEYS_COUNT)];
|
||||
}
|
||||
}
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
public static class MapContext {
|
||||
@Param({"HashMap", "OpenHashMap"})
|
||||
public String mapKind = "HashMap";
|
||||
|
||||
public Map<Object, Object> mapImpl;
|
||||
|
||||
@Setup
|
||||
public void setup(DataContext dataContext) throws Exception {
|
||||
mapImpl = switch (mapKind) {
|
||||
case "HashMap" -> new HashMap<>();
|
||||
|
||||
case "OpenHashMap" ->
|
||||
//noinspection SSBasedInspection
|
||||
new Object2ObjectOpenHashMap<>();
|
||||
|
||||
default -> throw new IllegalArgumentException("Unrecognized map kind: " + mapKind);
|
||||
};
|
||||
|
||||
for (int i = 0; i < dataContext.KEYS_COUNT; i++) {
|
||||
mapImpl.put(
|
||||
dataContext.existingKeys[i],
|
||||
dataContext.existingKeys[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object _baseline(DataContext dataContext) {
|
||||
ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
return dataContext.existingKey(rnd);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object lookupExistingKey(DataContext dataContext,
|
||||
MapContext mapContext) {
|
||||
ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
return mapContext.mapImpl.get(dataContext.existingKey(rnd));
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object lookupNonExistingKey(DataContext dataContext,
|
||||
MapContext mapContext) {
|
||||
ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
return mapContext.mapImpl.get(dataContext.nonExistingKey(rnd));
|
||||
}
|
||||
|
||||
public static void main(final String[] args) throws RunnerException {
|
||||
final Options opt = new OptionsBuilder()
|
||||
.jvmArgs()
|
||||
.forks(1)
|
||||
.threads(1)
|
||||
//.mode(Mode.SingleShotTime)
|
||||
//.warmupIterations(1000)
|
||||
//.warmupBatchSize(1000)
|
||||
//.measurementIterations(1000)
|
||||
.include("\\W" + MapImplBenchmark.class.getSimpleName() + ".*")
|
||||
.build();
|
||||
|
||||
new Runner(opt).run();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user