2017-05-14 131 views
0

我看到迭代在Chronicle Map上的時間非常緩慢 - 在下面的示例中,我的2013 MacbookPro上的1M條目每次迭代93ms。我想知道是否有更好的方法來迭代,或者我做錯了什麼,或者如果這是預期的?我知道Chronicle Map並未針對迭代進行優化,但幾年前的this ticket讓我期待更快的迭代時間。玩具下面的例子: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次迭代

+0

如果你需要比你需要有另外的數據結構來索引數據爲O(n)操作更好。大型地圖的蠻力迭代總是要測試你的硬件。 –

+0

在你提到的票據中,它顯示的條目是指容量不是使用的大小,對於大部分爲空的地圖,它可以加快速度。 –

回答

1

您的結果:每1個百萬個密鑰93毫秒正好基準的結果這裏匹配:http://jetbrains.github.io/xodus/#benchmarks,所以它在預期的球場。 93毫秒/ 1米按鍵每個按鍵93納秒,與「什麼」相比,「非常慢」?您的地圖包含16 MB有效負載,總堆外大小約爲30 MB(僅供參考,您可以通過​​查看),這比消費型筆記本電腦的L3內存容量大得多,因此迭代速度受延遲的主要記憶。 Chronicle Map的迭代主要不是順序的,所以內存預取不起作用。 I've created an issue about this.

而且你的代碼的幾個注意事項:

  • 在你的情況下,地圖的價值大小是固定的,所以你應該使用constantValueSizeBySample(ByteBuffer.allocate(12)),而不是averageValueSize()。即使地圖值大小不恆定,也最好使用averageValue()而不是averageValueSize(),因爲您無法確定序列化器有多少字節用於這些值。
  • 對於帶有兩個字段的value interfaces,您的價值似乎是一個很好的用例。此外,您已經使用值接口作爲密鑰類型 - IntValue
  • 做基準測試使用JMH
+0

雖然迭代可能會加快,特別是對於大部分爲空的地圖,人們總是應該期望在每個條目上的蠻力迭代最多也是昂貴的O(n)操作。 –

+0

感謝您的回覆!我的意思是說,與3毫秒輸入的1.5us相比,速度更慢;鏈接的代碼似乎使用3米條目的地圖而不是3米的容量,所以我很驚訝這些數字太遙遠了。我錯誤地閱讀了自述文件中的'使用上下文中的條目'部分 - 我期望能夠直接讀取 - 堆內存,而不是複製整個值,但似乎只適用於值接口。如果我切換到使用值,雖然速度仍然與值的大小成正比,即使它只是在測試v = data.get()但不訪問任何字段。 – jlw

+0

單步執行代碼我看到它調用((可複製)using).copyFrom(nativeReference);如果我正確讀取Generators.java中的copyFromMethod,它實際上會複製整個值,並且當我用jmc查看它時,通過使用initCachedEntryValue通過ValueReader.read可以看到45%的時間轉到Heap.copyFrom 。如果這是正確的,你會考慮添加還是已經有迭代的方法,而不需要將值複製到堆中?或者請讓我知道,如果我完全脫離了這一切的基礎;編年史地圖非常新,非常感謝幫助! – jlw