public final class TiledApertureProcessorFactory
extends java.lang.Object
Tiler: generator of tiled aperture matrix processors
.
The tiler works with some algorithm, represented by ApertureProcessor
interface
and called onetile processor: it can be any algorithm, which
processes one or several ndimensional matrices
(with identical sets of dimensions)
and returns one or several other matrices as a result (with the same sets of dimensions).
The only requirement is that the value of every element of the resulting matrices depends only on
the elements of the source matrices in a fixed rectangular aperture "around" the same position,
as described in ApertureProcessor
interface.
This class allows to convert the given onetile processor into another aperture processor,
called a tiled processor, created on the base of the original onetile processor
and functionally equivalent to it. (The equivalence can be violated on the bounds
of the matrices, where the tiled processor provides several models of continuations —
see below the section "Continuation model outside the bounds of the large matrices".)
This new processor splits all the matrices into relatively little tiles (rectangular areas,
i.e. submatrices), performs the processing of every tile with the onetile processor and places the results
into the corresponding submatrices of the resulting matrices.
Such conversion of one algorithm to another is called tiling an algorithm
and is performed by tile(ApertureProcessor)
method — the main method of this class.
The goal of tiling some algorithms is optimization of processing very large matrices,
usually located on external storage devices (for example, with help of LargeMemoryModel
).
First, very large matrices are usually tiled
, but many algorithms process matrices
in a simple "streaming" manner, i.e. load elements in the order, corresponding to the order of elements
in the builtin AlgART array
. This order of downloading is inefficient for tiled matrices.
The same algorithms, tiled with help of this class, process large tiled matrices in more efficient order:
they download a rectangular block from all source matrices into newly created
(relatively little) matrices, process them and store the results
into the corresponding submatrices of the destination matrices.
For maximal efficiency, the tiler tries to use SimpleMemoryModel
for storing and processing every
rectangular block (a tile); you can control this with maxTempJavaMemory argument of all
instantiation methods getInstance. In addition, the matrices, allocated by the tiled processor
(if it creates them), are automatically tiled by Matrix.tile(long...)
method (see more details below in
the specification of process method in the tiled aperture processors, stage 4.d).
Second, many algorithms (for example, the basic implementation of
mathematical morphology
from
net.algart.matrices.morphology
package) are multipass, i.e. process source matrices in many passes
though all the matrix. It can be very important for high performance, when all data are located in RAM,
especially in a form of Java memory (via SimpleMemoryModel
), but it can extremely slow down
the calculations, when the source matrices are very large and located on a disk or another storage,
because an algorithm downloads all data from external devices again during each pass.
Unlike this, a matrix processor, generated by the tiler (by tile(ApertureProcessor)
method),
is always singlepass: each tile is downloaded and saved only 1 time, and multipass processing is applied
to relatively little tile matrices, usually allocated in SimpleMemoryModel
.
Third, tiling by this class is the simplest way to optimize an algorithm for multiprocessor or multicore computers, if the algorithm does not provide multithreading optimization itself: several tiles can be processed simultaneously in parallel threads. See below about multithreading.
process
and other methods in the tiled aperture processorsHere is the precise specification of the behaviour of the ApertureProcessor
,
tiled by this tiler, i.e. of the result of tile(ApertureProcessor oneTileProcessor)
.
We call the argument of this method the onetile processor, and the result of this method
the tiled processor.
tile
method.)
ApertureProcessor.process(Map dest, Map src)
method of the tiled processor
does the following.comments to "process" method
,
a corresponding exception is thrown. In particular, this implementation checks,
that all matrices in the dest map are either null
or updatable, i.e. their builtin arrays
implement
UpdatableArray
interface — if at least one nonnull matrix in the dest map
is not updatable, IllegalArgumentException is thrown.number of dimensions
, other than the number of dimensions of this tiler,
returned by its dimCount()
method.process
method does nothing and immediately returns.
If at least one of dimensions of the passed matrices is 0, then
process
method also does nothing and immediately returns (there are no elements to process).
IRectangularArea
), containing all dependence apertures
A_{i} of the onetile processor (returned by its
dependenceAperture(i)
method)
for all indexes i∈Q=src.keySet(), and also containing the origin
of coordinates. While this calculation, IndexOutOfBoundsException will be thrown,
if the number of dimensions for one of results of dependenceAperture(i)
calls is less than dimCount()
, but if some
of them has more than dimCount()
dimensions, the extra dimensions of such aperture
are just ignored (here and in the further algorithm).
tileDim()
) or, maybe, less.
(This stage does not suppose any actual calculations: we consider this stage
for the sake of simplicity.)min(k)
≤ i_{k}
< te_{k}
= t_{k} + A^{m}.max(k)
.
process
method of the tiled processor does the following, for every tile (f, t)
and the corresponding extended tile (fe, te):
SimpleMemoryModel
. But, it the total
amount of memory, necessary simultaneously for all these matrices, is greater than
maxTempJavaMemory()
bytes (this parameter is passed to all instantiation methods
getInstance of the tiler), then the memory model from the current context
is used instead. Note that the total amount of memory depends not only on the number of arguments and
results and the tile dimensions, but also on the desired
number of parallel tasks
.builtin arrays
implement UpdatableArray
interface.
subMatrix
(fe, te, continuationMode)continuationMode()
method.process
method of
the onetile processor is called with the arguments m'_{j}
and m_{i} —
process
(destTile, srcTile)comments to "process" method
. If some matrix elementType()
, and dimensions,
equal to dimensions of other source and resulting matrices M_{i}
and M'_{j}. Each created matrix M'_{j} is saved
back into dest argument:
context()
.getMemoryModel()
. Moreover, every newly created matrix is automatically tiled, i.e. replaced
with newMatrix.tile
(allocationTileDim), where
allocationTileDim is the corresponding argument of the getInstance
instantiation method — with the only exception, when you explicitly specify null
for this argument (in this case the new matrices are not tiled). In most cases,
the tiler is used with very large matrices, and automatic tiling the resulting matrices
improves performance.
subMatrix
(f, t)ApertureProcessor.dependenceAperture(Object srcMatrixKey)
method of the tiled processor
just calls the same method of the onetile processor with the same srcMatrixKey argument
and returns its result.
Note: there is a guarantee, that each resulting matrix M'_{j},
created by process
method of the tiled processor
at the stage 4.d is updatable: its builtin array
is UpdatableArray
and, thus, the matrix can be cast to Matrix
<UpdatableArray>Matrix.cast
(UpdatableArray.class)
Note: process
method of the tiled processor can process several tiles simultaneously in parallel threads
to optimize calculations on multiprocessor or multicore computers.
It depends on the numberOfTasks argument of the instantiation methods getInstance.
If it is 0 (or absent), the desired number of parallel tasks
is detected automatically on the base of the ArrayContext
argument
of the instantiation methods.
Many algorithms (onetile processors) provide multithreading optimization themselves,
so there is no sense to use this feature: in this case you may specify numberOfTasks=1.
The behaviour of the aperture processor, tiled by tile(ApertureProcessor oneTileProcessor)
method,
can little differ from the behaviour of the original onetile processor near the bounds of the matrices,
namely for the resulting elements, for which the dependence aperture
dependenceAperture(i)
In such situation the behaviour of the original onetile processor depends on implementation —
for example, many algorithms suppose socalled pseudocyclic continuation mode, described in comments
to Matrix.ContinuationMode.PSEUDO_CYCLIC
constant.
But the behaviour of the resulting processor,
tiled by tile(ApertureProcessor)
method, is strictly defined always and corresponds to
the continuation mode
,
passed as continuationMode argument to an instantiation method getInstance
of the tiler and returned by continuationMode()
method.
You can see it from the specification of the behaviour of
process
method above,
stage 4.b.
If the onetile processor works according one of continuation models, provided by
Matrix.ContinuationMode
class, you can guarantee the identical behaviour of
the tiled processor by passing the same continuation mode into a tiler instantiation method getInstance;
if no, the tiled processor will be impossible to provide identical results.
Note that Matrix.ContinuationMode.NONE
continuation mode cannot be used in the tiler:
such value of continuationMode argument of instantiation methods getInstance leads
to IllegalArgumentException.
First of all, we note that every tiled processor — a result of tile(ApertureProcessor)
method
— always implements not only ApertureProcessor
, but also
ArrayProcessorWithContextSwitching
interface. So, you can use its
ArrayProcessor.context()
and ArrayProcessorWithContextSwitching.context(ArrayContext)
methods
after corresponding type cast. The current context of the tiled processor
(returned by context()
method) is initially equal to the
current context
of the tiler, and you can change it with help of
context(ArrayContext)
method.
This context (if it is not null) is used for determining memory model,
which should be used for allocating matrices, for showing execution progress
and allowing to stop execution after processing every tile (even if the onetile processor
does not support these features) and for multithreading simultaneous processing several tiles,
if numberOfTasks()
>1. And it will be initially null, if the
current context
of the tiler is null — then it will be ignored.
Many algorithms, which can be tiled by this class, also works with some ArrayContext
to provide abilities to stop calculations, show progress, determine desired memory model for allocating
AlgART arrays, etc. Such algorithms should implement not only ApertureProcessor
interface,
but also ArrayProcessorWithContextSwitching
interface, and should get the current context
via their context()
method. This requirement if not absolute, but if your algorithm retrieves
the context with some other way, then the behaviour of its ArrayContext.updateProgress(ArrayContext.Event)
method can be incorrect — your processor, processing one tile, will not "know" that it is only a part
of the full task (processing all tiles).
If a onetile processor, tiled by tile(ApertureProcessor)
method, really implements
ArrayProcessorWithContextSwitching
, then
process
method of the tiled processor
creates special tile context before processing every tile and
switches
the onetile processor
to this context before calling its
process
method.
In other words, at the stage 4.c the tiled processor calls not
process
(destTile, srcTile)
but
context
(tileContext))).process
(destTile, srcTile).
(By the way, it means that you are able not to think about the initial value of the
current context
in the constructor of your onetile processor:
it will be surely replaced with tileContext before usage of your processor.
For example, you may initialize it by null.)
Of course, it is supposed that the switching method
context
(tileContext)ApertureProcessor
—
if it is not so, it means an invalid implementation of that method, and AssertionError
or ClassCastException can be thrown in this case.
The tileContext here is never null: you can freely use this fact
in your implementation of the onetile processor.
This context is formed automatically as a part
of the current context of the tiled processor, returned by its context()
method — a part, corresponding to processing only one from a lot of tiles.
(As written above, by default the current context of the tiled processor is equal to the
current context
of the tiler.)
Thus, the tiler provides correct behaviour of
context()
.updateProgress(...)
process
method
of your onetile processor.
If the current context of the tiled processor is null,
tileContext is formed from ArrayContext.DEFAULT
.
The tileContext also provides additional information about the position and sizes
of the currently processed tile. Namely, it is created with help of ArrayContext.customDataVersion(Object)
method in such a way, that its customData()
method always returns
a correctly filled instance of TiledApertureProcessorFactory.TileInformation
class, describing the currently processed tile.
If the current number of tasks
, desired for this tiler,
is greater than 1, and the tiled processor uses multithreading for parallel processing several tiles,
then the tileContext is formed in a more complex way.
Namely, in this case it is also a part
of the full context
with correctly filled customData()
(an instance of TiledApertureProcessorFactory.TileInformation
),
and in addition:
ArrayContext.multithreadedVersion(int k, int n)
method is called — so,
the onetile processor can determine, in which of several parallel threads it is called
(the index k) and what is the total number of parallel threads
(the value n≤numberOfTasks()
— it can be less than numberOfTasks()
,
for example, when the total number of tiles is less than it).
This is helpful if the implementation of the onetile processor needs some work memory
or another objects, which should be created before all calculations
and must be separate for different threads;ArrayContext.singleThreadVersion()
method is called — in other words,
the tiler tries to suppress multithreading in the onetile processor, when it uses multithreading
itself for parallel processing several tiles;ArrayContext.noProgressVersion()
method is called — because a progress bar cannot be updated
correctly while parallel processing several tiles (it will be updated after finishing processing
this group of tiles).Every instance of this class can work only with some fixed number n of matrix dimensions,
returned by dimCount()
method and equal to the length of tileDim array,
passed as an argument of the instantiation methods getInstance. It means that
process
method of an aperture processor,
returned by tile(ApertureProcessor)
method, can process only ndimensional matrices
with n=dimCount()
and throws IllegalArgumentException if some of the passed matrices
has another number of dimensions.
The tiler has no restrictions for the types of matrix elements: it can work with any element types, including nonprimitive types. But usually the types of matrix elements are primitive.
Note: in improbable cases, when the dimensions of the source and resulting matrices and/or
the sizes of the dependence apertures
are extremely large (about 2^{63}),
so that the sum of some matrix dimension and the corresponding size of the aperture
(IRectangularArea.width(int)
) or the product of all such sums (i.e. the number of elements
in a source/resulting matrix, extended
by such aperture) is greater than process
method of the
tiled
processor throws IndexOutOfBoundsException and does nothing.
Of course, these are very improbable cases.
To create instances of this class, you should use one of the following methods:
getInstance(ArrayContext, Matrix.ContinuationMode, long, long[])
,getInstance(ArrayContext, Matrix.ContinuationMode, long, long[], long[])
,getInstance(ArrayContext, Matrix.ContinuationMode, long, long[], int)
,getInstance(ArrayContext, Matrix.ContinuationMode, long, long[], long[], int)
.This class is immutable and threadsafe:
there are no ways to modify settings of the created instance.
The same is true for the tiled processors, created by tile(ApertureProcessor)
method.
Modifier and Type  Class and Description 

