Skip to content

Commit 5f637cc

Browse files
committed
add Caches utility to cache RandomAccessibleIntervals
TODO preFetch method is untested
1 parent cc06b42 commit 5f637cc

File tree

1 file changed

+206
-0
lines changed

1 file changed

+206
-0
lines changed
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
package net.imglib2.algorithm.lazy;
2+
3+
import static net.imglib2.img.basictypeaccess.AccessFlags.VOLATILE;
4+
import static net.imglib2.type.PrimitiveType.BYTE;
5+
import static net.imglib2.type.PrimitiveType.DOUBLE;
6+
import static net.imglib2.type.PrimitiveType.FLOAT;
7+
import static net.imglib2.type.PrimitiveType.INT;
8+
import static net.imglib2.type.PrimitiveType.LONG;
9+
import static net.imglib2.type.PrimitiveType.SHORT;
10+
11+
import java.util.ArrayList;
12+
import java.util.Arrays;
13+
import java.util.concurrent.ExecutionException;
14+
import java.util.concurrent.ExecutorService;
15+
import java.util.concurrent.Executors;
16+
import java.util.concurrent.Future;
17+
18+
import net.imglib2.Cursor;
19+
import net.imglib2.Interval;
20+
import net.imglib2.RandomAccess;
21+
import net.imglib2.RandomAccessible;
22+
import net.imglib2.RandomAccessibleInterval;
23+
import net.imglib2.cache.Cache;
24+
import net.imglib2.cache.img.CachedCellImg;
25+
import net.imglib2.cache.img.CellLoader;
26+
import net.imglib2.cache.img.LoadedCellCacheLoader;
27+
import net.imglib2.cache.img.SingleCellArrayImg;
28+
import net.imglib2.cache.ref.SoftRefLoaderCache;
29+
import net.imglib2.img.basictypeaccess.AccessFlags;
30+
import net.imglib2.img.basictypeaccess.ArrayDataAccessFactory;
31+
import net.imglib2.img.cell.Cell;
32+
import net.imglib2.img.cell.CellGrid;
33+
import net.imglib2.type.NativeType;
34+
import net.imglib2.type.numeric.integer.GenericByteType;
35+
import net.imglib2.type.numeric.integer.GenericIntType;
36+
import net.imglib2.type.numeric.integer.GenericLongType;
37+
import net.imglib2.type.numeric.integer.GenericShortType;
38+
import net.imglib2.type.numeric.real.DoubleType;
39+
import net.imglib2.type.numeric.real.FloatType;
40+
import net.imglib2.util.Intervals;
41+
import net.imglib2.util.Util;
42+
import net.imglib2.view.Views;
43+
44+
/**
45+
* A simple method to cache an arbitrary a {@link RandomAccessibleInterval} of
46+
* the typical {@link NativeType} implementations in a memory cell image with
47+
* volatile cells.
48+
*
49+
* TODO These methods should be in imglib2-cache.
50+
*
51+
* @author Stephan Saalfeld
52+
*/
53+
public interface Caches
54+
{
55+
/**
56+
* A simple {@link CellLoader} implementation that fills a pre-allocated
57+
* cell with data from a {@link RandomAccessible} source at the same
58+
* coordinates.
59+
*
60+
* @param <T>
61+
*/
62+
public static class RandomAccessibleLoader< T extends NativeType< T > > implements CellLoader< T >
63+
{
64+
private final RandomAccessible< T > source;
65+
66+
public RandomAccessibleLoader( final RandomAccessible< T > source )
67+
{
68+
super();
69+
this.source = source;
70+
}
71+
72+
@Override
73+
public void load( final SingleCellArrayImg< T, ? > cell )
74+
{
75+
for ( Cursor< T > s = Views.flatIterable( Views.interval( source, cell ) ).cursor(),
76+
t = cell.cursor(); s.hasNext(); )
77+
t.next().set( s.next() );
78+
}
79+
}
80+
81+
/**
82+
* Cache a {@link RandomAccessibleInterval} of the typical
83+
* {@link NativeType} implementations in a memory cell image with volatile
84+
* cells. The result can be used with non-volatile types for processing but
85+
* it can also be wrapped into volatile types for visualization, see
86+
* {@link VolatileViews#wrapAsVolatile(RandomAccessible)}.
87+
*
88+
* This is a very naive method to implement this kind of cache, but it
89+
* serves the purpose for this tutorial. The imglib2-cache library offers
90+
* more control over caches, and you should go and test it out.
91+
*
92+
* @param <T>
93+
* @param source
94+
* @param blockSize
95+
* @return
96+
*/
97+
@SuppressWarnings( { "unchecked", "rawtypes" } )
98+
public static < T extends NativeType< T > > RandomAccessibleInterval< T > cache( final RandomAccessibleInterval< T > source, final int... blockSize )
99+
{
100+
final long[] dimensions = Intervals.dimensionsAsLongArray( source );
101+
final CellGrid grid = new CellGrid( dimensions, blockSize );
102+
103+
final RandomAccessibleLoader< T > loader = new RandomAccessibleLoader< T >( Views.zeroMin( source ) );
104+
105+
final T type = Util.getTypeFromInterval( source );
106+
107+
final CachedCellImg< T, ? > img;
108+
final Cache< Long, Cell< ? > > cache = new SoftRefLoaderCache().withLoader( LoadedCellCacheLoader.get( grid, loader, type, AccessFlags.setOf( VOLATILE ) ) );
109+
110+
if ( GenericByteType.class.isInstance( type ) )
111+
{
112+
img = new CachedCellImg( grid, type, cache, ArrayDataAccessFactory.get( BYTE, AccessFlags.setOf( VOLATILE ) ) );
113+
}
114+
else if ( GenericShortType.class.isInstance( type ) )
115+
{
116+
img = new CachedCellImg( grid, type, cache, ArrayDataAccessFactory.get( SHORT, AccessFlags.setOf( VOLATILE ) ) );
117+
}
118+
else if ( GenericIntType.class.isInstance( type ) )
119+
{
120+
img = new CachedCellImg( grid, type, cache, ArrayDataAccessFactory.get( INT, AccessFlags.setOf( VOLATILE ) ) );
121+
}
122+
else if ( GenericLongType.class.isInstance( type ) )
123+
{
124+
img = new CachedCellImg( grid, type, cache, ArrayDataAccessFactory.get( LONG, AccessFlags.setOf( VOLATILE ) ) );
125+
}
126+
else if ( FloatType.class.isInstance( type ) )
127+
{
128+
img = new CachedCellImg( grid, type, cache, ArrayDataAccessFactory.get( FLOAT, AccessFlags.setOf( VOLATILE ) ) );
129+
}
130+
else if ( DoubleType.class.isInstance( type ) )
131+
{
132+
img = new CachedCellImg( grid, type, cache, ArrayDataAccessFactory.get( DOUBLE, AccessFlags.setOf( VOLATILE ) ) );
133+
}
134+
else
135+
{
136+
img = null;
137+
}
138+
139+
return img;
140+
}
141+
142+
/**
143+
* Trigger pre-fetching of an {@link Interval} in a {@link RandomAccessible}
144+
* by concurrent sampling of values at a sparse grid.
145+
*
146+
* Pre-fetching is only triggered and the set of value sampling
147+
* {@link Future}s is returned such that it can be used to wait for
148+
* completion or ignored (typically, ignoring will be best).
149+
*
150+
* This method is most useful to reduce wasted time waiting for high latency
151+
* data loaders (such as AWS S3 or GoogleCloud). Higher and more random
152+
* latency benefit from higher parallelism, e.g. total parallelism with
153+
* {@link Executors#newCachedThreadPool()}. Medium latency loaders may be
154+
* served better with a limited number of threads, e.g.
155+
* {@link Executors#newFixedThreadPool(int)}. The optimal solution depends
156+
* also on how the rest of the application is parallelized and how much
157+
* caching memory is available.
158+
*
159+
* We do not suggest to use this to fill a {@link CachedCellImg} with a
160+
* generator because now the {@link ExecutorService} will do the complete
161+
* processing work without guarantees that the generated cells will persist.
162+
*
163+
* @param <T>
164+
* @param source
165+
* @param interval
166+
* @param spacing
167+
* @param exec
168+
* @return
169+
* @throws InterruptedException
170+
* @throws ExecutionException
171+
*/
172+
public static < T > ArrayList< Future< T > > preFetch( final RandomAccessible< T > source, final Interval interval, final long[] spacing, final ExecutorService exec ) throws InterruptedException, ExecutionException
173+
{
174+
final int n = interval.numDimensions();
175+
176+
final long[] max = new long[ n ];
177+
Arrays.setAll( max, d -> interval.max( d ) + spacing[ d ] );
178+
179+
final ArrayList< Future< T > > futures = new ArrayList<>();
180+
181+
final long[] offset = Intervals.minAsLongArray( interval );
182+
for ( int d = 0; d < n; )
183+
{
184+
final long[] offsetLocal = offset.clone();
185+
futures.add( exec.submit( () -> {
186+
final RandomAccess< T > access = source.randomAccess( interval );
187+
access.setPosition( offsetLocal );
188+
return access.get();
189+
} ) );
190+
191+
for ( d = 0; d < n; ++d )
192+
{
193+
offset[ d ] += spacing[ d ];
194+
if ( offset[ d ] < max[ d ] )
195+
{
196+
offset[ d ] = Math.min( offset[ d ], interval.max( d ) );
197+
break;
198+
}
199+
else
200+
offset[ d ] = interval.min( d );
201+
}
202+
}
203+
204+
return futures;
205+
}
206+
}

0 commit comments

Comments
 (0)