0

我看到在编年史地图上迭代的时间非常缓慢——在下面的示例中,在我的 2013 MacbookPro 上,每次迭代超过 100 万个条目需要 93 毫秒。我想知道是否有更好的迭代方法或我做错了什么,或者这是预期的吗?我知道 Chronicle Map 没有针对迭代进行优化,但是几年前的这张票让我期待更快的迭代时间。下面的玩具示例:

    public static void main(String[] args) throws Exception {
    int numEntries = 1_000_000;
    int numIterations = 1_000;
    int avgEntrySize = BitUtil.SIZE_OF_LONG + BitUtil.SIZE_OF_INT;
    ChronicleMap<IntValue, ByteBuffer> map = ChronicleMap.of(IntValue.class, ByteBuffer.class)
            .name("test").entries(numEntries).averageValueSize(avgEntrySize)
            .putReturnsNull(true).create();
    IntValue value = Values.newHeapInstance(IntValue.class);
    ByteBuffer buffer = ByteBuffer.allocate(avgEntrySize);
    for (int i = 0; i < numEntries; i++) {
        value.setValue(i);
        buffer.clear();
        buffer.putLong(i);
        buffer.putInt(i);
        buffer.flip();
        map.put(value, buffer);
    }
    System.out.println("Finished insertion");

    for (int i = 0; i < numIterations; i++) {
        map.forEachEntry(entry -> {
            Data<ByteBuffer> data = entry.value();
            ByteBuffer val = data.get();
        });
    }
    System.out.println("Finished priming");
    long start = System.currentTimeMillis();
    for (int i = 0; i < numIterations; i++) {
        map.forEachEntry(entry -> {
            Data<ByteBuffer> data = entry.value();
            ByteBuffer val = data.get();
        });
    }
    System.out.println(
            "Elapsed: " + (System.currentTimeMillis() - start) + " for " + numIterations
                    + " iterations");

}

输出:完成插入完成启动经过:93327 1000 次迭代

4

1 回答 1

1

您的结果:每 100 万个键 93 毫秒与此处的基准测试结果完全匹配:http: //jetbrains.github.io/xodus/#benchmarks,所以它在预期的范围内。93 ms / 1m 键是每个键 93 ns,与什么相比它“非常慢”?您的地图包含 16 MB 的有效负载,它的总堆外大小约为 30 MB(仅供参考,您可以通过 来检查map.offHeapMemoryUsed()),这比消费笔记本电脑中的 L3 内存量大得多,因此迭代速度受延迟的限制主内存。Chronicle Map 的迭代主要不是顺序的,所以内存预取不起作用。我已经为此创建了一个问题。

还有一些关于您的代码的注释:

  • 在您的情况下,地图的值大小是恒定的,因此您应该使用constantValueSizeBySample(ByteBuffer.allocate(12))而不是averageValueSize(). 即使映射值大小不是恒定的,也最好使用averageValue()而不是averageValueSize(),因为您无法确定序列化程序为这些值使用了多少字节。
  • 对于具有两个字段的值接口,您的值似乎是一个很好的用例。此外,您已经使用值接口作为键类型 - IntValue
  • 使用JMH进行基准测试
于 2017-05-15T04:59:01.810 回答