From 18d64138d37de314d491e0bf9287001380f9cb2b Mon Sep 17 00:00:00 2001 From: Ben Steffensmeier Date: Fri, 15 Feb 2013 14:18:02 -0600 Subject: [PATCH] Issue #1614 Cache LatLonReprojection results. Change-Id: If1a8499fbb6a06bad41867078da3e957738f4338 Former-commit-id: e53416937e432fb0b3a25643c44f6cb547a87ef4 [formerly e53416937e432fb0b3a25643c44f6cb547a87ef4 [formerly 2596b3105e377f0fb8713be502a41b4b2f470e93]] Former-commit-id: feb2e98e58197fafb7f9d574f6bff1e3ed52d171 Former-commit-id: 6b53511f9e10249d51d2e74d1a02f46ea11f5707 --- .../common/geospatial/LatLonReprojection.java | 90 +++++++++++++++++-- 1 file changed, 81 insertions(+), 9 deletions(-) diff --git a/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/LatLonReprojection.java b/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/LatLonReprojection.java index 3cddf2bef8..b3f9dee7a4 100644 --- a/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/LatLonReprojection.java +++ b/edexOsgi/com.raytheon.uf.common.geospatial/src/com/raytheon/uf/common/geospatial/LatLonReprojection.java @@ -19,7 +19,14 @@ **/ package com.raytheon.uf.common.geospatial; +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + import org.geotools.coverage.grid.GeneralGridGeometry; +import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.coverage.grid.InvalidGridGeometryException; import org.geotools.referencing.operation.DefaultMathTransformFactory; import org.geotools.referencing.operation.projection.ProjectionException; @@ -43,6 +50,7 @@ import com.raytheon.uf.common.status.UFStatus.Priority; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Jan 15, 2013 mnash Initial creation + * Feb 15, 2013 1614 bsteffen Cache LatLonReprojection results. * * * @@ -55,6 +63,27 @@ public class LatLonReprojection { private static final transient IUFStatusHandler statusHandler = UFStatus .getHandler(LatLonReprojection.class); + // The cache is an LRU map that holds a soft reference to LatLonWrapper. + // Using an LRU map prevents the buildup of empty references and places a + // reasonable upper limit on the number of objects to cache. Using soft + // references prevents running out of memory and allows all the memory to be + // reclaimed if the LatLonReprojection is not used for awhile. + private static Map> cache = new LinkedHashMap>( + 64, 0.8f, true) { + + private static final long serialVersionUID = 1L; + + @Override + protected boolean removeEldestEntry( + Entry> eldest) { + if (size() > 50) { + return true; + } + return false; + } + + }; + /** * Take a {@link GeneralGridGeometry} and reproject it to lat/lon space * @@ -106,17 +135,60 @@ public class LatLonReprojection { * @return */ public static LatLonWrapper getLatLons(GeneralGridGeometry source) { - float[] latlons = reproject(source); - float[] lats = new float[latlons.length / 2]; - float[] lons = new float[latlons.length / 2]; - - for (int i = 0; i < lats.length; i++) { - int index = i * 2; - lons[i] = latlons[index]; - lats[i] = latlons[index + 1]; + // normalize grid geometry to a GridGeometry2D object since a + // GeneralGridGeometry and a GridGeometry2D can produce identical + // results but cannot be interchanged as map keys. + source = GridGeometry2D.wrap(source); + LatLonWrapper wrapper = null; + Reference wrapperRef = null; + synchronized (cache) { + // There are three paths through this sync block. + // 1) The wrapper was in the cache and can be returned. + // 2) The wrapperRef was in the cache but it has no wrapper, the + // wrapperRef can be used as a lock object for synchronizing this + // geometry. + // 3) Nothing is in the cache, an empty ref will be placed in the + // cache to provide a lock object for this geometry. + wrapperRef = cache.get(source); + if (wrapperRef != null) { + wrapper = wrapperRef.get(); + } else { + wrapperRef = new SoftReference(null); + cache.put(source, wrapperRef); + } } + if (wrapper == null) { + // wrapperRef is used to provide fine grained locking on an + // individual geometry. Different geometries can run simultaneously. + synchronized (wrapperRef) { + // If 2 threads manage to block on the same wrapperRef then the + // second thread will be able to pull the wrapper out of the + // cache and avoid calculation. + synchronized (cache) { + wrapperRef = cache.get(source); + } + if (wrapperRef != null) { + wrapper = wrapperRef.get(); + } + if (wrapper == null) { + float[] latlons = reproject(source); + float[] lats = new float[latlons.length / 2]; + float[] lons = new float[latlons.length / 2]; - LatLonWrapper wrapper = new LatLonWrapper(lats, lons); + for (int i = 0; i < lats.length; i++) { + int index = i * 2; + lons[i] = latlons[index]; + lats[i] = latlons[index + 1]; + } + + wrapper = new LatLonWrapper(lats, lons); + synchronized (cache) { + cache.put(source, new SoftReference( + wrapper)); + } + } + } + } return wrapper; } }