You are not logged in.

Dear visitor, welcome to QtForum.org. If this is your first visit here, please read the Help. It explains in detail how this page works. To use all features of this page, you should consider registering. Please use the registration form, to register here or read more information about the registration process. If you are already registered, please login here.

1

Monday, April 9th 2012, 12:16pm

Concurrent processing/multi-threading

Hey everyone,

This is my first post on the forums and I'm interested in finding out about Qt's threading features. The project I'm working on involves data acquisition and visualisation.

My code currently looks like this:

Source code

1
2
3
4
5
while(isAcquiring){
  getData();
  saveDataToFile();
  visualiseData();
}


I need to acquire data from external hardware, which is done with no problem. The issue is the visualisation function, because it takes ~10ms to display the data. When above 15ms, this approach works fine, but when data acquisition is done at 2-3ms, then visualisation becomes problematic. I've thought of a way but I don't know how to implement it. If I run visualiseData(); in a different thread, then I could theoretically separate the GUI/acquisition from the visualisation, right? I've had a look at QThread but didn't succeed to make the threads parallel.

Or if that wouldn't work, then how would I be able to visualise data from 2ms acquisitions if it takes 10ms to process? Some sort of concurrent processing perhaps.

Thank you.


Mr_Cloud

2

Monday, April 9th 2012, 3:06pm

