[SOLVED] Is it possible to speed up scripts using multi-threading?

Issue

I have to perform CPU intensive analysis on data in DigitalMicrographs’s DM-script language.
I noticed that only a single CPU core maxes out during processing. Is there a way to achieve better performance by multi-threading in DM scripting? Simple examples would be appreciated.

Solution

Multi-threading is certainly possible in DM-scripting, and it is documented in the F1 help here:

F1 help on threading

Whether or not speed improvement can be achieved depends on various things, most importantly whether or not the individual threads need access to the same resource (like the same data, or some GMS resource which is only available via the main-thread – f.e. the UI).

Also, a lot of data-processing is already multi-threaded internally when you use commands on image-expressions. You might achieve a lot more speedup by rephrasing your analytical processing in a way that doesn’t requires for-loops in the scripting language but uses image-expressions instead.

Finally, doing things multi-threaded is a great way of introducing bugs and unexpected behavior which can be really hard to debug. Don’t be frustrated if you run into those things while learning stuff.

That said, the below example demonstrates (at least on my machine) a speed improvement by "chunking" some data-analysis over multiple parallel background threads.

// Example showing the explicit use of multi-threading in DM scripting 

class CMultiThreadtest
{
    image data, keep
    number sx,sy,sz 
    number nChunks, chunksize, lastChunk, doneChunk 
    
    object SetData(object self, image img, number nChunks_) 
    { 
        if ( img.imagegetNumdimensions() != 3 ) throw( "only 3D data for testing please")
        img.ImageGetDimensionSizes(sx,sy,sz)
        nChunks = nChunks_
        if ( sz % nChunks != 0 ) Throw( "Z-size needs to be integer multiple of nChunks for this test.")
        chunksize = sz / nChunks
        
        data:=img
        keep = data
        
        return self
    }
    
    void CompareResult(object self){
        image dif := keep*2-data
        number ok = 0==sum(dif)
        Result("\n\t Result is " + (ok?"correct":"wrong"))
    }
    
    void RunOnData(object self){
        
        // For extra-caution of thread safety, the two lines below shoud be guarded with critical sections
        // but given the near-atomic nature of the call, this is omitted here.
        number chunkIndex = lastChunk
        lastChunk++
        
        image work := data.slice3(0,0,chunkIndex*chunksize, 0,sx,1, 1,sy,1, 2,chunksize,1)
        number startp = GetHighresTickCount()       
        for( number z=0;z<chunksize;z++)
            for( number y=0;y<sy;y++ )
                for( number x=0;x<sx;x++ ){
                        work[x,y,z] *= 2
        }
        number endp = GetHighresTickCount()     
        Result("\n\t\t Process (chunk "+chunkIndex+") done with " + sx*sy*chunksize + " steps in " + (endp-startp)/GetHighResTicksPerSecond())
        
        // For extra-caution of thread safety, the line below shoud be guarded with critical sections
        // but given the near-atomic nature of the call, this is omitted here.
        doneChunk++
    }
    
    void RunWithSubsets(object self, image src, number nChunks_, number inbackground){
        self.SetData(src, nChunks_)
        lastChunk = 0
        doneChunk = 0
        Result("\n.....\n Running with "+nChunks+" chunks of size "+chunksize+ " on " + (inbackground?" multiple background threads":" single main thread") +":")
        number startp = GetHighresTickCount()   
        for( number i=0; i<nChunks; i++){
            if ( inbackground )
                self.StartThread("runondata")
            else
                self.RunOnData()
        }   
        
        while( doneChunk != nChunks ){
            if ( ShiftDown() ){
                Throw("abort")
            doEvents()
            }
        }
        number endp = GetHighresTickCount()     
        Result("\n Total duration:" + (endp-startp)/GetHighResTicksPerSecond())
        self.CompareResult();
        Result("\n.....")
        
    }
};

void Test(){
    image img := RealImage("test cub",4,50,50,10)
    img = random()
    clearresults()
    object tester = Alloc(CMultiThreadtest)
    tester.RunWithSubsets(img, 1, 0)
    tester.RunWithSubsets(img, 1, 1)
    tester.RunWithSubsets(img, 5, 0)
    tester.RunWithSubsets(img, 5, 1)
}
test()

Answered By – BmyGuest

Answer Checked By – Clifford M. (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *