PDA

View Full Version : How to slow down a worker thread doing some scientific calculations?



agarny
25th November 2013, 21:16
One of the big features of my application is to run scientific simulations. For this, I have a worker thread where I do all the scientific calculations, and then I have the main thread that handles all the GUI stuff, including updating plots using the scientific data that has been calculated. Now, that data can tends to get calculated very quickly, meaning that the plots also get updated very quickly. I would therefore like to slow the calculations down (and, as a result, the updates to the plots too). Right now, my solution consists of using QWaitCondition::wait(), but the problem is that the time to wait can only be expressed in milliseconds, and that really makes things far too slow now. My understanding is that on Windows (I am targetting Windows, Linux and OS X), we can't go lower than 1 ms. So be it, I guess. Still, what other solution/approach/etc. would you recommend to achieve a sub-millisecond delay in a thread? Or am I simply out of luck here?...

Cheers, Alan.

d_stranz
25th November 2013, 21:28
This is a first - I have never heard of someone doing simulations who wanted to slow down the calculation :-)

I think you are looking at this from the wrong perspective. The base problem is that your GUI can't keep up with the rate at which your simulation is producing results. So rather than making your simulation thread be in charge of when GUI updates occur, put the GUI in charge. Instead of letting whatever signal is coming from the thread trigger an immediate update, use it to simply set a flag that says an update is needed. The GUI can then check this flag whenever it is ready to do an update, and refresh your plots. It won't matter how fast your simulation runs, the GUI won't respond to it until it is ready.

You might need to implement your own processEvents() loop where you check the update flag.

agarny
25th November 2013, 21:39
More often than not, you don't want to slow down simulations, but for teaching or demonstration purposes it can be very useful indeed. :)

Otherwise, my GUI definitely keeps up with the rate at which simulation data is being generated. In fact, the worker thread doesn't communicate with the GUI thread at all. Its only purpose is to generate simulation data, that's all. The GUI thread, on the other hand, does check whether new simulation data is available and, if so, then update its plots. The GUI thread will basically keep looking for new simulation data and when none is available anymore, then it will stop looking (until we run a new simulation). For this, I don't use signals/slots since it slows down things too much (yes, I want to slow things down, but only when I want them to be slowed down otherwise I want things to be as fast as possible... :)). So, I am already kind of doing what you are suggesting, and I still want and need to be able to slow things down in my worker thread...

d_stranz
25th November 2013, 22:24
OK, so if I understand you, what you are after is a way to run your simulation stepwise so you can display intermediate results for demonstration purposes?