If it takes 10ms to process data for visualisation, then there is no point in sending so much data for visualisation (if we're doing real time visualisation).

Of course, the graphics should not be tightly coupled to the data anyway...

You cant do anything concurrent in visualiseData unless you can split it up into processData1() processData2() processData3(), ..., visualiseProcessedData().

Tell me more about what you are actually visualising - just real time updates, or can user scroll back through all the different times? What resolution is the user working with? Can the user interact with visualisation whilst you are acquiring?

You need to answer all of these questions and then you can see some more efficient ways to go. But already there are design improvements to be made - you should consider how often you are saving data to disk, and why gui is so tightly coupled to acquisition.
If you have a problem, CUT and PASTE your code. Do not retype or simplify it. Give a COMPLETE and COMPILABLE example of your problem. Otherwise we are all guessing the problem from a fabrication where relevant details are often missing.

3

Tuesday, April 10th 2012, 2:10am

Amleto, I am visualising real time updates. There is an unsigned char[] data stream coming from the hardware, usually 2048 bytes long. Around 260 of those bytes are relevant to the visualisation. The visualisation itself is a 2D colormap of the data which the user can interact with in a simple manner, only changing the scale to alter the min-max color ranges. This is done with a horizontal slider. There are also a couple of QwtPlots which visualise the same data, but in a conventional XY plot style.

The while loop specifically looks like this:

Source code

1
2
3
4
5
6
7
while(isAcquiring){
  if (newDataPresent==1){
    readFromHardware(buf,2048);//where 2048 is the size of buf array
    file.write((char *) buf,2048);
    processAndDisplay(buf);
  }
}


The newDataPresent boolean is set to high when new data is available from the hardware. This is acquisition-specific, usually ranging from 2ms, 3, 5, 10 up to 100ms. As specified before, anything under 10ms and the visualisation fails to display the correct data.

Of course, the duration of processAndDisplay(buf); depends on the specs of the machine running the software. If I have to run it on a different machine, the duration of the visualisation may vary to be less or more than ~10ms. This is why I'd like to keep the function in a worker thread so that it may display the data in parallel whenever it finishes processing. Or perhaps there may be a way to stack up a number of bufs in memory somehow and process them all at a time (the data manipulation processing itself is very fast, in the order of microseconds) and then just visualise the data once every 20ms. Thoughts?

Thank you.


Mr_Cloud

4

Tuesday, April 10th 2012, 8:32am

yes, this is what I was getting at - just because you acquire data at X ms doesn't mean you need to display every 'frame'.

I think I should point out that data processing and saving to disk should be in the 'sub' threads. Visualisation will be the main thread otherwise the gui will not be responsive since widgets must be in the main thread.

You need to consider what resolution the user needs to see while acquiring, and if it is any different to playback.

You might like to consider implementing something like an observer pattern so that the gui only updates when you want it to via notifications that are not tightly coupled to acquisition.
If you have a problem, CUT and PASTE your code. Do not retype or simplify it. Give a COMPLETE and COMPILABLE example of your problem. Otherwise we are all guessing the problem from a fabrication where relevant details are often missing.

5

Tuesday, April 10th 2012, 10:17am

The data acquisition is the most important, Amleto. I think it's best if it stays in the main thread. It needs to be done in real-time, whereas the visualisation is secondary. The resolution of the visualisation is non-important, as the graphs and 2D maps don't change sizes whilst the acquisition is running. In any case I'm using QApplication::processEvents(); within my while loop to keep the UI responsive.

If I simply visualise the data every 20ms or so, I'm introducing a dead time in the system and this would cause data loss. To prevent this I'm looking at trying to use QtConcurrent::run(); to visualise the data, but I'm not having much luck with passing parameters for the member function. If you could point me in the right direction with this, I'd greatly appreciate it.

The function I'm trying to run in a separate thread using QtConcurrent() is:

Source code

1
2
3
4
5
6
7
8
void FPGA::Tvisualise(){
    TrealPlot(DoubleSamplingProcR(useDoubleSampling,TrealtimeData));//QwtPlot
    TcumulPlot(DoubleSamplingProcC(useDoubleSampling,TcumulativeSum));//QwtPlot
    double int_time=sbTset_delay->value();
    AnglePlot->setGraphData(TinclinPlotData,TinclinVector.size(),1,int_time/1000,1,1,0);//QwtPlot angle
    delete[] TinclinPlotData;//free memory after data's been sent to plot
    hsPixmapSliderRChanged();//2D visualisation update
}


The documentation at http://qt-project.org/doc/qt-4.8/qtconcurrentrun.html shows an example:

Source code

1
2
3
4
5
6
 // call 'void QImage::invertPixels(InvertMode mode)' in a separate thread
 QImage image = ...;
 QFuture<void> future = QtConcurrent::run(&image, &QImage::invertPixels, QImage::InvertRgba);
 ...
 future.waitForFinished();
 // At this point, the pixels in 'image' have been inverted

But I don't know how to apply it so that I can do QtConcurrent::run(FPGA::Tvisualise(),/*...*/);. Any ideas?

Thank you.


Mr_Cloud

This post has been edited 1 times, last edit by "Mr_Cloud" (Apr 10th 2012, 10:34am)


6

Tuesday, April 10th 2012, 12:52pm

honestly, you are doing it ass-backwards. Qt plotting etc MUST be done in the main thread - widgets CANNOT be made outside of it. Also, just because acquisition is in another thread does not mean it gets less priority. So your point is moot.

remark: using postEvents/sendEvents is just a sign that what you have in place is not a good solution.

"If I simply visualise the data every 20ms or so, I'm introducing a dead time in the system and this would cause data loss."
No, that is not correct. Your implementation has tight coupling of acquisition and visualisation. Your design is not flexible enough to cope with fixed rate visualisation + variable rate (or just different rate) acquisition. This is not a problem that is inherent to acquisition-visualisation!

I strongly suggest you modify your design before further obfuscating with unnecessary, rigid, tightly-coupled complications.

As for concurrent::run, you cannot pass a member method (FPG::Tvisualise) to something expecting a zero-argument function. All member methods take at least one argument ('this'). When you see pointers to member methods being invoked, it will always be done with a class instance, never on its own.

If you persevere with your Qt concurrent route, you will need to get friendly with boost::bind or make your own functors that overload operator()
If you have a problem, CUT and PASTE your code. Do not retype or simplify it. Give a COMPLETE and COMPILABLE example of your problem. Otherwise we are all guessing the problem from a fabrication where relevant details are often missing.

7

Wednesday, April 11th 2012, 1:03am

Very well, then how do I make my design more flexible? How do I ensure I don't introduce dead time in my program? Any suggestions?

8

Wednesday, April 11th 2012, 9:25am

You have three (maybe four) aspects - acquisition, saving to disk, and gui (maybe split this into processing + gui)

I'm guessing that your acquisition is fine - you seem to have control over it and with no issues.

A simple route to take:

Make some class(es) that only deal with saving data to disk, and can be setup to save variable size buffers to disk (0.5mb, 1mb, 2mb - whatever will make sense as options for you.

Seperate out your classes for processing and gui work.

Add some intermediate 'node' between the acquisition and data saving/gui classes. All data should come to this node. You configure the node to send as much data as you want for saving. You configure the node to send as much data as you want to the processing/gui classes - This bit here is where you consider how many frames to skip.


Using signals/slots etc will work well with threads - you should consider whether you want to send duplicate data from the node to different locations or make it readonly.

Those classes that handle data saving should be wrapped up in a thread - QThread and its event loop will work well - the gui classes should be in the main thread (maybe the data processing for gui should be in main thread or maybe not)
If you have a problem, CUT and PASTE your code. Do not retype or simplify it. Give a COMPLETE and COMPILABLE example of your problem. Otherwise we are all guessing the problem from a fabrication where relevant details are often missing.

9

Friday, April 13th 2012, 1:07am

Thanks for your advice.