static class 
TiledApertureProcessorFactory.TileInformation
Additional information about the current processed tile, available for tiled aperture processors
via their context.

Modifier and Type  Method and Description 

ArrayContext 
context()
Returns the current context, used by this tiler.

TiledApertureProcessorFactory 
context(ArrayContext newContext)
Switches the context: returns an instance, identical to this one excepting
that it uses the specified newContext for all operations.

Matrix.ContinuationMode 
continuationMode()
Returns the continuation mode, used by this tiler.

int 
dimCount()
Returns the number of dimensions of this tiler.

static TiledApertureProcessorFactory 
getInstance(ArrayContext context,
Matrix.ContinuationMode continuationMode,
long maxTempJavaMemory,
long[] tileDim)
Creates new instance of the tiler.

static TiledApertureProcessorFactory 
getInstance(ArrayContext context,
Matrix.ContinuationMode continuationMode,
long maxTempJavaMemory,
long[] tileDim,
int numberOfTasks)
Creates new instance of the tiler.

static TiledApertureProcessorFactory 
getInstance(ArrayContext context,
Matrix.ContinuationMode continuationMode,
long maxTempJavaMemory,
long[] tileDim,
long[] allocationTileDim)
Creates new instance of the tiler.

static TiledApertureProcessorFactory 
getInstance(ArrayContext context,
Matrix.ContinuationMode continuationMode,
long maxTempJavaMemory,
long[] tileDim,
long[] allocationTileDim,
int numberOfTasks)
Creates new instance of the tiler.

