我们在 Android 应用程序中为位图使用加载 Google Guava LoadingCache。在应用程序中,我正在运行一个绘图线程,它将缓存中的位图绘制到 Canvas 上。如果特定的位图不在缓存中,它不会被绘制,因此任何加载都不会阻塞绘制线程。
但是,这幅画会导致视觉卡顿,并且每秒的帧数并不是我们想要的。我把它归结为缓存的 getIfPresent()
方法。仅此一项就占用了应用程序总 CPU 时间的 20% 以上。在 getIfPresent()
中,LocalCache$Segment.get()
占据了 80% 以上的时间:
请记住,这只是对已经存在的位图的查找。 get()
中永远不会发生加载。我认为 LRU 队列的 get()
中会有记账开销,该队列决定在段已满时进行哪个驱逐。但这至少比 LRU-LinkedHashmap.get()
中的 Key-Lookup
给我的速度慢一个数量级。
如果一个元素在缓存中,我们使用缓存来快速查找,如果查找速度很慢,缓存它就没有意义了。我也试过 getAllPresent(a)
和 asMap()
但它提供了相同的性能。
库版本为:guava-11.0.1.jar
LoadingCache 定义如下:
LoadingCache<TileKey, Bitmap> tiles = CacheBuilder.newBuilder().maximumSize(100).build(new CacheLoader<TileKey,Bitmap>() {
@Override
public Bitmap load(TileKey tileKey) {
System.out.println("Loading in " + Thread.currentThread().getName() + " "
+ tileKey.x + "-" + tileKey.y);
final File[][] tileFiles = surfaceState.mapFile.getBuilding()
.getFloors().get(tileKey.floorid)
.getBackground(tileKey.zoomid).getTileFiles();
String tilePath = tileFiles[tileKey.y][tileKey.x].getAbsolutePath();
Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
return BitmapFactory.decodeFile(tilePath, options);
}
});
我的问题是:
- 我用错了吗?
- 它的实现不适合 Android 吗?
- 我错过了配置选项吗?
- 这是正在处理的缓存的已知问题吗?
更新:
绘制大约 100 帧后,CacheStats 是:
I/System.out( 6989): CacheStats{hitCount=11992, missCount=97,
loadSuccessCount=77, loadExceptionCount=0, totalLoadTime=1402984624, evictionCount=0}
之后,missCount
与 hitCount
增量基本相同。在这种情况下,缓存足够大,可以稀疏地进行加载,但是 getIfPresent()
仍然很慢。
最佳答案
CacheBuilder
专为服务器端缓存而设计,其中并发性是主要关注点。因此,它牺牲了单线程和内存开销,以换取更好的多线程行为。 Android 开发者应该使用 LruCache 、LinkedHashMap
或类似的,其中单线程性能和内存是主要关注点。将来可能会有 concurrencyLevel=0 表示需要轻量级的非并发缓存。
关于java - Android 上的 Guava Cache 性能不佳,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9283070/