[Interest] [External]Re: How to get QtConcurrent to do what I want?

Michael Jackson mike.jackson at bluequartz.net
Tue Feb 1 16:36:11 CET 2022

I've gotten a bit lost in these requirements but having written a large piece of open source data analysis software (including image processing and tiling of data to form and image) there are certain scenarios when processing an image that you can share the entire image with *all* of the threads because each thread works on its own small part of the image and does NOT care about any other region of the image. This is the "embarrassingly parallel" algorithm you always hear about.

Like Konstantin suggested get the total number of "CPUs" that you want to use. Tell each thread what area it will work on and send the pointer to the image to process to each thread. You can work out the user feedback as the threads go through each section. We do this all the time in our program (also built with Qt5). Just my 2 cents.

Also an alternate suggestion would be to take a look at Threading Building Blocks (TBB). It has worked out very nicely for us and is a compliment to QThread in certain scenarios. We use both QThread and TBB in our application.

Mike Jackson

On 2/1/22, 10:21 AM, "Interest on behalf of Murphy, Sean" <interest-bounces at qt-project.org on behalf of Sean.Murphy at centauricorp.com> wrote:

    > On Mon, Jan 31, 2022 at 7:15 PM Murphy, Sean <mailto:Sean.Murphy at centauricorp.com> wrote:
    > >   1. Creating 60,000 QObjects in a single thread appears to be slow  
    > [...]
    > Ehm, maybe I'm not understanding something, but why do you need objects to begin with?

    I do need to report progress back to the UI and I mistakenly thought I needed to have 
    each tile inherit from QObject to provide that functionality. After reading up a bit more about 
    QFuture and QFutureWatcher and then refactoring yesterday to use those classes, as of the 
    moment, my tile class no longer inherits from anything, and I'm able to use signals from 
    QFutureWatcher to relay progress back to the UI. 

    > The actual loop is this:
    >    // generate each tile with its assignment
    > [snip]
    > What's the significance of the tiles? As far as I can tell from your requirements, you don't care about
    > the "true geometry" of the data.

    Either I'm understanding what you mean by "true geometry", or this assumption is at least partially incorrect. 
    Looking back on my list of requirements I've posted, I left off the last step: 

      At the end of all this processing, I do need to produce an onscreen image to the user. 

    So any way I slice up the work that needs to be done using threads, once they are all finished, I do need to 
    know what chunk of the original image each thread was working on to know where place its normalized pixels 
    in what I display to the user.

    > At least to me it seems you want something like (pseudo algorithm):
    > 1) Start QThread::idealThreadCount threads (QThread::create<> / std::thread)
    > 2) Each thread works on "total samples" / QThread::idealThreadCount buffers that are completely independent.
    > 2.1) Each thread goes through each sample from a partially mapped (from the file) buffer, takes the min/max to get the dynamic range
    > 2.2) Sync the threads to get the global min/max
    > 2.3) Go through each of the buffers a second time to normalize the dynamic range (again no tiles involved, just samples)
    > 3) Done.

    I think this is an preferable approach to what I was attempting, and I'm glad you suggested it. This being my first attempt at this, 
    I naively started from asking "what seems like it would be a reasonable tile size?", arbitrarily thought "256 pixels square" and worked 
    backwards from there, which is how I got into this mindset of "I might have 60,000+ tiles to deal with". Your approach starts from 
    what now appears to me a much better thought of "what's the ideal number of threads for your machine? Don't bother creating 
    more threads than that because you're not going to benefit by having more" and then working forward from there.

    > Note: As each thread works on its own piece of data in both cases there's no sync required at any one point - 
    > you just read/write different parts of the same thing. Which is true both for when you load/write from/to a file 
    > and from/to memory.

    Not sure if I quite understand what you meant by this note? There is the sync you pointed out as your step 2.2, and then since I need to 
    form the results into an onscreen image (most likely a QGraphicsPixmapItem, etc.) there's another sync at your step 3 before I can make
    the final onscreen image. Otherwise I think I understand and prefer your concept to what I was doing.

    Thanks again for your help! Now to test this out in practice... 

    Interest mailing list
    Interest at qt-project.org

More information about the Interest mailing list