Skip to content

Commit e324fb1

Browse files
committed
Add BlockSupplier.tile(int...) default method
TilingBlockSupplier wraps a source BlockSupplier, and splits large BlockSupplier. copy requests into several smaller copy calls on the source BlockSupplier. Example use cases: - Computing large outputs (e. g. to write to N5 or wrap as ArrayImg) with operators that have better performance with smaller block sizes. - Avoiding excessively large blocks when chaining downsampling operators.
1 parent 37176aa commit e324fb1

File tree

2 files changed

+271
-2
lines changed

2 files changed

+271
-2
lines changed

src/main/java/net/imglib2/algorithm/blocks/BlockSupplier.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
* %%
1212
* Redistribution and use in source and binary forms, with or without
1313
* modification, are permitted provided that the following conditions are met:
14-
*
14+
*
1515
* 1. Redistributions of source code must retain the above copyright notice,
1616
* this list of conditions and the following disclaimer.
1717
* 2. Redistributions in binary form must reproduce the above copyright notice,
1818
* this list of conditions and the following disclaimer in the documentation
1919
* and/or other materials provided with the distribution.
20-
*
20+
*
2121
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2222
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2323
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -112,6 +112,31 @@ default void copy( Interval interval, Object dest )
112112
*/
113113
BlockSupplier< T > independentCopy();
114114

