은/단순히 getAllPresent의 통화 중 가끔 GC 수 있습니다
"요청 시간은 P99와 p99.9 사이에 두 배로". 이를 실제로 조사하려면 GC 활동 (카운터 만)을 추적하는 벤치 마크를 제거해야합니다.
또 다른 문제의 원인은 잠금 경합 일 수 있습니다. 문제 설명에 정확한 액세스 패턴이 누락되었습니다. 얼마나 많은 요청이 병렬로 처리됩니까? 핵심 공간은 어떻게 겹 칩니 까? Guava는 캐시 해시 테이블을 내부적으로 분할하고 concurrencyLevel을 힌트로 사용합니다. LRU 목록을 업데이트해야하므로 읽기 액세스가 완전히 잠기지 않습니다. 다른 스레드에서 동일한 키에 액세스 할 때, 이것은 잠금 경합의 소스입니다. 다음은이 효과를 나타내는 nitro cache performance에 대한 (구형 인) 평가입니다. (업데이트 : 구아바 캐시는 읽기에 잠금을 피하기 위해 몇 가지 전략이있다, 이것은 추가 조사 필요)하는 방법에 대한
빠른
가장 비용이 많이 드는 일을 당신이 캐시 액세스 (15 회?) 퇴거 알고리즘이 데이터 구조를 업데이트하는 것입니다. 그러나 최대 캐시 크기 (2E6)는 최대 경험 크기 (1.2E6)를 초과합니다. 용량 제한이 결코 도달하지 않기 때문에 퇴거가 발생하지 않습니다. 즉, Guava 캐시의 LRU 목록을 모두 업데이트하는 것은 의미가 없습니다. 나는 Google 구아바, EHCache, infinispan 및 다른 축출 전략에 대한 캐시 런타임을 cache2k benchmarks에서 벤치 마크했습니다. "히트에 대한 런타임 비교"를 참조하십시오. 멀티 스레드 액세스에 대한 벤치 마크가 아직 없습니다. 이것은 8 월에 나타납니다.
제 생각에 구아바 캐시에서 퇴거 전략을 변경하거나 전환 할 수있는 옵션이 없습니다 (다른 사람이 할 수 있습니까?).
cache2k에서 잠금 해제 읽기 액세스를 허용하는 대체 퇴거 전략을 실험합니다. 시나리오 내에서 간단히 "random eviction"을 선택할 수 있으며 약 15 배의 속도 향상을 기대할 수 있습니다. 또한 cache2k 캐시는 hashCode() 구현에 대한 해시 테이블 통계 및 품질 메트릭을 출력합니다. cache2k statistics .
빠른 평가가 가능해야합니다. 여기에 몇 가지 코드 조각에서는 사용자가 빠르게 시작할 수 :
<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>
비고 : 캐시 구현은 API 모듈에 노출되지 않습니다, 우리는 컴파일 범위의 핵심 모듈을 필요로하는 이유가 있습니다. 캐시 초기화 :
// 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();
옵션을 변경하여 다른 퇴거 알고리즘을 시험해 볼 수 있습니다. getAllPresent
가 cache2k에서 사용할 수 없습니다, 당신은 스스로를 코딩 할 수 있습니다 : cache2k에서
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;
}
는 cache.peek()
캐시 소스를 호출하지 않고 매핑 된 요소를 반환, 즉 정확히 의도 getAllPresent
의 의미입니다. 해시 맵을 작성하면 실제로 많은 GC로드가 생성됩니다. getAll
또는 getAllPresent
과 같은 대량 작업의 사용은주의 깊게 결정해야합니다. cache2k의 액세스 시간은 해시 테이블 액세스 시간과 비슷하기 때문에 대량 작업으로 인해 작업 속도가 빨라지지는 않을 것입니다.
getAllPresent에 주() 내 cache2k
동일한 목적에 대해 작용하는 JSR107 호환 getAll()
방법이있다. API 설계자 입장에서 볼 때 이러한 방법은 악의적인데, 이는 리소스를 제어하기위한 캐시의 아이디어와 모순되기 때문입니다. 그냥 cache.get() 또는 cache.peek()가 있습니다. CacheSource (aka CacheLoader)가있는 경우 cache.prefetch(keys)
"캐시에 말하기"를 사용하여 다음 키와 함께 작업 할 수 있습니다 .... 미안하지만 약간의 문제가 있습니다.
실제 벤치 마크 코드를 알려 주시면 감사하겠습니다. – dimo414
공유하지 않고 간단하게 stopwatch.start(); after cache.getAllPresent 및 stopwatch.elapsed (TimeUnit.MILLISECONDS) 후에. 이 대기 시간이보고 된 시스템에서 통계를 추적하는 별도의 시스템이 있습니다. 나중에 모든 컴퓨터에서 집계됩니다. –
refreshAfterWrite를 의미합니까? CacheLoader를 사용하고 있습니까? getAllPresent()는 캐시 로더를 호출하지 않습니다. – cruftex