Author: bpoussin Date: 2013-12-06 18:32:33 +0100 (Fri, 06 Dec 2013) New Revision: 3869 Url: http://forge.codelutin.com/projects/isis-fish/repository/revisions/3869 Log: amelioration du cache guava (calcul la taille en fonction du premier pas de temps) Modified: trunk/src/main/java/fr/ifremer/isisfish/util/IsisCache.java trunk/src/main/java/fr/ifremer/isisfish/util/IsisCacheBackend.java trunk/src/main/java/fr/ifremer/isisfish/util/IsisCacheBackendOnGuava.java trunk/src/main/java/fr/ifremer/isisfish/util/IsisCacheBackendOnReferenceMap.java Modified: trunk/src/main/java/fr/ifremer/isisfish/util/IsisCache.java =================================================================== --- trunk/src/main/java/fr/ifremer/isisfish/util/IsisCache.java 2013-12-05 14:37:17 UTC (rev 3868) +++ trunk/src/main/java/fr/ifremer/isisfish/util/IsisCache.java 2013-12-06 17:32:33 UTC (rev 3869) @@ -229,6 +229,7 @@ percent = 100 * cacheUsed / totalCall; } result.append("Cache usage: ").append(percent).append("%\n"); + result.append(cacheBackend.getStat()); result.append("--------------------\n"); cacheBackend.clear(); Modified: trunk/src/main/java/fr/ifremer/isisfish/util/IsisCacheBackend.java =================================================================== --- trunk/src/main/java/fr/ifremer/isisfish/util/IsisCacheBackend.java 2013-12-05 14:37:17 UTC (rev 3868) +++ trunk/src/main/java/fr/ifremer/isisfish/util/IsisCacheBackend.java 2013-12-06 17:32:33 UTC (rev 3869) @@ -20,6 +20,12 @@ void removeStep(Object key); + /** + * get specific backend cache statistic + * @return backend specific string cache stat representation + */ + String getStat(); + static public interface Factory { IsisCacheBackend createNew(); } Modified: trunk/src/main/java/fr/ifremer/isisfish/util/IsisCacheBackendOnGuava.java =================================================================== --- trunk/src/main/java/fr/ifremer/isisfish/util/IsisCacheBackendOnGuava.java 2013-12-05 14:37:17 UTC (rev 3868) +++ trunk/src/main/java/fr/ifremer/isisfish/util/IsisCacheBackendOnGuava.java 2013-12-06 17:32:33 UTC (rev 3869) @@ -3,6 +3,7 @@ import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheStats; import com.google.common.cache.Weigher; import fr.ifremer.isisfish.types.TimeStep; import java.util.Collection; @@ -12,6 +13,11 @@ import org.nuiton.math.matrix.MatrixND; /** + * Cache qui reserve de la place pour l'equivalent de N pas de temps dans le cache. + * + * Une premiere instanciation du cache est faite. Puis apres le premier pas + * de temps on regarde la taille des objets ayant ete ajoutes au cache pour + * recalculer la nouvelle taille pour permettre de stocker N pas de temps. * * @author poussin * @version $Revision$ @@ -28,6 +34,10 @@ final static public int DOUBLE_SIZE = 8; final static public int CHAR_SIZE = 2; + /** number of time steps to cache by interpolating from the first step*/ + // perhaps allow the configuration + static public int CACHE_STEP = 3; + static public class IsisCacheBackendOnGuavaFactory implements IsisCacheBackend.Factory { @Override public IsisCacheBackend createNew() { @@ -37,49 +47,74 @@ static public Factory factory = new IsisCacheBackendOnGuavaFactory(); + static public class IsisWeigher implements Weigher<String, Object> { + + protected IsisCacheBackendOnGuava backend; + public IsisWeigher(IsisCacheBackendOnGuava backend) { + this.backend = backend; + } + + @Override + public int weigh(String key, Object value) { + int result = key.length() * CHAR_SIZE; + if (value instanceof MatrixND) { + // MatrixND + // on prend le nombre de case de la matrice pour sa taille + // on ne tient pas compte des dimensions qui sont des objets + // utilises ailleurs + int[] dim = ((MatrixND)value).getDim(); + int size = 1; + for (int i=0; i<dim.length; i++) { + size *= dim[i]; + } + result += size * DOUBLE_SIZE; // size is wrong, because depend on matrix type (float or double) + } else if (value instanceof Collection) { + // List<Zone|Pop|Str|Metier|Cell> + // on prend la taille de la collection, car la donnees + // elle meme qu'elle soit dans le backend ou non est + // certainement utilisee ailleur, donc seul la taille + // des pointeurs dans la collection est prise ici + result += ((Collection)value).size() * POINTER_SIZE; + } else if (value instanceof Number) { + // int| double + // seulement une valeur, on ajoute 1 + result += 1 * DOUBLE_SIZE; + } else { + // a priori, il n'y a pas d'autre type de donnee + if (value != null) { + log.info("Cache can't compute value size of " + ClassUtils.getPackageCanonicalName(value, "null")); + } + } + + backend.totalCached += result; + backend.biggestCached = Math.max(backend.biggestCached, result); + + result = Math.max(1024, result); // min to 1ko + return result; + } + } + protected Cache<String, Object> cache; + protected IsisWeigher isisWeigher; + protected long maxMemory; + protected long maxCache; + /** le total en octet mis en backend */ + protected long totalCached = 0; + /** la plus grosse donnee mis en backend */ + protected int biggestCached = 0; + protected boolean ajusted = false; public IsisCacheBackendOnGuava() { + isisWeigher = new IsisWeigher(this); // TODO poussin 20131117 300Mo and 500Mo must be refined - long max = Runtime.getRuntime().maxMemory() - 300*1024*1024; // isis need about 300M - max = Math.max(max, 500*1024*1024); // to do simulation we need 500M minimum + maxMemory = Runtime.getRuntime().maxMemory(); + maxCache = maxMemory - 300*1024*1024; // isis need about 300M + maxCache = Math.max(maxCache, 500*1024*1024); // to do simulation we need 500M minimum + + // create first cache, this cache is juste for one step (see removeStep) cache = CacheBuilder.newBuilder() - .maximumWeight(max) - .weigher(new Weigher<String, Object>(){ - @Override - public int weigh(String key, Object value) { - int result = key.length() * CHAR_SIZE; - if (value instanceof MatrixND) { - // MatrixND - // on prend le nombre de case de la matrice pour sa taille - // on ne tient pas compte des dimensions qui sont des objets - // utilises ailleurs - int[] dim = ((MatrixND)value).getDim(); - int size = 1; - for (int i=0; i<dim.length; i++) { - size *= dim[i]; - } - result += size * DOUBLE_SIZE; // size is wrong, because depend on matrix type (float or double) - } else if (value instanceof Collection) { - // List<Zone|Pop|Str|Metier|Cell> - // on prend la taille de la collection, car la donnees - // elle meme qu'elle soit dans le cache ou non est - // certainement utilisee ailleur, donc seul la taille - // des pointeurs dans la collection est prise ici - result += ((Collection)value).size() * POINTER_SIZE; - } else if (value instanceof Number) { - // int| double - // seulement une valeur, on ajoute 1 - result += 1 * DOUBLE_SIZE; - } else { - // a priori, il n'y a pas d'autre type de donnee - if (value != null) { - log.info("Cache can't compute value size of " + ClassUtils.getPackageCanonicalName(value, "null")); - } - } - return result; - } - }) + .maximumWeight(maxCache) + .weigher(isisWeigher) .build(); } @@ -90,7 +125,22 @@ @Override public void removeStep(Object o) { - // do nothing, can"t search step in cache without change cache behavior + // after first step, use information to resize cache + if (!ajusted) { + Cache<String, Object> oldCache = cache; + // compute new size for cache with data from the first step + // if this new size is less than old maxCache, use it + // to keep more memory for JVM + maxCache = Math.min(maxCache, totalCached * CACHE_STEP); // keep place for 3 step + cache = CacheBuilder.newBuilder() + .maximumWeight(maxCache) + .weigher(isisWeigher) + .initialCapacity((int)oldCache.size() * 3) + .build(); + + log.info(String.format("Cache size ajusted to %sMo (equivalent to %s step need)", + maxCache/1024/1024, CACHE_STEP)); + } } @Override @@ -103,6 +153,23 @@ public void put(TimeStep step, String key, Object value) { cache.put(key, value); } + + @Override + public String getStat() { + CacheStats stat = cache.stats(); + long evictionCount = stat.evictionCount(); + long hitCount = stat.hitCount(); + double hitRate = stat.hitRate(); + long missCount = stat.missCount(); + double missRate = stat.missRate(); + return String.format("%s - \n" + + "\tmaxMemory: %s, cacheMemory: %s, totalCached: %s, biggestCached: %s\n", +// + "\t evictionCount: %s, hitCount: %s, hitRate: %s, missCount: %s, missRate: %s\n", + getClass().getSimpleName(), + maxMemory, maxCache, totalCached, biggestCached, + evictionCount, hitCount, hitRate, missCount, missRate); + } + } Modified: trunk/src/main/java/fr/ifremer/isisfish/util/IsisCacheBackendOnReferenceMap.java =================================================================== --- trunk/src/main/java/fr/ifremer/isisfish/util/IsisCacheBackendOnReferenceMap.java 2013-12-05 14:37:17 UTC (rev 3868) +++ trunk/src/main/java/fr/ifremer/isisfish/util/IsisCacheBackendOnReferenceMap.java 2013-12-06 17:32:33 UTC (rev 3869) @@ -135,7 +135,11 @@ public void removeStep(Object o) { cache.remove(o); } - + + @Override + public String getStat() { + return String.format("%s - no specific stat\n", getClass().getSimpleName()); + } }