long 
maxTempJavaMemory()
Returns the maximal amount of Java memory, in bytes, allowed for allocating temporary matrices
for storing a tile.

int 
numberOfTasks()
Returns the number of tiles, which should be processed simultaneously in
parallel threads to optimize calculations on multiprocessor or multicore computers.

<K> ApertureProcessor<K> 
tile(ApertureProcessor<K> oneTileProcessor)
The main method: builds the tiled aperture processor on the base of the given onetile processor.

long[] 
tileDim()
Returns the desired dimensions of every tile.

java.lang.String 
toString()
Returns a brief string description of this object.

public static TiledApertureProcessorFactory getInstance(ArrayContext context, Matrix.ContinuationMode continuationMode, long maxTempJavaMemory, long[] tileDim)
getInstance
(context, continuationMode, maxTempJavaMemory, tileDim, Matrices.defaultTileDimensions
(tileDim.length), 0).context
 see the basic getInstance
method.continuationMode
 see the basic getInstance
method.maxTempJavaMemory
 see the basic getInstance
method.tileDim
 see the basic getInstance
method.java.lang.NullPointerException
 if continuationMode or tileDim argument is null.java.lang.IllegalArgumentException
 if continuationMode==Matrix.ContinuationMode.NONE
,
or if maxTempJavaMemory<0,
or if tileDim.length==0,
or if one of elements of tileDim Java array is zero or negative.public static TiledApertureProcessorFactory getInstance(ArrayContext context, Matrix.ContinuationMode continuationMode, long maxTempJavaMemory, long[] tileDim, long[] allocationTileDim)
getInstance
(context, continuationMode, maxTempJavaMemory, tileDim, allocationTileDim, 0).context
 see the basic getInstance
