<html><head><meta http-equiv="Content-Type" content="text/html charset=windows-1252"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; ">Hi guys,<div><br></div><div>I made a benchmark for ArrayImg backed by</div><div>- primitive arrays</div><div>- direct buffers</div><div>- sun.misc.Unsafe (off-heap memory)</div><div><a href="https://github.com/imagej/imglib/blob/2e1929873e2f4bafcfe9d90ad616fedb8b7ed4f0/tests/src/test/java/net/imglib2/img/BufferAndUnsafeBenchmark.java">https://github.com/imagej/imglib/blob/2e1929873e2f4bafcfe9d90ad616fedb8b7ed4f0/tests/src/test/java/net/imglib2/img/BufferAndUnsafeBenchmark.java</a></div><div><div>(in branch "buffer-and-unsafe")</div><div><br></div><div>On my computer, the result is that Unsafe is the about the same speed as primitive arrays. Direct buffers are consistently slower:</div><div><div style="margin: 0px; font-size: 11px; font-family: Monaco; "><br></div><div style="margin: 0px; font-size: 11px; font-family: Monaco; ">copy image using class net.imglib2.img.array.ArrayImgFactory</div><div style="margin: 0px; font-size: 11px; font-family: Monaco; ">median: 52 ms</div><div style="margin: 0px; font-size: 11px; font-family: Monaco; min-height: 15px; "><div style="margin: 0px; "><br></div><div style="margin: 0px; ">copy image using class net.imglib2.img.array.ArrayImgUnsafeFactory</div><div style="margin: 0px; ">median: 52 ms</div><div style="margin: 0px; min-height: 15px; "><div style="margin: 0px; "><br></div><div style="margin: 0px; ">copy image using class net.imglib2.img.array.ArrayImgBufferFactory</div><div style="margin: 0px; ">median: 59 ms</div><div style="margin: 0px; min-height: 15px; "><br></div></div></div></div><div>The not-so-good news is that using the same type backed by different accesses will make the calls into the ByteAccess (or whatever the type is) polymorphic, spoiling it for the JIT (at least if both are used from the same call-site).</div><div>This is visible in the second part of the benchmark. Here, a constant UnsignedByteType is added to every pixel of the image. Because the <span style="font-family: Monaco; font-size: 11px; color: rgb(147, 26, 104); ">new</span><span style="font-family: Monaco; font-size: 11px; "> UnsignedByteType(1)</span> is always backed by a primitive array, we hit the bimorphic case for Unsafe and direct buffer, and this results in a big slowdown:</div><div><br></div><div><div style="margin: 0px; font-size: 11px; font-family: Monaco; ">add constant to image using class net.imglib2.img.array.ArrayImgFactory</div><div style="margin: 0px; font-size: 11px; font-family: Monaco; ">median: 22 ms</div></div><div><div style="font-family: Monaco; font-size: 11px; margin: 0px; "><br></div><div style="font-family: Monaco; font-size: 11px; margin: 0px; ">add constant to image using class net.imglib2.img.array.ArrayImgUnsafeFactory</div><div style="font-family: Monaco; font-size: 11px; margin: 0px; ">median: 122 ms</div></div><div><div style="font-family: Monaco; font-size: 11px; margin: 0px; "><br></div><div style="font-family: Monaco; font-size: 11px; margin: 0px; ">add constant to image using class net.imglib2.img.array.ArrayImgBufferFactory</div><div style="font-family: Monaco; font-size: 11px; margin: 0px; ">median: 186 ms</div></div><div><br></div><div><div>We can try some tricks to make UnsignedByteType constants backed by the other ByteAccess types, but in general I see no easy way around this.</div></div><div>Maybe someone has a few months to spare to make imglib3 using ASM or Javassist to manually inline this stuff (that would solve the bottleneck in making a proper bidirectional read/write type hierarchy…) Of course, we can always hope that the JIT will "get it" at some point in the future, but I think this is quite a pathological case.</div><div><br></div><div>I think, we should go for the sun.misc.Unsafe for now.</div><div>If it really turns out to be a show-stopper, we will have to look into using primitive arrays and pinning. One downside of that would be that then the memory must be owned by Java, so it would be a problem for the Avian integration. Another downside is that JNI implementations are not forced to pin the array, they may just decide to copy the data.</div><div><br></div><div>I also made a imglib2-vigra branch that uses Unsafe <a href="https://github.com/tpietzsch/vigra-imglib2/commits/unsafe">https://github.com/tpietzsch/vigra-imglib2/commits/unsafe</a> (requires the buffer-and-unsafe branch of imglib2).</div><div><br></div><div>best regards,</div><div>Tobias</div><div><br></div><div><div>On Nov 30, 2013, at 12:36 AM, Tobias Pietzsch <<a href="mailto:pietzsch@mpi-cbg.de">pietzsch@mpi-cbg.de</a>> wrote:</div><br class="Apple-interchange-newline"><blockquote type="cite"><meta http-equiv="Content-Type" content="text/html charset=iso-8859-1"><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; ">Hi Johannes and Ulli,<div><br></div><div>with respect to the need to set up the paths in the pom.xml:</div><div>This is an issue with the nar-maven-plugin, which compiles and packages the native code.</div><div>With Johannes' help, I submitted an integration test to the nar-maven-plugin project, that illustrates this problem. So hopefully it will be fixed at some point in the future.</div><div>Ulli, I remember that there was another issue on Windows: The dll was named differently to what the <span style="font-family: Monaco; font-size: 11px; ">NarHelper</span> class expects. Could you elaborate on that?</div><div><br></div><div>With respect to what we did on the vigra-imglib2 project. I forked your project, Johannes, <a href="https://github.com/tpietzsch/vigra-imglib2">https://github.com/tpietzsch/vigra-imglib2</a>.</div><div>We worked on the "buffer" branch.</div><div>This is using direct buffers as the underlying data structure for both ImgLib2's ArrayImg and VIGRA's <span style="font-family: Monaco; font-size: 11px; ">MultiArrayView</span>. At the moment, the direct buffer is allocated on the Java side, but as you pointed out, one can use JNI's NewDirectByteBuffer() from the C++ side. I think this is a good solution, because it leaves the option for both scenarios, Java embedded in C++ and C++ embedded in Java. The ByteBuffer is wrapped in a ImgLib2 Access (e.g. <span style="font-family: Monaco; font-size: 11px; ">IntAccess</span> for Imgs of IntType. It is easy to provide two constructors there, one which allocates the buffer and one which takes an existing buffer. Responsibility for freeing the memory is not a problem. Note that JNI NewDirectByteBuffer is constructed with already allocated memory. So, if C++ allocated the memory, Java may garbage-collect the ByteBuffer, but will not free the memory block. If creating the ByteBuffer form Java, Java will also free the memory.</div><div>The good thing is that both ImgLib ArrayImgs and VIGRA MultiArrayViews were designed to wrap flat arrays, which is what is happening here.</div><div><br></div><div>The ByteBuffer code came from the ImgLib2 branch "buffer-and-unsafe", where I played with using direct buffers and sun.misc.Unsafe to back ArrayImgs instead of Java primitive arrays.</div><div>sun.misc.Unsafe is the other viable option. In contrast to primitive arrays and direct buffers it does not suffer from the 2G size limit of Java arrays. Of course, if we put it behind an ArrayImg, we cannot make use of that fact yet. So we could have a BigArrayImg at some point in the future (which would be useful in its own right). Otherwise, same advantages as explained above.</div><div>The benchmark that Johannes mentioned was comparing the speed of ArrayImgs backed by primitive arrays, direct buffers, and sun.misc.Unsafe, respectively. I just had a look and I couldn't find it. I'll recreate it next week.</div><div><br></div><div>Note, that I copied the byte buffer stuff from the ImgLib2 branch "buffer-and-unsafe", so that vigra-imglib2 works with the current ImgLib2 beta.</div><div><br></div><div>But wrapping the images back and forth was only part of what we did. An important point is to make it easy to make bindings to VIGRA functions withou writing lots of boilderplate code on either side of JNI. Based on earlier VIGRA-Matlab wrapper we made some macros that allow to write on the C++ side</div><div><div style="margin: 0px; font-size: 11px; font-family: Monaco; "><div style="margin: 0px; "><br></div><div style="margin: 0px; ">JNIEXPORT void JNICALL Java_net_imglib2_vigra_VigraWrapper_gaussianSmoothMultiArray</div><div style="margin: 0px; ">  (JNIEnv *<span style="text-decoration: underline">env</span>, <span style="text-decoration: underline">jclass</span>, jlongArray shape, <span style="text-decoration: underline">jint</span> typeId, <span style="text-decoration: underline">jobject</span> sourceData, <span style="text-decoration: underline">jobject</span> destData, <span style="text-decoration: underline">jdouble</span> <span style="text-decoration: underline">sigma</span>)</div><div style="margin: 0px; ">{</div><div style="margin: 0px; ">    using <span style="text-decoration: underline">namespace</span> <span style="text-decoration: underline">vigra</span>; // to get UInt8 and Int32</div><div style="margin: 0px; ">    #define F(T) gaussianSmoothMultiArray<T>(<span style="text-decoration: underline">env</span>, shape, typeId, sourceData, destData, <span style="text-decoration: underline">sigma</span>)</div><div style="margin: 0px; ">    ALLOW_TYPES(typeId, UInt8, Int32, float)</div><div style="margin: 0px; ">    #<span style="text-decoration: underline">undef</span> F</div><div style="margin: 0px; ">}</div><div><div style="font-family: Helvetica; font-size: medium; "><br></div><div style="font-family: Helvetica; font-size: medium; ">This creates a switch statement that instantiates the VIGRA template function for the specified C++ types and dispatches to these according to the corresponding ImgLib type.</div><div style="font-family: Helvetica; font-size: medium; ">The same is done for supported dimensionalities (because with templates we have to specify that at compile time).</div><div style="font-family: Helvetica; font-size: medium; "><br></div></div><div style="font-family: Helvetica; font-size: medium; ">The plan is to directly pass in the ArrayImg jobject (and maybe even views later) and extract the type and dimension etc directly from that.</div><div style="font-family: Helvetica; font-size: medium; ">This would mean, that on the Java side, we only have one native method for each exported VIGRA function, basically with the same signature and just replacing MultiArrayView with Img.</div><div style="font-family: Helvetica; font-size: medium; "><br></div><div style="font-family: Helvetica; font-size: medium; ">best regards,</div><div style="font-family: Helvetica; font-size: medium; ">Tobias</div><div style="font-family: Helvetica; font-size: medium; "><br></div></div></div></div>_______________________________________________<br>ImageJ-devel mailing list<br><a href="mailto:ImageJ-devel@imagej.net">ImageJ-devel@imagej.net</a><br>http://imagej.net/mailman/listinfo/imagej-devel<br></blockquote></div><br></div></body></html>