Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/magento/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java LocalCache guava,针对更高吞吐量的优化_Java_Performance_Caching_Guava - Fatal编程技术网

Java LocalCache guava,针对更高吞吐量的优化

Java LocalCache guava,针对更高吞吐量的优化,java,performance,caching,guava,Java,Performance,Caching,Guava,我使用的是guava库中的CacheBuilder和LocalCache,但是getAllPresent的p99.9延迟约为300-400毫秒,存在一些性能问题。 在p99和p99.9之间,请求的延迟几乎翻了一番(p99大约为150毫秒) 使用以下配置: refreshAfterWrite为120秒,maxsize设置为2e6,到期时间为24小时,初始容量为1e6。未使用removeListener,也未在写入后过期。并发级别256(尝试了不同的值)。这台机器有12个磁芯。 在使用缓存时,它有8

我使用的是guava库中的CacheBuilder和LocalCache,但是getAllPresent的p99.9延迟约为300-400毫秒,存在一些性能问题。 在p99和p99.9之间,请求的延迟几乎翻了一番(p99大约为150毫秒)

使用以下配置: refreshAfterWrite为120秒,maxsize设置为2e6,到期时间为24小时,初始容量为1e6。未使用removeListener,也未在写入后过期。并发级别256(尝试了不同的值)。这台机器有12个磁芯。 在使用缓存时,它有8e5到1.2e6个条目。 p99.9上大约3k键和大约100个qps的使用模式是getAllPresent

键是hashCode的一个复杂对象,Objects.hash方法用于提供的所有字段。我尝试了不同的散列函数,以确保分布是一致的(第3个显示了类似的结果)。所以,问题不在于碰撞


有没有关于如何调整它以提高性能的建议?

我想说,在Java中,99%的平铺是90%平铺的两倍,99.9%的平铺是99%平铺的两倍是有效的。如果您看到这种模式,您将需要从整体上降低操作成本,以减少延迟,即不太可能有一些快速的胜利可以帮助您

注意:当您有一个大缓存并在其中进行扫描时,您可以期望每个条目至少涉及一个或两个三级缓存未命中。这会很贵的。对于适合CPU缓存的小型缓存,这将快很多倍


我会使用探查器来减少此操作的CPU和内存分配,或者更改调用缓存的方式以执行所需操作,这也会降低99.9%的分幅。

在不同的请求时间上/“请求时间在p99和p99.9之间翻倍”

这可能只是getAllPresent调用期间偶尔发生的GC。要真正研究这一点,您应该做一个精简的基准测试来跟踪GC活动(只是计数器)

另一个问题可能是锁争用。我在你的问题陈述中遗漏了确切的访问模式。有多少请求是并行完成的?密钥空间如何重叠?Guava在内部对缓存哈希表进行分区,并使用concurrencyLevel作为提示。由于需要更新LRU列表,读取访问不是完全无锁的。对于从不同线程访问同一密钥,这是锁争用的来源。这是一个(过时的)评估来显示这种效果。(更新:guava缓存有一些避免读取时锁定的策略;这需要进一步研究)

关于如何更快(15倍?)的信息


访问缓存时,代价最高的是逐出算法更新其数据结构。但是,最大缓存大小(2E6)高于最大经验大小(1.2E6)。这意味着不会发生驱逐,因为容量限制从未达到。这意味着Guava缓存中所有LRU列表的更新都是毫无意义的。我已经为Google Guava、EHCache、infinispan和不同的逐出策略在“点击率的运行时比较”中对缓存运行时进行了基准测试。多线程访问的基准仍然缺失,这将在8月份出现

据我所知,在番石榴缓存中没有改变或切换驱逐策略的选项(有人能支持吗?)

在cache2k中,我尝试了替代的逐出策略,允许无锁读取访问。在您的场景中,您可以简单地选择“随机驱逐”,我预计速度将提高大约15倍。顺便说一句:cache2k缓存还为您的hashCode()实现打印出哈希表统计信息和质量指标,请参阅上的注释

应该可以进行快速评估。下面是一些快速入门的代码片段:

<dependency>
  <groupId>org.cache2k</groupId>
  <artifactId>cache2k-core</artifactId>
  <version>0.19.1</version>
</dependency>
<dependency>
  <groupId>org.cache2k</groupId>
  <artifactId>cache2k-api</artifactId>
  <version>0.19.1</version>
</dependency>
在cache2k
cache.peek()
中,返回一个映射元素而不调用缓存源,这正是
getAllPresent
的预期语义。构建哈希映射实际上会产生大量GC负载。使用诸如
getAll
getAllPresent
之类的批量操作应该是一个谨慎的决定。由于cache2k中的访问时间类似于哈希表访问时间,因此批量操作可能不会加快速度

关于getAllPresent()的说明


在cache2k中,有一个与JSR107兼容的
getAll()
方法,其作用大致相同。从API设计者的角度来看,这些方法是有害的,因为它与缓存控制资源的思想相矛盾。刚通过cache.get()或cache.peek()获得。如果存在CacheSource(也称为CacheLoader),请使用
cache.prefetch(键)
“对缓存说”下一步要使用这些键。。。。抱歉,有点离题。

想分享你的实际基准代码吗?没什么可分享的,只需放上stopwatch.start();在cache.getAllPresent和stopwatch.appeased之前(TimeUnit.millizes)之后。有单独的系统来跟踪报告延迟的机器的统计数据。后来它从所有机器中聚合。你是说刷新后写吗?你正在使用缓存加载程序吗?getAllPresent()不会调用缓存加载程序。是,refreshAfterWrite。是的,我只使用cacheLoader覆盖重新加载。getAllPresent()可能会触发scheduleRefreshPeter,你能详细说明一下吗:如果你看到这个模式,你将需要从整体上降低操作成本吗?我描述的模式对于高效和非琐碎的代码IMHO来说是正常的,对我来说,这表明不会有一些快速的胜利。为了提高结果,您必须加快典型性能,这将提高所有百分位数。个人资料有助于改善典型或平均潜伏期(但不太擅长发现高百分位数的问题)。@RomanDzhabarov请参阅本演示文稿esp幻灯片8至fir
// optional data source (similar to CacheLoader)
CacheSource<Integer, String> source =
  new CacheSource<Integer, String>() {
    public String get(Integer o) {
      return o + "hello";
    }
  };
Cache<Integer, String> cache =
  CacheBuilder.newCache(Integer.class, String.class)
    .implementation(RandomCache.class)
    .maxSize(3000000)
    .expiryMillis(120 * 1000)
    /* optional, if cache should do the refresh itself
    .source(source)
    .backgroundRefresh(true)
    */
  .build();
public Map<Integer, String> getAllPresent(Iterator<Integer> it) {
  HashMap<Integer, String> hash = new HashMap<>();
  while(it.hasNext()) {
    int k = it.next();
    String v = cache.peek(k);
    if (v != null) {
      hash.put(k, v);
    }
  }
  return hash;
}