method.continuationMode
 see the basic getInstance
method.maxTempJavaMemory
 see the basic getInstance
method.tileDim
 see the basic getInstance
method.allocationTileDim
 see the basic getInstance
method.java.lang.NullPointerException
 if continuationMode or tileDim argument is null.java.lang.IllegalArgumentException
 if continuationMode==Matrix.ContinuationMode.NONE
,
or if maxTempJavaMemory<0,
or if tileDim.length==0,
or if allocationTileDim!=null and
allocationTileDim.length!=tileDim.length,
or if one of elements of tileDim or (nonnull)
allocationTileDim Java arrays is zero or negative.public static TiledApertureProcessorFactory getInstance(ArrayContext context, Matrix.ContinuationMode continuationMode, long maxTempJavaMemory, long[] tileDim, int numberOfTasks)
getInstance
(context, continuationMode, maxTempJavaMemory, tileDim, Matrices.defaultTileDimensions
(tileDim.length), numberOfTasks).context
 see the basic getInstance
method.continuationMode
 see the basic getInstance
method.maxTempJavaMemory
 see the basic getInstance
method.tileDim
 see the basic getInstance
method.numberOfTasks
 see the basic getInstance
method.java.lang.NullPointerException
 if continuationMode or tileDim argument is null.java.lang.IllegalArgumentException
 if continuationMode==Matrix.ContinuationMode.NONE