Some Googling turned up the SetWaitableTimer() Windows API call, which apparently can give you a timer with 100 ns resolution. See this MSDN entry (http://msdn.microsoft.com/en-us/library/windows/desktop/ms686289%28v=vs.85%29.aspx). Also take a look at this (http://www.songho.ca/misc/timer/timer.html).

Unfortunately, there appears to be no portable cross-platform way to do this.

agarny
25th November 2013, 22:37
What I want and need to be able to do is to display the simulation data as if I was to display it in slow motion. Think of a movie that you play in slow motion. That's what I want to achieve.

I will have a look at SetWaitableTimer() and see what I can do with it, if anything. Otherwise, I am starting to wonder whether the fact that my worker thread doesn't communicate with my GUI thread isn't actually a problem when I want my data to be displayed in slow motion. Hmm, I will need to look into that too.

qtbeginner0
25th November 2013, 22:56
don't know if that helps:
http://stackoverflow.com/questions/7684359/using-nanosleep-in-c

d_stranz
25th November 2013, 22:57
Well, a movie typically runs at 30 fps, so that's 33 ms, well within the standard QTimer resolution. Updating faster than that won't be of much use since you can't visually process it even if the GUI can keep up.

It probably makes sense to get your simulation thread talking to your GUI thread. Add start(), step(), and stop() slots to it, and connect the timeout() signal from a QTimer in the GUI thread to the step() slot. Add a stepDone() signal that the thread emits to let the GUI update the display and restart the timer.

Set the timer as a single-shot to whatever interval makes sense (and this also gives you convenient control over the simulation from a slider in the GUI so you can implement variable slow motion). You can have an alternate entry point for the simulation when you want it to run at full speed without steps.

So you basically have a signal / slot based handshake between the GUI and thread that runs when you want it to and is bypassed when you don't.

---
Later: An alternative is to change the way your worker thread produces results. Let it run at full speed, and instead of producing a single final result, let it create a linked list or some other data structure that contains stepwise results. You can choose to play this back through the GUI by stepping through the list at whatever rate you want (or go backwards in time without having to re-run the simulation), or just display the final result by skipping to the end of the list.

anda_skoa
26th November 2013, 20:16
Stupid question, but have you tried QThread::usleep()?

Cheers,
_

agarny
29th November 2013, 18:46
@qtbeginner0: that doesn't work under Windows.

@d_stranz: I have tried CreateWaitableTimer()/SetWaitableTimer(), but it doesn't make any difference. I am still getting the same result as QWaitCondition::wait() or QThread::msleep(). So, it seems that I am really out of luck when it comes to Windows' time resolution which really seems to be one millisecond. I haven't yet had time to look into getting my worker thread to talk to my main thread (through the signal/slot mechanism), but it might be my only option left...

@anda_skoa: yes, I have tried it, except that it doesn't work on Windows since it uses the ::Sleep() function which has a millisecond resolution. In fact, if you check qthread_win.cpp, you will see that QThread::usleep() is defined as ::Sleep((usecs/1000)+1)... !!

d_stranz
3rd December 2013, 03:51
I haven't yet had time to look into getting my worker thread to talk to my main thread (through the signal/slot mechanism), but it might be my only option left...

What about my alternative suggestion of having your simulator write out a series of intermediate results as a list instead of just the final result? This doesn't require signals / slots or anything fancy, and is probably the least invasive as far as changes to your simulator code. If it already runs with some kind of time stepping (which is implied by your original question about how to slow it down), then you probably just need to write out a new link in the list at each step (or some multiple of steps, whatever makes sense). The simulator can then just run to completion, and hand its output over to the GUI side, just as it does now. No need for handshaking via signals and slots, no need to slow it down.

Most of the changes come on the GUI side, which needs to be reworked to "play" the list of results. Despite this being my idea :) I think this has a lot of advantages. I've seen simulations of molecular motion where you can "rock" the molecule back and forth around some axis of rotation - in your case, this would amount to stepping backwards and forwards through the list of intermediate results. It also means you never have to re-run the simulation unless you change parameters - just keep replaying the same set of results. Likewise, you can vary the replay speed to get faster or slower slow motion.

agarny
4th December 2013, 09:12
What about my alternative suggestion of having your simulator write out a series of intermediate results as a list instead of just the final result? This doesn't require signals / slots or anything fancy, and is probably the least invasive as far as changes to your simulator code. If it already runs with some kind of time stepping (which is implied by your original question about how to slow it down), then you probably just need to write out a new link in the list at each step (or some multiple of steps, whatever makes sense). The simulator can then just run to completion, and hand its output over to the GUI side, just as it does now. No need for handshaking via signals and slots, no need to slow it down.
I guess I could let the worker thread do its job as fast as possible (i.e. without taking into account the fact that the user might want a delay in the plotting of the results).

However, this is not an option, because I use a progress bar to show the progress of the worker thread. Now, I agree that I could update the progress bar in a different way, i.e. by keeping track of how much has been plotted rather than how far down the simulation we are.

Still, there is a more important reason why your approach is not an option, and it is the fact that the GUI allows the user to pause the simulation, modify some parameter values, and then resume the simulation. Now, as you can see, if I was to let the worker thread do its work and only plot the results when I want (so to speak), I would break an important feature of my application, and that is definitely not an option.

So, as far as I am concerned, I have no other option but to slow down the worker thread itself.