115+
/**
116+
* Returns a new {@code BlockSupplier} that handles {@link #copy} requests
117+
* by splitting into {@code tileSize} portions that are each handled by this
118+
* {@code BlockSupplier} and assembled into the final result.
119+
* <p>
120+
* Example use cases:
121+
* <ul>
122+
* <li>Compute large outputs (e.g. for writing to N5 or wrapping as {@code
123+
* ArrayImg}) with operators that have better performance with small
124+
* block sizes.</li>
125+
* <li>Avoid excessively large blocks when chaining downsampling
126+
* operators.</li>
127+
* </ul>
128+
*
129+
* @param tileSize
130+
* (maximum) dimensions of a request to the {@code srcSupplier}.
131+
* {@code tileSize} is expanded or truncated to the necessary size. For
132+
* example, if {@code tileSize=={64}} and this {@code BlockSupplier} is 3D,
133+
* then {@code tileSize} is expanded to {@code {64, 64, 64}}.
134+
*/
135+
default BlockSupplier< T > tile( int... tileSize )
136+
{
137+
return new TilingBlockSupplier<>( this, tileSize );
138+
}
139+
115140
/**
116141
* Returns a {@code UnaryBlockOperator} that is equivalent to applying
117142
* {@code this}, and then applying {@code op} to the result.
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
/*-
2+
* #%L
3+
* ImgLib2: a general-purpose, multidimensional image processing library.
4+
* %%
5+
* Copyright (C) 2009 - 2024 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld,
6+
* John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke,
7+
* Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner,
8+
* Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert,
9+
* Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin,
10+
* Jean-Yves Tinevez and Michael Zinsmaier.
11+
* %%
12+
* Redistribution and use in source and binary forms, with or without
13+
* modification, are permitted provided that the following conditions are met:
14+
*
15+
* 1. Redistributions of source code must retain the above copyright notice,
16+
* this list of conditions and the following disclaimer.
17+
* 2. Redistributions in binary form must reproduce the above copyright notice,
18+
* this list of conditions and the following disclaimer in the documentation
19+
* and/or other materials provided with the distribution.
20+
*
21+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
25+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31+
* POSSIBILITY OF SUCH DAMAGE.
32+
* #L%
33+
*/
34+
package net.imglib2.algorithm.blocks;
35+
36+
import java.util.function.Supplier;
37+
38+
import net.imglib2.blocks.SubArrayCopy;
39+
import net.imglib2.blocks.TempArray;
40+
import net.imglib2.type.NativeType;
41+
import net.imglib2.type.PrimitiveType;
42+
import net.imglib2.util.Cast;
43+
import net.imglib2.util.CloseableThreadLocal;
44+
import net.imglib2.util.Intervals;
45+
import net.imglib2.util.Util;
46+
47+
/**
48+
* {@code TilingBlockSupplier} wraps a source {@code BlockSupplier}, and splits
49+
* large {@link BlockSupplier#copy} requests into several smaller {@code copy}
50+
* calls on the source {@code BlockSupplier}.
51+
* <p>
52+
* Each {@code copy} on the source {@code BlockSupplier} requests (at most) an
53+
* interval of the {@code tileSize} specified in the constructor. The source
54+
* {@code BlockSupplier.copy} writes to a temporary buffer, which is then copied
55+
* into the appropriate portion of the {@code dest} buffer.
56+
* <p>
57+
* {@link BlockSupplier#copy} requests that are smaller or equal to {@code
58+
* tileSize} are passed directly to the source {@code BlockSupplier}.
59+
* <p>
60+
* Example use cases:
61+
* <ul>
62+
* <li>Computing large outputs (e.g. to write to N5 or wrap as {@code ArrayImg})
63+
* with operators that have better performance with smaller block sizes.</li>
64+
* <li>Avoiding excessively large blocks when chaining downsampling
65+
* operators.</li>
66+
* </ul>
67+
*
68+
* @param <T>
69+
* pixel type
70+
* @param <P>
71+
* corresponding primitive array type
72+
*/
73+
class TilingBlockSupplier< T extends NativeType< T >, P > implements BlockSupplier< T >
74+
{
75+
private final BlockSupplier< T > p0;
76+
77+
private final int[] innerTileSize;
78+
private final int[] borderTileSize;
79+
private final int[] numTiles;
80+
81+
private final TempArray< P > tempArray;
82+
private final int innerTileNumElements;
83+
84+
private final SubArrayCopy.Typed< P, P > subArrayCopy;
85+
86+
private Supplier< BlockSupplier< T > > threadSafeSupplier;
87+
88+
final int[] tile_pos_in_dest;
89+
final long[] tile_pos_in_src;
90+
final int[] tile_origin;
91+
final int[] tile_size;
92+
93+
/**
94+
* Create a {@code BlockSupplier} that handles {@link BlockSupplier#copy}
95+
* requests by splitting into {@code tileSize} portions that are each
96+
* handled by the given {@code srcSupplier} and assembled into the final
97+
* result.
98+
*
99+
* @param srcSupplier
100+
* source {@code BlockSupplier} to wrap.
101+
* @param tileSize
102+
* (maximum) dimensions of a request to the {@code srcSupplier}.
103+
* {@code tileSize} is expanded or truncated to the necessary size.
104+
* For example, if {@code tileSize=={64}} when wrapping a 3D {@code
105+
* srcSupplier}, {@code tileSize} is expanded to {@code {64, 64,
106+
* 64}}.
107+
*/
108+
public TilingBlockSupplier(
109+
final BlockSupplier< T > srcSupplier,
110+
final int... tileSize )
111+
{
112+
this.p0 = srcSupplier;
113+
final int n = srcSupplier.numDimensions();
114+
innerTileSize = Util.expandArray( tileSize, n );
115+
innerTileNumElements = Util.safeInt( Intervals.numElements( innerTileSize ) );
116+
borderTileSize = new int[ n ];
117+
numTiles = new int[ n ];
118+
119+
final PrimitiveType primitiveType = srcSupplier.getType().getNativeTypeFactory().getPrimitiveType();
120+
tempArray = TempArray.forPrimitiveType( primitiveType );
121+
subArrayCopy = SubArrayCopy.forPrimitiveType( primitiveType );
122+
123+
tile_pos_in_dest = new int[ n ];
124+
tile_pos_in_src = new long[ n ];
125+
tile_origin = new int[ n ];
126+
tile_size = new int[ n ];
127+
}
128+
129+
private TilingBlockSupplier( final TilingBlockSupplier< T, P > s )
130+
{
131+
p0 = s.p0.independentCopy();
132+
innerTileSize = s.innerTileSize;
133+
innerTileNumElements = s.innerTileNumElements;
134+
tempArray = s.tempArray.newInstance();
135+
subArrayCopy = s.subArrayCopy;
136+
137+
final int n = numDimensions();
138+
borderTileSize = new int[ n ];
139+
numTiles = new int[ n ];
140+
tile_pos_in_dest = new int[ n ];
141+
tile_pos_in_src = new long[ n ];
142+
tile_origin = new int[ n ];
143+
tile_size = new int[ n ];
144+
}
145+
146+
@Override
147+
public T getType()
148+
{
149+
return p0.getType();
150+
}
151+
152+
@Override
153+
public int numDimensions()
154+
{
155+
return p0.numDimensions();
156+
}
157+
158+
@Override
159+
public void copy( final long[] srcPos, final Object dest, final int[] size )
160+
{
161+
final int n = numDimensions();
162+
boolean singleTile = true;
163+
for ( int d = 0; d < n; ++d )
164+
{
165+
numTiles[ d ] = ( size[ d ] - 1 ) / innerTileSize[ d ] + 1;
166+
if ( numTiles[ d ] > 1 )
167+
singleTile = false;
168+
borderTileSize[ d ] = size[ d ] - ( numTiles[ d ] - 1 ) * innerTileSize[ d ];
169+
}
170+
if ( singleTile )
171+
{
172+
p0.copy( srcPos, dest, size );
173+
}
174+
else
175+
{
176+
final P tile_buf = tempArray.get( innerTileNumElements );
177+
compute_tiles_recursively( n - 1, srcPos, Cast.unchecked( dest ), size, tile_buf );
178+
}
179+
}
180+
181+
private void compute_tiles_recursively( final int d, final long[] srcPos, final P dest, final int[] dest_size, final P tile_buf ) {
182+
final int numTiles = this.numTiles[ d ];
183+
for ( int i = 0; i < numTiles; ++i )
184+
{
185+
tile_pos_in_dest[ d ] = innerTileSize[ d ] * i;
186+
tile_pos_in_src[ d ] = srcPos[ d ] + tile_pos_in_dest[ d ];
187+
tile_size[ d ] = ( i == numTiles - 1 ) ? borderTileSize[ d ] : innerTileSize[ d ];
188+
if ( d == 0 )
189+
{
190+
p0.copy( tile_pos_in_src, tile_buf, tile_size );
191+
subArrayCopy.copy( tile_buf, tile_size, tile_origin, dest, dest_size, tile_pos_in_dest, tile_size );
192+
}
193+
else
194+
{
195+
compute_tiles_recursively( d - 1, srcPos, dest, dest_size, tile_buf );
196+
}
197+
}
198+
}
199+
200+
@Override
201+
public BlockSupplier< T > independentCopy()
202+
{
203+
return new TilingBlockSupplier<>( this );
204+
}
205+
206+
@Override
207+
public BlockSupplier< T > threadSafe()
208+
{
209+
if ( threadSafeSupplier == null )
210+
threadSafeSupplier = CloseableThreadLocal.withInitial( this::independentCopy )::get;
211+
return new BlockSupplier< T >()
212+
{
213+
@Override
214+
public T getType()
215+
{
216+
return p0.getType();
217+
}
218+
219+
@Override
220+
public int numDimensions()
221+
{
222+
return p0.numDimensions();
223+
}
224+
225+
@Override
226+
public void copy( final long[] srcPos, final Object dest, final int[] size )
227+
{
228+
threadSafeSupplier.get().copy( srcPos, dest, size );
229+
}
230+
231+
@Override
232+
public BlockSupplier< T > independentCopy()
233+
{
234+
return TilingBlockSupplier.this.independentCopy().threadSafe();
235+
}
236+
237+
@Override
238+
public BlockSupplier< T > threadSafe()
239+
{
240+
return this;
241+
}
242+
};
243+
}
244+
}

0 commit comments

Comments
 (0)