Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>As you mentioned, this question can really be thought of as going far beyond images in scratch space. I've actually come across this in many different forms (memory sections, typed arrays, threads, network connections, ...).</p> <p>So what I ended up doing was to write myself a generic "<code>BufferPool</code>". It's a class which manages any form of buffer object, be it a byte-array, some other piece of memory, or (in your case) an allocated image. I loaned this idea from the <code>ThreadPool</code>.</p> <p>It's a fairly simple class that maintains a pool of <code>Buffer</code> objects from which you can <code>acquire</code> a buffer when you need one and <code>release</code> it back to the pool when you're done with it. The <code>acquire</code> function will check whether there is a buffer in the pool available, and if not, will create a fresh one. If there is one in the pool, it will <code>reset</code> the <code>Buffer</code>, i.e. clear it so that behaves identical to a freshly created one.</p> <p>I then have a couple of static instances of this <code>BufferPool</code>, one for each different type of <code>Buffer</code> I use: One for <code>byte</code> arrays, one for <code>char</code> arrays, ... (I'm coding in Java, in case you are wondering... :) I then use these static instances in <strong>all</strong> of the library functions I'm writing. This allows me that, for example, my cryptography functions can share byte-arrays with my binary flattening functions, or any other code in my application. This way, I get maximal reuse of these objects and it has given me a <strong>major</strong> performance increase in many cases.</p> <p>In C++ you might be able to implement this use-everywhere scheme very elegantly by writing a <a href="http://en.cppreference.com/w/cpp/concept/Allocator" rel="nofollow">custom allocator</a> for the data structures you need based on this pooling technique (Thanks to Andrew for pointing this out; see comments).</p> <p>One thing I did for my byte-array buffer is that the <code>acquire</code> function will accept a <code>minimumLength</code> parameter that specifies the minimum size of the buffer I need. It will then only return a byte array of at least this length from the pool, or create a new one, if the pool is empty or only has smaller images in it. You could use the same approach with your image buffer. Let the <code>acquire</code> function accept a <code>minWidth</code> and <code>minHeight</code> parameter and then return an image of at least these dimensions from the pool, or create one with exactly these dimensions. You could then have the <code>reset</code> function only clear the (0, 0) to (<code>minWidth</code>, <code>minHeight</code>) section of the image, if you even need it cleared at all.</p> <p>The one feature that I decided to not worry about in my code, but you might want to consider depending on how long your application will run for and how many different image sizes it will process is whether you want to limit the buffer size in some way and free up cached images to reduce memory use of your application.</p> <p>Just as an example, here is the code I use for my <code>ByteArrayPool</code>:</p> <pre><code>public class ByteArrayPool { private static final Map&lt;Integer, Stack&lt;byte[]&gt;&gt; POOL = new HashMap&lt;Integer, Stack&lt;byte[]&gt;&gt;(); /** * Returns a &lt;code&gt;byte[]&lt;/code&gt; of the given length from the pool after clearing * it with 0's, if one is available. Otherwise returns a fresh &lt;code&gt;byte[]&lt;/code&gt; * of the given length. * * @param length the length of the &lt;code&gt;byte[]&lt;/code&gt; * @return a fresh or zero'd buffer object */ public static byte[] acquire(int length) { Stack&lt;byte[]&gt; stack = POOL.get(length); if (stack==null) { if (CompileFlags.DEBUG) System.out.println("Creating new byte[] pool of lenth "+length); return new byte[length]; } if (stack.empty()) return new byte[length]; byte[] result = stack.pop(); Arrays.fill(result, (byte) 0); return result; } /** * Returns a &lt;code&gt;byte[]&lt;/code&gt; of the given length from the pool after optionally clearing * it with 0's, if one is available. Otherwise returns a fresh &lt;code&gt;byte[]&lt;/code&gt; * of the given length.&lt;br/&gt; * &lt;br/&gt; * If the initialized state of the needed &lt;code&gt;byte[]&lt;/code&gt; is irrelevant, calling this * method with &lt;code&gt;zero&lt;/code&gt; set to &lt;code&gt;false&lt;/code&gt; leads to the best performance. * * @param length the length of the &lt;code&gt;byte[]&lt;/code&gt; * @param zero T - initialize a reused array to 0 * @return a fresh or optionally zero'd buffer object */ public static byte[] acquire(int length, boolean zero) { Stack&lt;byte[]&gt; stack = POOL.get(length); if (stack==null) { if (CompileFlags.DEBUG) System.out.println("Creating new byte[] pool of lenth "+length); return new byte[length]; } if (stack.empty()) return new byte[length]; byte[] result = stack.pop(); if (zero) Arrays.fill(result, (byte) 0); return result; } /** * Returns a &lt;code&gt;byte[]&lt;/code&gt; of the given length from the pool after setting all * of its entries to the given &lt;code&gt;initializationValue&lt;/code&gt;, if one is available. * Otherwise returns a fresh &lt;code&gt;byte[]&lt;/code&gt; of the given length, which is also * initialized to the given &lt;code&gt;initializationValue&lt;/code&gt;.&lt;br/&gt; * &lt;br/&gt; * For performance reasons, do not use this method with &lt;code&gt;initializationValue&lt;/code&gt; * set to &lt;code&gt;0&lt;/code&gt;. Use &lt;code&gt;acquire(&lt;i&gt;length&lt;/i&gt;)&lt;/code&gt; instead. * * @param length the length of the &lt;code&gt;byte[]&lt;/code&gt; * @param initializationValue the * @return a fresh or zero'd buffer object */ public static byte[] acquire(int length, byte initializationValue) { Stack&lt;byte[]&gt; stack = POOL.get(length); if (stack==null) { if (CompileFlags.DEBUG) System.out.println("Creating new byte[] pool of lenth "+length); byte[] result = new byte[length]; Arrays.fill(result, initializationValue); return result; } if (stack.empty()) { byte[] result = new byte[length]; Arrays.fill(result, initializationValue); return result; } byte[] result = stack.pop(); Arrays.fill(result, initializationValue); return result; } /** * Puts the given &lt;code&gt;byte[]&lt;/code&gt; back into the &lt;code&gt;ByteArrayPool&lt;/code&gt; * for future reuse. * * @param buffer the &lt;code&gt;byte[]&lt;/code&gt; to return to the pool */ public static byte[] release(byte[] buffer) { Stack&lt;byte[]&gt; stack = POOL.get(buffer.length); if (stack==null) { stack = new Stack&lt;byte[]&gt;(); POOL.put(buffer.length, stack); } stack.push(buffer); return buffer; } } </code></pre> <p>And then, in the rest of <strong>all</strong> my code where I need <code>byte[]</code>'s, I use something like:</p> <pre><code>byte[] buffer = ByteArrayPool.acquire(65536, false); try { // Do something requiring a byte[] of length 65536 or longer } finally { ByteArrayPool.release(buffer); } </code></pre> <p>Note, how I added 3 different <code>acquire</code> functions that allow me to specify how "clean" I need the buffer to be that I am requesting. If I'm overwriting all of it anyways, for example, there is no need to waste time on zeroing it first.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload