1

我正在使用 Chronicle Map 临时存储/查找大量 KV 对(实际上是数十亿)。我不需要持久性或复制,我使用的是内存映射文件,而不是纯粹的堆外内存。平均密钥长度为 8 个字节。

对于较小的数据集 - 最多 2 亿个条目 - 我每秒获得大约 100 万个条目的吞吐量,即创建条目大约需要 200 秒,这是惊人的,但是通过 4 亿个条目,地图已经显着放慢并且需要创建它们需要 1500 秒。

我已经在 Mac OSX/16GB 四核/500GB SSD 和 Proliant G6 服务器上运行了 8 核/64GB ram/300GB Raid 1(不是 SSD)的 Linux 的测试。在两个平台上都表现出相同的行为。

如果有帮助,这是地图设置:

    try {
        f = File.createTempFile(name, ".map");
        catalog = ChronicleMapBuilder
                .of(String.class, Long.class)
                .entries(size)
                .averageKeySize(8)
                .createPersistedTo(f);
    } catch (IOException ioe) {
        // blah
    }

还有一个简单的作家测试:

    long now = -System.currentTimeMillis();
    long count = 400_000_000L;

    for (long i = 0; i < count; i++) {
        catalog.put(Long.toString(i), i);
        if ((i % 1_000_000) == 0) {
            System.out.println(i + ": " + (now + System.currentTimeMillis()));
        }
    }
    System.out.println(count + ": " + (now + System.currentTimeMillis()));
    catalog.close();

所以我的问题是 - 我可以做一些调整来改善这一点,例如更改段数,使用不同的键类型(例如 CharSequence),或者这仅仅是操作系统分页如此大文件的人工制品?

4

1 回答 1

1

有几件事可能会有所帮助:

  • 确保您使用最新可用的 Chronicle Map 版本(目前是3.3.0-beta,下一个3.4.0-beta是几天后)

  • 确实使用无垃圾技术,即使对于这样的测试,这也可能很重要,因为垃圾收集可能会启动:

    • 用作CharSequence键类型和LongValue值类型。
    • 简单的测试代码可能看起来像

      public class VinceTest {
          public static void main(String[] args) throws IOException {
              long count = 400_000_000L;
              File f = File.createTempFile("vince", ".map");
              f.deleteOnExit();
              try (ChronicleMap<CharSequence, LongValue> catalog = ChronicleMap
                      .of(CharSequence.class, LongValue.class)
                      .entries(count)
                      .averageKeySize(8.72)
                      .putReturnsNull(true)
                      .createPersistedTo(f)) {
      
                  long prev = System.currentTimeMillis();
      
                  StringBuilder key = new StringBuilder();
                  LongValue value = Values.newHeapInstance(LongValue.class);
      
                  for (long i = 1; i <= count; i++) {
                      key.setLength(0);
                      key.append(i);
                      value.setValue(i);
                      catalog.put(key, value);
                      if ((i % 1_000_000) == 0) {
                          long now = System.currentTimeMillis();
                          System.out.printf("Average ns to insert per mi #%d: %d\n",
                                  (i / 1_000_000), now - prev);
                          prev = now;
                      }
                  }
                  System.out.println("file size " + MEGABYTES.convert(f.length(), BYTES) + " MB");
              }
          }
      }
      
    • 从上面的来源,请注意使用putReturnsNull(true)以避免意外垃圾创建作为返回值(尽管此测试不是这种情况,因为所有键都是唯一的并且put()总是返回null,但可能是您的生产的情况)

  • 确保您指定了正确的averageKeySize(). 从这个测试来看,平均密钥大小实际上接近 9 个字节(因为大多数密钥大于 100 000 000)。但最好尽可能精确,对于这个计数为 400 000 000 的特定测试,这是 8.72。

于 2016-01-05T02:20:28.520 回答