Efficient Imglib/Javacpp converters

Hi all

I am trying to improve my converters from imglib2 structures to JavaCpp FloatPointers. I have to convert to a primitive type array first. Of course this is trivial if your input is an ArrayImg, but I’m interested in the general case of RandomAccessibleIntervals.

Here is what I have right now…

	public static float[] convert(RandomAccessibleInterval<FloatType> rai,
		String testName)
	{
		float[] floats = new float[(int) (rai.dimension(0) * rai.dimension(1) * rai
			.dimension(2))];

		Cursor<FloatType> cursor = Views.iterable(rai).cursor();

		cursor.fwd();

		int i = 0;

		while (cursor.hasNext()) {
			floats[i++] = cursor.get().getRealFloat();
			cursor.fwd();
		}

		return floats;
	}

If I create an array and an out of bounds interval as follows…

		Img<FloatType> arrayTest = ArrayImgs.floats(new long[] { 512, 512, 300 });

		RandomAccessibleInterval<FloatType> outOfBoundsInterval = Views.interval(
			Views.extendZero(arrayTest), Intervals.createMinMax(-10, -10, -10, 490,
				490, 290));

…I find it takes about 10 times as long to convert the out of bounds interval. A couple of questions…

  1. Are there any tricks I can use to improve performance with the out of bounds interval??
  2. Obviously I can thread it to improve performance. @maarzt , @tpietzsch are there any good examples you would recommend that show the latest best practices for threading??

Thanks

Brian

OK… this was pretty easy. The LoopBuilder is really convenient https://github.com/imglib/imglib2/blob/master/src/main/java/net/imglib2/loops/LoopBuilder.java

		LoopBuilder.setImages(outOfBoundsInterval, arrayOut).multiThreaded().forEachPixel((a,b)->b.set(a.get()));
4 Likes

A small remark on your initial comment: You are using

Cursor<FloatType> cursor = Views.iterable(rai).cursor();

to iterate over the image and write the result into an array. This will fail (or rather give surprising result) if your RandomAccessibleInterval is Iterable but does not have flat iteration order. This is the case (AFAIK) for AbstractCellImg, for example: instead of going over the voxels in flat order, the voxels are iterated cell by cell. This is obviously more efficient and therefore desirable unless you need flat iteration order.

TLDR:

  1. If you need flat iteration order, use Views.flatIterable
  2. If you do not care about iteration order (e.g. calculate the sum of an image), use Views.iterable.
2 Likes

Hi @hanslovsky

Thanks for pointing that out. It looks like LoopBuilder has a function to set flat iteration order to true or false.

		LoopBuilder.setImages(rai, temp).multiThreaded().flatIterationOrder(true)
			.forEachPixel((a, b) -> b.setReal(a.getRealFloat()));

Does anyone know how to get and set the number of threads being used?? Does it default to the number of cores?? There are some cases with embedded loops where you’d want to control this. For example tune the number of threads in the outer loop and inner loops for optimal performance (it too many threads get started I’ve seen things slow down).

Brian

1 Like

Hi @bnorthan,

Multi Threaded LoopBuilder:
Yes! LoopBuilder can now execute operations in a multi threaded fashion.

LoopBuilder.setImages(rai, temp).multiThreaded().forEachPixel(
    (a, b) -> b.setReal(a.getRealFloat())
);

When using LoopBuilder you don’t need to worry about the iteration order. It will use an iteration order that fits all the images you iterate over. And you cannot get mismatching iteration orders. This is very different if you use Cursors rather than LoopBuilder. For Cursors you should consider very well if to use Views.flatIterable() or Views.iterable(), as @hanslovsky pointed out before.

Single Threaded, Flat Iteration Order
The flatIterationOrder() and multiThreaded() flags of LoopBuilder should not be used together. Because due to the nature of multi threading no iteration order can be guarantied. But let’s look at this example:

LoopBuilder.setImages(rai).flatIterationOrder().forEachPixel(
     pixel -> System.out.println(pixel)
);

This loop prints the pixels of an image. Here you want the print operation to be executed in a specific order: Start with the top left pixel, go row by row, to the bottom right pixel. This is called flat iteration order in imglib2. If you want to have flat iteration order with LoopBuilder, use the flatIterationOrder() method.
(Note: For a copy operation you don’t need flat iteration order. It’s not important which pixel is copied first or last. And for certain combination’s of images, other iteration orders are faster)

Specifying The Number Of Threads
Currently LoopBuilder is using a ForkJoinPool for multi threading. And by default it uses the default ForkJoinPool, which has as much threads as there are cores. If you want to limit the number of threads you can create a ForkJoinPool with the intended number of threads and execute LoopBuilder inside this ForkJoinPool:

int numberOfThreads = 2;
ForkJoinPool executor = new ForkJoinPool(numberOfThreads);
executor.submit(() -> {
	LoopBuilder.setImages(rai, temp).multiThreaded().forEachPixel(
		(a, b) -> b.setReal(a.getRealFloat())
	);
}).get();

This might change in the future see: https://github.com/imglib/imglib2-algorithm/issues/81

That’s it, Matthias

2 Likes