,
or if maxTempJavaMemory<0,
or if tileDim.length==0,
or if numberOfTasks<0,
or if one of elements of tileDim Java array is zero or negative.public static TiledApertureProcessorFactory getInstance(ArrayContext context, Matrix.ContinuationMode continuationMode, long maxTempJavaMemory, long[] tileDim, long[] allocationTileDim, int numberOfTasks)
The passed Java arrays tileDim and allocationTileDim are cloned by this method: no references to them are maintained by the created object.
context
 the context
that will be used by this tiler;
may be null, then it will be ignored, and
the tiled
processor will create all temporary
matrices in SimpleMemoryModel
.continuationMode
 continuation mode, used by the tiled
processor
(see also the specification of the
process
method in the comments to this class
,
stage 4.b).maxTempJavaMemory
 maximal amount of Java memory, in bytes, allowed for allocating by the
process
method
of the tiled
processor
(see ibid., stage 4.a). If you are sure that there is enough Java memory
for allocating all necessary matrices for numberOfTasks()
tiles
of all source and resulting matrices (with the given dimensions tileDim),
you may specify here Long.MAX_VALUE.tileDim
 the desired dimensions of tiles, into which the source and resulting matrices
are split by the tiled
processor
(see ibid., stage 3). Typical values for most applications are 4096x4096
or 2048x2048 (in 2dimensional case).allocationTileDim
 if not null, then the resulting matrices M'_{j},
