Update Progressbar inside a loop

I have a mathematical application which has a multiple loop. I wish to monitor the progress of the outer loop, which can take as much as a day to complete as there are five inner loops all doing math. The problem I am having is that progressbar.refresh does not update the progress bar. I can get it to update by using the dreaded app.doevents(0), but for obvious reasons I would prefer not to use that option.

I note that progressbar.invalidate also does nothing.

Can anyone suggest a way to force an update of the progressbar without using app.doevents(0).

Loop code in a Thread, update UI with a timer

Thanks Pedro,

That does indeed work, but for reasons I dont fully understand, the code in a thread runs much slower than the code in a method in the main window. But you are correct- that does solve the problem. But using app.doevents(0) provides only a minor slowdown. I wish it were not so dangerous.

You are not setting a Priority acording to your proyect

I will try using priority to speed things up. Thanks.

[quote=405935:@Robert Birge]Thanks Pedro,

That does indeed work, but for reasons I dont fully understand, the code in a thread runs much slower than the code in a method in the main window. [/quote]

Many people forget that in a Thread, the framework may try to yield on every loop boundary. This is very expensive in a tight loop.

Try adding #pragma BackgroundTasks False to all methods, and then explicitly add calls to Yield or .Sleep() when you think it’s appropriate for the thread to yield. This may require some tuning to get just right.

See http://developer.xojo.com/pragma-directives

That is not acurate.

A thread does not necessarily yield time at every loop boundary even though it has an opportunity to do so. A thread actually yields to another thread when the Thread Scheduler decides that its timeslice has expired. Context switches are expensive, so the Thread Scheduler always tries to avoid them.

Using a Priority to define the timeslice of the thread and let the Thread Scheduler do its job, it is much better than atempting to handle the threads manualy

[i]The main thread for the app has a priority of 5. You can alter the priority of your own threads to give them more or less time relative to the main thread. The default is also 5.

These values are totalled, then divided by the main thread and divvied out by proportion, but the main thread always has a priority of five, so using very large numbers for other threads will potentially starve the main thread. For example, if you give a second thread a priority of 500, there will be a total 505 slots in the scheduler and five of those belong to the main thread. [/i]

So if you dont set a Priority, the thread will yield constantly to give the main thread the same time. If you set a priority of 500, the thread will yield rarely just to keep the main thread updated.

I’ll stand by my statements: I’ve been doing performance profiling for years using RealBASIC, RealStudio, and Xojo, and the data is pretty clear: the overhead from the thread scheduler even checking whether to yield at loop boundaries is substantial, even if it does not yield at those checks.

I put together a test app to demonstrate this, which runs this code in the main thread:

dim t0 as double = Microseconds
for i as integer = 1 to 1e6
  for j as integer = 1 to 1e2
    dim x as integer= i*j
  next
next
dim deltaT as double = Microseconds-t0
dim msg as string = "Elapsed time = " + format(deltaT/1e6,"#.##")
System.debugLog msg

Results (macOS, built app):

  • 32 bit build background tasks disabled: 1.39 seconds
  • 32 bit build background tasks enabled: 4.57 seconds (328% slower)
  • 64 bit build background tasks disabled: 0.36 seconds
  • 64 bit build background tasks enabled: 2.99 seconds (830% slower)

These tests are as I expect, and demonstrate that the thread scheduler has a big overhead.

Some other tests are giving strange results, so I submitted a feedback case if anyone wants to chime in: <https://xojo.com/issue/53395> Namely, in a thread with priority 5, #pragma DisableBackgroundTasks seems to have zero effect, whereas in a thread with priority 10, the result is 10x faster. This seems inconsistent with how the thread scheduler is described to work.

What makes me sad is running the example from your ticket, I get a faster result in a mac VM on a windows machine than running it directly on the windows machine… how is that even possible, it…

Turns out it’s a different issue altogether:

#pragma BackgroundTasks Off 

doesn’t work. (see http://developer.xojo.com/pragma-directives )

With that fixed, setting thread.priority has little or no effect (which makes sense, as in these tests only 1 thread is running).

See
<https://xojo.com/issue/53396>

The actual difference beetween Priority 5 and 10, it is actually not a great difference. Since the timeslices are divided among all the threads.

Your code in the main thread with #Pragma BackgroundTasks False
vs
your code in a thread with priority 5
vs
your code in a thread with priority 500

As expected, a priority of 5 yields to the main thread and make the proces slower, but The difference in times disabling other threads and using a priority of 500, are negligible.

But, with the correct use of a priority, the UI is still responsive, also the OS will not consider the app as “Not responding” also, you dont have to yield manually.

The pragmas are only valid for the current method, aren’t they? Are there any drawbacks when the backgroundtasks are disabled?

Correct. If you call into another method of your own, it may yield. And there are a small number of framework calls which also yield internally.

Yes, if you aren’t yielding at all, then you might as well not be using threads.

The trick is to yield “just enough” without wasting overhead.

in the code example I posted above:

for i as integer = 1 to 1e6
  for j as integer = 1 to 1e2
    dim x as integer= i*j
  next
next

There are 1e6 x 1e2 = 1e8 = 100 million loop boundaries and thus 100 million chances for the thread scheduler to yield. Since this entire operation takes less than 0.5 seconds to run, checking 100 million times whether to yield is massive overkill.

A rule of thumb is to yield at least 60-100 times per second to keep other threads and the OS happy. Not 100 million :slight_smile:

Hm… I need to experiment with this. I have a complex calculation which spans quite a few classes. I’ve tried to add the pragma at a few keypoints in the code. And made everything slower.

Thanks for the explanation, though.