Android上的Guava Cache性能不佳

时间:2012-02-14 19:35:02

标签: java android guava

我们在Android应用程序中使用加载Google Guava LoadingCache作为位图。在应用程序中,我正在运行一个绘图线程,它将缓存中的位图绘制到Canvas。如果特定位图不在缓存中,则不会绘制它,因此不会加载任何阻塞绘图线程。

但是,这种绘画导致视觉口吃,而每秒帧速率并不是我们想要的。我把它钉在了缓存的getIfPresent()方法中。仅此一项占应用总CPU时间的20%。 getIfPresent() LocalCache$Segment.get()占用了80%以上的时间:

profiling-guava-cache.jpg

请记住,这只是对已经存在的位图的查找。 get()中永远不会发生负载。我想在LRU队列的get()中会有一个簿记开销,它决定了如果段已满就会发生哪些驱逐。但这至少比Key-Lookup LRU-LinkedHashmap.get()给我的慢一个数量级。

如果一个元素在缓存中,我们使用缓存来获得快速查找,如果查找速度很慢,则缓存它没有意义。我还尝试了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仍然很慢。

2 个答案:

答案 0 :(得分:31)

CacheBuilder是为服务器端缓存而设计的,其中并发性是主要关注点。因此,它会牺牲单线程和内存开销来换取更好的多线程行为。 Android开发人员应该使用LruCacheLinkedHashMap或类似的单线程性能和内存是主要问题。将来可能会有concurrencyLevel = 0来表示需要一个轻量级的非并发缓存。

答案 1 :(得分:2)

Android代码并不总是以与JVM相同的方式进行优化。在Java中可以很好地执行什么可能在Android中表现不佳。我建议你写一个非常简单的自己的缓存。例如使用LinkedHashMap.removeEldestEntry(),看看情况如何。