created by the tiled
processor
(see ibid., stage 4.d), are automatically tiled by the call
newMatrix.tile
(allocationTileDim).
If it is null, the resulting matrices are not tiled.numberOfTasks
 the desired number of tiles, which should be processed simultaneously in
parallel threads to optimize calculations on multiprocessor or multicore computers;
may be 0, then it will be detected automatically as
Arrays.getThreadPoolFactory
(context).recommendedNumberOfTasks()
.
You may specify numberOfTasks=1 for saving memory, if you know that
the onetile processors, which you are going to tile, provide multithreading
optimization themselves.java.lang.NullPointerException
 if continuationMode or tileDim argument is null.java.lang.IllegalArgumentException
 if continuationMode==Matrix.ContinuationMode.NONE
,
or if maxTempJavaMemory<0,
or if tileDim.length==0,
or if allocationTileDim!=null and
allocationTileDim.length!=tileDim.length,
or if numberOfTasks<0,
or if one of elements of tileDim or (nonnull)
allocationTileDim Java arrays is zero or negative.context()
,
continuationMode()
,
maxTempJavaMemory()
,
dimCount()
,
tileDim()
,
numberOfTasks()
public ArrayContext context()
This context (if it is not null) is used by the tiled
processor
for determining memory model, which should be used for allocating resulting matrices and, maybe,
temporary matrices for every tile (if maxTempJavaMemory()
is too small to allocate them
in SimpleMemoryModel
), for showing execution progress and allowing to stop execution after
processing every tile (even if the onetile processor does not support these features)
and for multithreading simultaneous processing several tiles, if numberOfTasks()
>1.
See also the comments to this class
, the section
"Contexts for the onetile processor".
public TiledApertureProcessorFactory context(ArrayContext newContext)
newContext
 another context, used by the returned instance; may be null.public int dimCount()
The tiled processor, created by tile(ApertureProcessor)
method of this tiler,
can process only matrices with this number of dimensions.
public Matrix.ContinuationMode continuationMode()
See comments to the basic
getInstance
method and the comments to this class
for more details.
Matrix.ContinuationMode.NONE
.public long maxTempJavaMemory()
See comments to the basic
getInstance
method and the comments to this class
for more details.
public long[] tileDim()
The returned array is a clone of the internal dimension array stored in this object. The returned array is never empty (its length cannot be zero). The elements of the returned array are never zero or negative.
See comments to the basic
getInstance
method and the comments to this class
for more details.
public int numberOfTasks()
getInstance
method, having such argument, and if this argument was nonzero orArrays.getThreadPoolFactory
(context()
).recommendedNumberOfTasks()
if this instance was created by
getInstance
method without numberOfTasks argument or if this argument was zero
(numberOfTasks=0).public <K> ApertureProcessor<K> tile(ApertureProcessor<K> oneTileProcessor)
comments to this class
for more details.
The result of this method always implements ArrayProcessorWithContextSwitching
interface.
See the comments to this class
, the section
"Contexts for the onetile processor".
oneTileProcessor
 onetile aperture processor.java.lang.NullPointerException
 if the argument is null.public java.lang.String toString()
The result of this method may depend on implementation and usually contains a short description of this tiler.
toString
in class java.lang.Object