cache - guava java
Mal desempeƱo con Guava Cache en Android (2)
El código de Android no siempre se optimiza de la misma manera que en una JVM. Lo que puede funcionar muy bien en Java podría no funcionar tan bien en Android. Te sugiero que escribas tu propio caché muy simple. por ejemplo, utilizando LinkedHashMap.removeEldestEntry() y vea cómo va.
Usamos un Google Guava LoadingCache para mapas de bits en una aplicación de Android. En la aplicación Estoy ejecutando un subproceso de dibujo, que pinta los mapas de bits en el caché en un lienzo. Si un mapa de bits específico no está en el caché, no se dibuja, por lo que ninguna carga bloqueará el subproceso del dibujo.
Sin embargo, los resultados de la pintura en el tartamudeo visual y los cuadros por segundo no es como nos gustaría. Lo clavé al método getIfPresent()
de la memoria caché. Solo eso toma más del 20% del tiempo total de CPU. En getIfPresent()
LocalCache$Segment.get()
toma más del 80% del tiempo:
Tenga en cuenta que esto es solo una búsqueda de un mapa de bits ya presente. Nunca habrá una carga en get()
. Pensé que habría una sobrecarga de contabilidad en get()
para la cola LRU que decide qué desalojo tiene lugar si el segmento está lleno. Pero esto es al menos un orden de magnitud más lento de lo que me daría una Key-Lookup
en LRU-LinkedHashmap.get()
.
Usamos un caché para obtener búsquedas rápidas si un elemento está en el caché, si la búsqueda es lenta, no hay ningún punto en caché. También probé getAllPresent(a)
y asMap()
pero da el mismo rendimiento.
La versión de la biblioteca es: guava-11.0.1.jar
LoadingCache se define de la siguiente manera:
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);
}
});
Mis preguntas son:
- ¿Lo uso mal?
- ¿Es su implementación insostenible para Android?
- ¿Me perdí una opción de configuración?
- ¿Es este un problema conocido con el caché en el que se está trabajando?
Actualizar:
Después de unos 100 cuadros pintados, los CacheStats son:
I/System.out( 6989): CacheStats{hitCount=11992, missCount=97,
loadSuccessCount=77, loadExceptionCount=0, totalLoadTime=1402984624, evictionCount=0}
Después de eso, missCount se mantiene básicamente igual que los incrementos de hitCount. En este caso, la memoria caché es lo suficientemente grande como para que las cargas ocurran de forma dispersa, pero getIfPresent es lento, sin embargo.
CacheBuilder
fue diseñado para el almacenamiento en caché del lado del servidor, donde la concurrencia era una preocupación principal. Por lo tanto, se intercambia con un solo hilo y la sobrecarga de memoria a cambio de un mejor comportamiento de múltiples hilos. Los desarrolladores de Android deben usar LruCache , LinkedHashMap
o similar, donde el rendimiento y la memoria de un solo subproceso son las principales preocupaciones. En el futuro, puede haber un concurrencyLevel = 0 para indicar que se requiere un caché ligero y no concurrente.