Warning: Can't synchronize with repository "(default)" (/home/git/ome.git does not appear to be a Git repository.). Look in the Trac log for more information.

Ticket #6746: alex.patch

File alex.patch, 16.5 KB (added by jmoore, 12 years ago)

Patch from Alex with multithreaded export (in case github branch is deleted)

  • new file components/blitz/src/ome/services/blitz/impl/BackgroundTiffWriter.java

    From 5354b5bf77f16dc360c70fa4fd58b4643b72ad58 Mon Sep 17 00:00:00 2001
    From: Alex Herbert <a.herbert@sussex.ac.uk>
    Date: Mon, 26 Sep 2011 11:51:10 +0100
    Subject: [PATCH] Added code to perform a multi-threaded Tiff export
    
    Created a new wrapper class for the TiffWriter that operates in a separate thread. The plane bytes are passed using a thread-safe BlockingQueue.
    
    Added code to the ExporterI class to use the background thread worker if the file to be written is above a certain size. Added timings for the methods that open the image plane from OMERO, save the plane using BioFormats and the time that the master thread has to wait for the background writer to finish.
    
    Overall timings for exporting different images are:
    
    MB     Single      Multi       Ratio
    0.5    46.03       44.42       0.97
    1      79.78       79.11       0.99
    2      112.59      106.21      0.94
    4      217.53      210.07      0.97
    8      436.03      421.24      0.97
    16     864.59      840.96      0.97
    32     1721.95     1675.20     0.97
    64     3441.70     3361.51     0.98
    128    6879.39     6727.62     0.98
    256    13650.14    13358.87    0.98
    
    The background writing therefore adds a 2-3% increase in performance.
    
    Given the low level of performance increase I suggested that the patch is not worth pursueing further:
    http://www.openmicroscopy.org/community/viewtopic.php?f=6&t=814&sid=a870884013f0657bd3a079d1ca16e41d#p3107
    ---
     .../services/blitz/impl/BackgroundTiffWriter.java  |  189 ++++++++++++++++++++
     .../src/ome/services/blitz/impl/ExporterI.java     |  102 +++++++++--
     2 files changed, 277 insertions(+), 14 deletions(-)
     create mode 100644 components/blitz/src/ome/services/blitz/impl/BackgroundTiffWriter.java
    
    diff --git a/components/blitz/src/ome/services/blitz/impl/BackgroundTiffWriter.java b/components/blitz/src/ome/services/blitz/impl/BackgroundTiffWriter.java
    new file mode 100644
    index 0000000..5cf5d31
    - +  
     1package ome.services.blitz.impl; 
     2 
     3import java.io.IOException; 
     4import java.util.concurrent.ArrayBlockingQueue; 
     5import java.util.concurrent.BlockingQueue; 
     6 
     7import org.apache.commons.logging.Log; 
     8import org.apache.commons.logging.LogFactory; 
     9 
     10import loci.formats.FormatException; 
     11import loci.formats.out.TiffWriter; 
     12 
     13/** 
     14 * Simple wrapper object for the BioFormats TiffWriter class that allows the  
     15 * {@link loci.formats.out.TiffWriter#saveBytes(int, byte[])} method to be called  
     16 * in a background thread. This allows a producer of image bytes to operate  
     17 * simultaneously with the consumer (BioFormats): 
     18 *  
     19 * <pre> 
     20    TiffWriter writer = new TiffWriter() 
     21     
     22    // TODO: Initialise writer here 
     23 
     24    BackgroundTiffWriter BackgroundTiffWriter = new BackgroundTiffWriter(writer); 
     25    Thread thread = new Thread(BackgroundTiffWriter); 
     26    thread.start(); 
     27     
     28    try { 
     29        for ( &lt;get planes loop&gt; ) { 
     30            byte[] plane = new byte[planeSize]; 
     31             
     32            // TODO: fill byte[] array 
     33 
     34            BackgroundTiffWriter.saveBytes(plane); 
     35        } 
     36        BackgroundTiffWriter.finalise(); 
     37        thread.join(); 
     38    } 
     39    catch (Exception e) { 
     40        BackgroundTiffWriter.shutdown(); 
     41    }  
     42    finally { 
     43        // The original writer is not closed by the BackgroundTiffWriter  
     44        writer.close(); 
     45    } 
     46        </pre> 
     47 *  
     48 * <p> 
     49 * The class uses a BlockingQueue to store successive byte arrays passed to  
     50 * the {@link #saveBytes(byte[])} method. These are sequentially dequeued and  
     51 * passed to the TiffWriter. The size of the blocking queue can be set in the 
     52 * constructor. Consequently the producer of the bytes should not get too far 
     53 * ahead of the consumer TiffWriter class.     
     54 *  
     55 * <p> 
     56 * The BackgroundTiffWriter expects the {@link loci.formats.TiffWriter } class 
     57 * to be correctly initialised to accept bytes to the  
     58 * {@link loci.formats.TiffWriter#saveBytes(int, byte[])} method.  
     59 *  
     60 * <p> 
     61 * Note: The TiffWriter is not closed by this class.  
     62 *  
     63 * @author Alex Herbert, GDSC 
     64 */ 
     65public class BackgroundTiffWriter implements Runnable { 
     66 
     67        private final static Log log = LogFactory.getLog(BackgroundTiffWriter.class); 
     68 
     69        private TiffWriter writer; 
     70        private BlockingQueue<byte[]> queue; 
     71        private volatile boolean closed = false; 
     72         
     73        private final Object lock = new Object(); 
     74         
     75        /** 
     76         * Default constructor 
     77         * @param writer An opened and initialised TiffWriter 
     78         */ 
     79        public BackgroundTiffWriter(TiffWriter writer) { 
     80                this.writer = writer; 
     81                init(5); 
     82        } 
     83         
     84        /** 
     85         * Constructor 
     86         * @param writer An opened and initialised TiffWriter 
     87         * @param queueSize The size of the blocking queue 
     88         */ 
     89        public BackgroundTiffWriter(TiffWriter writer, int queueSize) { 
     90                this.writer = writer; 
     91                init(queueSize); 
     92        } 
     93         
     94        /** 
     95         * Initialise the blocking queue 
     96         * @param queueSize 
     97         */ 
     98        private void init(int queueSize) { 
     99                queue = new ArrayBlockingQueue<byte[]>(queueSize); 
     100        } 
     101 
     102        /* (non-Javadoc) 
     103         * @see java.lang.Runnable#run() 
     104         */ 
     105        @Override 
     106        public void run() { 
     107                Thread.currentThread(); 
     108                try { 
     109                        int no = 0; 
     110                        byte[] bytes; 
     111                        while (!closed) { 
     112                                bytes = queue.take(); 
     113                                if (bytes == null || bytes.length == 0 || closed) 
     114                                        break; 
     115                                synchronized (lock) { 
     116                                        writer.saveBytes(no, bytes); 
     117                                } 
     118                                no++; 
     119                        } 
     120                } 
     121                catch (InterruptedException e) { 
     122                        log.info(e.toString()); 
     123                        throw new RuntimeException(e); 
     124                } catch (FormatException e) { 
     125                        log.error(e.toString()); 
     126                        throw new RuntimeException(e); 
     127                } catch (IOException e) { 
     128                        log.error(e.toString()); 
     129                        throw new RuntimeException(e); 
     130                } finally { 
     131                        closed = true; 
     132 
     133                        // Free remaining memory 
     134                        queue.clear(); 
     135                        queue = null; 
     136                        writer = null; 
     137 
     138                        notifyAll(); 
     139                } 
     140        } 
     141 
     142        /** 
     143         * Adds the bytes to the processing queue. A zero length byte array indicates 
     144         * that no more input is expected. 
     145         * @param bytes The bytes 
     146         * @throws RuntimeException If the writer is closed or the thread is interrupted 
     147         *                          adding to the queue. 
     148         */ 
     149        public void saveBytes(byte[] bytes) throws RuntimeException { 
     150                if (closed) 
     151                        throw new RuntimeException("The writer has been closed"); 
     152                try { 
     153                        queue.put(bytes); 
     154                } catch (InterruptedException iex) { 
     155                        Thread.currentThread().interrupt(); 
     156                        throw new RuntimeException("Unexpected interruption", iex); 
     157                } 
     158        } 
     159         
     160        /** 
     161         * Send a signal to the queue to indicate no more input is expected. 
     162         */ 
     163        public void finalise() { 
     164                saveBytes(new byte[0]); 
     165        } 
     166 
     167        /** 
     168         * Finish the current write process and stop further writing.  
     169         * Use this method to end the processing in the event of error. 
     170         */ 
     171        public void shutdown() { 
     172                if (closed) 
     173                        return; 
     174                 
     175                // Ensure that the current write process is over. This allows the  
     176                // TiffWriter to be closed after shutting down the BackgroundTiffWriter. 
     177                synchronized (lock) { 
     178                        closed = true; 
     179                        log.info("Terminating ..."); 
     180                } 
     181        } 
     182 
     183        /** 
     184         * @return True if no further writes are possible 
     185         */ 
     186        public boolean isClosed() { 
     187                return closed; 
     188        } 
     189} 
  • components/blitz/src/ome/services/blitz/impl/ExporterI.java

    diff --git a/components/blitz/src/ome/services/blitz/impl/ExporterI.java b/components/blitz/src/ome/services/blitz/impl/ExporterI.java
    index 1bdf0e2..db70f8d 100644
    a b public class ExporterI extends AbstractAmdServant implements 
    9898     * @see ticket:6520 
    9999     */ 
    100100    private final static long BIG_TIFF_SIZE = 2L * Integer.MAX_VALUE; 
     101     
     102    /** 
     103     * The size above which a tiff file should be written using a background 
     104     * thread. This is to avoid the thread overhead when small files are 
     105     * exported. 
     106     */ 
     107    private final static long MULTI_THREAD_EXPORT_SIZE = 1024L * 1024;  // 1MB 
    101108 
    102109 
    103110    /** 
    public class ExporterI extends AbstractAmdServant implements 
    357364                            RawPixelsStore raw = null; 
    358365                            OmeroReader reader = null; 
    359366                            TiffWriter writer = null; 
     367                            BackgroundTiffWriter backgroundWriter = null; 
    360368                            try { 
    361369 
    362370                                Image image = retrieve.getImage(0); 
    public class ExporterI extends AbstractAmdServant implements 
    387395                                long dSize = getDataBytes(reader); 
    388396                                final boolean bigtiff = 
    389397                                    ( ( mSize + dSize ) > BIG_TIFF_SIZE ); 
     398                                final boolean multiThread = ( dSize > MULTI_THREAD_EXPORT_SIZE ); 
    390399                                if (bigtiff) { 
    391400                                    writer.setBigTiff(true); 
    392401                                } 
    public class ExporterI extends AbstractAmdServant implements 
    397406                                log.info(String.format( 
    398407                                            "Using big TIFF? %s mSize=%d " + 
    399408                                            "dSize=%d planeCount=%d " + 
    400                                             "planeSize=%d", 
     409                                            "planeSize=%d multiThread=%s", 
    401410                                            bigtiff, mSize, dSize, 
    402                                             planeCount, planeSize)); 
    403                                 byte[] plane = new byte[planeSize]; 
    404                                 for (int i = 0; i < planeCount; i++) { 
    405                                     int[] zct = FormatTools.getZCTCoords( 
    406                                         retrieve.getPixelsDimensionOrder(0).getValue(), 
    407                                         reader.getSizeZ(), reader.getSizeC(), reader.getSizeT(), 
    408                                         planeCount, i); 
    409                                     int readerIndex = reader.getIndex(zct[0], zct[1], zct[2]); 
    410                                     reader.openBytes(readerIndex, plane); 
    411                                     writer.saveBytes(i, plane); 
     411                                            planeCount, planeSize, 
     412                                            multiThread)); 
     413 
     414                                // Perform some debug timing for the export 
     415                                long start = System.nanoTime(); 
     416                                long checkpoint1, checkpoint2; 
     417                                long openTime = 0, saveTime = 0, waitTime = 0; 
     418                                 
     419                                if (multiThread) 
     420                                { 
     421                                    // Save the image using a different thread 
     422                                    backgroundWriter = new BackgroundTiffWriter(writer, 10); 
     423                                    Thread thread = new Thread(backgroundWriter); 
     424                                    thread.start(); 
     425                                     
     426                                    for (int i = 0; i < planeCount; i++) { 
     427                                        int[] zct = FormatTools.getZCTCoords( 
     428                                            retrieve.getPixelsDimensionOrder(0).getValue(), 
     429                                            reader.getSizeZ(), reader.getSizeC(), reader.getSizeT(), 
     430                                            planeCount, i); 
     431                                        int readerIndex = reader.getIndex(zct[0], zct[1], zct[2]); 
     432                                        byte[] plane = new byte[planeSize]; 
     433                                        checkpoint1 = System.nanoTime(); 
     434                                        reader.openBytes(readerIndex, plane); 
     435                                        checkpoint2 = System.nanoTime(); 
     436                                        openTime += checkpoint2 - checkpoint1; 
     437                                        backgroundWriter.saveBytes(plane); 
     438                                        checkpoint1 = System.nanoTime(); 
     439                                        saveTime += checkpoint1 - checkpoint2; 
     440                                    } 
     441                                     
     442                                    // Wait for background writer 
     443                                    checkpoint1 = System.nanoTime(); 
     444                                        backgroundWriter.finalise(); 
     445                                        thread.join(); 
     446                                    waitTime = System.nanoTime() - checkpoint1; 
     447                                } 
     448                                else 
     449                                { 
     450                                    byte[] plane = new byte[planeSize]; 
     451                                     
     452                                    for (int i = 0; i < planeCount; i++) { 
     453                                        int[] zct = FormatTools.getZCTCoords( 
     454                                            retrieve.getPixelsDimensionOrder(0).getValue(), 
     455                                            reader.getSizeZ(), reader.getSizeC(), reader.getSizeT(), 
     456                                            planeCount, i); 
     457                                        int readerIndex = reader.getIndex(zct[0], zct[1], zct[2]); 
     458                                        checkpoint1 = System.nanoTime(); 
     459                                        reader.openBytes(readerIndex, plane); 
     460                                        checkpoint2 = System.nanoTime(); 
     461                                        openTime += checkpoint2 - checkpoint1; 
     462                                        writer.saveBytes(i, plane); 
     463                                        checkpoint1 = System.nanoTime(); 
     464                                        saveTime += checkpoint1 - checkpoint2; 
     465                                    } 
    412466                                } 
     467                                 
     468                                long end = System.nanoTime(); 
     469                                String msg = String.format("Saved image %s in %f ms. open = %f ms. save = %f ms. wait = %f ms\n",  
     470                                                retrieve.getImageName(0),  
     471                                                (end-start) / 1000000.0,  
     472                                                openTime / 1000000.0,  
     473                                                saveTime / 1000000.0, 
     474                                                waitTime / 1000000.0); 
     475                                 
     476                                log.info(msg); 
     477 
    413478                                retrieve = null; 
    414479 
    415480                                try { 
    public class ExporterI extends AbstractAmdServant implements 
    418483                                    // Nulling to prevent another exception 
    419484                                    writer = null; 
    420485                                } 
    421  
     486                                 
    422487                                    __cb.ice_response(file.length()); 
    423488                                } catch (Exception e) { 
    424489                                    omero.InternalException ie = new omero.InternalException( 
    public class ExporterI extends AbstractAmdServant implements 
    427492                                    IceMapper.fillServerError(ie, e); 
    428493                                    __cb.ice_exception(ie); 
    429494                                } finally { 
    430                                     cleanup(raw, reader, writer); 
     495                                    cleanup(raw, reader, writer, backgroundWriter); 
    431496                                } 
    432497 
    433498                            return null; // see calls to __cb above 
    434499                        } 
    435500 
    436501                        private void cleanup(RawPixelsStore raw, 
    437                                 OmeroReader reader, IFormatWriter writer) { 
     502                                OmeroReader reader, IFormatWriter writer, 
     503                                BackgroundTiffWriter backgroundWriter) { 
    438504                            try { 
    439505                                if (raw != null) { 
    440506                                    raw.close(); 
    public class ExporterI extends AbstractAmdServant implements 
    450516                                log.error("Error closing reader", e); 
    451517                            } 
    452518                            try { 
     519                                if (backgroundWriter != null) { 
     520                                        backgroundWriter.shutdown(); 
     521                                        backgroundWriter = null; 
     522                                } 
     523                            } catch (Exception e) { 
     524                                log.error("Error closing background writer", e); 
     525                            } 
     526                            try { 
    453527                                if (writer != null) { 
    454528                                    writer.close(); 
    455529                                } 

1.3.13-PRO © 2008-2011 Agilo Software all rights reserved (this page was served in: 0.61913 sec.)

We're Hiring!