У меня есть поле hashmap с 100_000
записями и двумя методами A и B.
- Метод A : вызовы
map.get()
один разсо случайным ключом - Метод B : Вызов
map.get()
десять раз с a случайным ключом (один и тот же ключ каждый раз)
Я настроил эти два метода с помощью JMH
для многократного запуска (весь тест занял более 1 часа на моем MacBook Pro) и измерения производительности, и получается, что Метод A всего лишь в два раза быстрее, чем Метод B . Я надеялся на 10-кратную разницу.
Я не смог объяснить это поведение, вот результаты
# Run complete. Total time: 01:08:36
Benchmark Mode Throughput Units
TestClass.benchmark_with_one_get thrpt 23819.007 operations/ms
TestClass.benchmark_with_ten_gets thrpt 12021.025 operations/ms
Затем я хотел поэкспериментировать больше, я отверг метод hashCode()
из hashmap * 1032Клавиша * s (TestKey
) с константной функцией (всегда возвращает целое число 5
), чтобы по существу сделать хэш-карту списком. Только тогда я мог видеть ожидаемые результаты. Я не запускал весь тест, так как для его завершения требуется более 6 часов, но вот приблизительные результаты (только из первых двух итераций)
Benchmark Mode Throughput Units
TestClass.benchmark_with_one_get thrpt 244.266 operations/s
TestClass.benchmark_with_ten_gets thrpt 24.981 operations/s
А вот исходный класс, который я использовал для оценки производительности.
package test.benchmark;
import org.openjdk.jmh.annotations.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
@State(Scope.Benchmark)
public class TestClass {
private Map<TestKey, Integer> map = new HashMap<>();
private List<TestKey> keyList = new ArrayList<>();
private int test1 = 0;
private int test2 = 0;
public TestClass() {
Random random = new Random();
for (int i = 0; i < 100_000; i++) {
TestKey testKey = new TestKey(i);
map.put(testKey, i);
keyList.add(new TestKey(random.nextInt(100_001)));
}
}
@Setup
public void setup() {
Random random = new Random();
int tmp = random.nextInt(100_001);
test1 = tmp;
test2 = tmp;
}
@Benchmark
@BenchmarkMode(Mode.Throughput)
@Measurement(iterations = 20, time = 10)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 5)
public boolean benchmark_with_one_get() {
TestKey testKey = keyList.get(test1);
map.get(testKey);
test1++;
if (test1 >= 100_000) {
test1 = 0;
}
return true;
}
@Benchmark
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Measurement(iterations = 20, time = 10)
@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 5)
public boolean benchmark_with_ten_gets() {
TestKey testKey = keyList.get(test2);
map.get(testKey);
map.get(testKey);
map.get(testKey);
map.get(testKey);
map.get(testKey);
map.get(testKey);
map.get(testKey);
map.get(testKey);
map.get(testKey);
map.get(testKey);
test2++;
if (test2 >= 100_000) {
test2 = 0;
}
return true;
}
public class TestKey {
private int key;
public TestKey(int key) {
this.key = key;
}
public int getKey() {
return key;
}
@Override
public boolean equals(Object obj) {
return obj instanceof TestKey && ((TestKey) obj).key == this.key;
}
@Override
public int hashCode() {
return 31 * 17 + key;
}
}
}
Любые идеи, объяснения приветствуются. спасибо