stampede
4th December 2013, 09:28
Still, there is a more important reason why your approach is not an option, and it is the fact that the GUI allows the user to pause the simulation, modify some parameter values, and then resume the simulation.
On "pause" signal you can tell your worker to stop as well as stop displaying the results in gui. Results that are still in the "todo" list and were not yet displayed in gui could be simply discarded at this point (if you change the processing parameters).
I agree with d_stranz, some time ago I was working on very similar problem with live data processed / generated faster than the gui refresh rate. In general, the final implementation contains a list of intermediate results, updated very frequently in the worker thread, and displayed with desired speed in the gui thread (or played backwards, whatever, if you have a list of intermediate results with "timestamps" you can do whatever you want with them).

agarny
4th December 2013, 09:39
On "pause" signal you can tell your worker to stop as well as stop displaying the results in gui. Results that are still in the "todo" list and were not yet displayed in gui could be simply discarded at this point (if you change the processing parameters).
Yes, I did think of that, it's just that it would require some work on both my GUI and worker threads while it wouldn't require any work if I could, somehow, slow down my worker thread as such. This being said, it might be my only option here...

d_stranz
5th December 2013, 00:30
Hope you don't mind us beating up on you :) but you can still accomplish what you want without inserting delays into the simulation.

If you can currently pause your simulation, change parameters, and then resume it again, I assume you have some concept of "current state of the simulation using parameters {x}".

So, back to my proposal where the simulation runs at full speed to completion and saves a list of results: A relatively easy way to implement the pause / change / resume feature would be to not only output the intermediate results at each time step, but also output whatever state variables / parameter values there are at that step. When the user pauses the playback, edits parameters, and resumes, you reload the simulation with the current state values and new parameters, discard the rest of the results list, then let the simulation run to completion from that point forward.

Another benefit: If simulation always runs to completion, you have the option of saving the whole list to a file. The user can then step backwards to some point, change parameters, and let it run again. Now you have a way to directly compare two versions of the simulation side-by-side to see the effect of the parameter change. Or you can reload a saved simulation to study it again, without having to recompute it. If you really decouple the results viewing from the results generation and save some version information in the results, now you have a way to compare the effects of algorithmic changes as well as parameter changes.

agarny
5th December 2013, 09:48
Hope you don't mind us beating up on you :) but you can still accomplish what you want without inserting delays into the simulation.

If you can currently pause your simulation, change parameters, and then resume it again, I assume you have some concept of "current state of the simulation using parameters {x}".

So, back to my proposal where the simulation runs at full speed to completion and saves a list of results: A relatively easy way to implement the pause / change / resume feature would be to not only output the intermediate results at each time step, but also output whatever state variables / parameter values there are at that step. When the user pauses the playback, edits parameters, and resumes, you reload the simulation with the current state values and new parameters, discard the rest of the results list, then let the simulation run to completion from that point forward.

Another benefit: If simulation always runs to completion, you have the option of saving the whole list to a file. The user can then step backwards to some point, change parameters, and let it run again. Now you have a way to directly compare two versions of the simulation side-by-side to see the effect of the parameter change. Or you can reload a saved simulation to study it again, without having to recompute it. If you really decouple the results viewing from the results generation and save some version information in the results, now you have a way to compare the effects of algorithmic changes as well as parameter changes.
Thanks a lot d_stranz, you make some good points. I particularly like the possible benefit of comparing two versions of a simulation side-by-side. Possible because it assumes that the 'default' simulation would have time to finish, for which there is no guarantee whatsoever.

Anyway, I have to be pragmatic. My simulation code has been stable for months. Yet, from what I can tell, to implement your suggestion would require quite a bit of work on my end. So, the simplest solution for me would be to slow down my worker thread: minimal work on my end, no additional testing required, etc.

So... this is what I did yesterday afternoon. I simply added a loop that does nothing for a given number of times (to make the worker thread more or less slow). I genuinely wish there was a better way to slow down my worker thread, but at least it was very simple to implement, didn't require me having to rework my GUI thread, retest things, etc. and, more importantly, it does exactly what I want. Sure, a loop that does nothing will use the CPU for... nothing, but then again it's a worker thread, so I am 'fine' with it being always busy while it's actually running a simulation.