我开始对在 Guava 缓存中按值查找键的方法进行基准测试,我注意到与并发级别相关的奇怪行为。我不确定这是错误还是未定义的行为,甚至可能是预期但未指定。
我的基准测试应该在 Guava Cache 中按值查找键,我知道这不是通常的事情。
这是我完整的基准课程:
@Fork(4)
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Warmup(iterations = 1, time = 100, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 4, time = 100, timeUnit = TimeUnit.MILLISECONDS)
public class ValueByKey {
private Long counter = 0L;
private final int MAX = 2500;
private final LoadingCache<String, Long> stringToLong = CacheBuilder.newBuilder()
.concurrencyLevel(1)
.maximumSize(MAX + 5)
.build(new CacheLoader<String, Long>() {
public Long load(String mString) {
return generateIdByString(mString);
}
});
private final Map<String, Long> mHashMap = new Hashtable<>(MAX);
private final Map<String, Long> concurrentHashMap = new ConcurrentHashMap<>(MAX);
@Setup(Level.Trial)
public void setup() {
// Populate guava cache
for(int i = 0; i <= MAX; i++) {
try {
stringToLong.get(UUID.randomUUID().toString());
} catch (ExecutionException e) {
e.printStackTrace();
System.exit(1);
}
}
}
@Benchmark
public String stringToIdByIteration() {
Long randomNum = ThreadLocalRandom.current().nextLong(1L, MAX);
for(Map.Entry<String, Long> entry : stringToLong.asMap().entrySet()) {
if(Objects.equals(randomNum, entry.getValue())) {
return entry.getKey();
}
}
System.out.println("Returning null as value not found " + randomNum);
return null;
}
@Benchmark
public String stringToIdByIterationHashTable() {
Long randomNum = ThreadLocalRandom.current().nextLong(1L, MAX);
for(Map.Entry<String, Long> entry : mHashMap.entrySet()) {
if(Objects.equals(randomNum, entry.getValue())) {
return entry.getKey();
}
}
System.out.println("Returning null as value not found " + randomNum);
return null;
}
@Benchmark
public String stringToIdByIterationConcurrentHashMap() {
Long randomNum = ThreadLocalRandom.current().nextLong(1L, MAX);
for(Map.Entry<String, Long> entry : concurrentHashMap.entrySet()) {
if(Objects.equals(randomNum, entry.getValue())) {
return entry.getKey();
}
}
System.out.println("concurrentHashMap Returning null as value not found " + randomNum);
return null;
}
private Long generateIdByString(final String mString) {
mHashMap.put(mString, counter++);
concurrentHashMap.put(mString, counter);
return counter;
}
}
我注意到的是,当我更改.concurrencyLevel(1)
为不同于 1 的数字时,我开始丢失数据。以下输出来自并发级别 4:
Iteration 1: Returning null as value not found 107
Returning null as value not found 43
Returning null as value not found 20
Returning null as value not found 77
Returning null as value not found 127
Returning null as value not found 35
Returning null as value not found 83
Returning null as value not found 43
Returning null as value not found 127
Returning null as value not found 107
Returning null as value not found 83
Returning null as value not found 82
Returning null as value not found 40
Returning null as value not found 58
Returning null as value not found 127
Returning null as value not found 114
Returning null as value not found 119
Returning null as value not found 43
Returning null as value not found 114
Returning null as value not found 18
Returning null as value not found 58
66.778 us/op
我注意到在使用HashMap
或HashTable
使用相同的代码时我从未丢失任何数据,它的性能也更好:
Benchmark Mode Cnt Score Error Units
ValueByKey.stringToIdByIteration avgt 16 58.637 ± 15.094 us/op
ValueByKey.stringToIdByIterationConcurrentHashMap avgt 16 16.148 ± 2.046 us/op
ValueByKey.stringToIdByIterationHashTable avgt 16 11.705 ± 1.095 us/op
我的代码是错误的还是 Guava 无法正确处理并发级别高于 1 的分区 HashTable?
- 并发级别选项用于在内部对表进行分区,以便可以在没有争用的情况下进行更新。
- 理想的设置是一次可能访问缓存的最大线程数。