02-14-2024 06:03 PM
This one surprised me and I wanted to post it for anyone else that might make some bad assumptions
In my top loop I use some static references. I always imaged those as constants requiring nearly zero resources. As it turns out, if you busy the UI with something like updating a graph, it actually takes easily 300 ms to read 10 references.
02-14-2024 07:43 PM - edited 02-14-2024 07:44 PM
Three things:
02-15-2024 01:53 PM
Yes, I definitely learned the hard way that static references should go top level. I still don't understand why since they should be like a numeric constant which would be fine in a tight loop right? Putting the references at the top is ugly.
No, if you do something with the reference array outside the for loop, we still see code that should be constant folded taking hundreds of miliseconds even if you disable debugging
Neither loop is a tight loop. Each has a 5 ms sleep. I don't totally understand the load on the UI thread and how that might slow down my loops. When you write a value to a control like in the bottom loop, you aren't asking for a synchronous draw are you?
02-15-2024 02:34 PM - edited 02-15-2024 02:48 PM
A sorry, I did not see the occurrence timeout. Haven't use that in decades, ever since the event structure was introduced. 😄
@nanocyte wrote:
No, if you do something with the reference array outside the for loop, we still see code that should be constant folded taking hundreds of miliseconds even if you disable debugging
Can you define "something". I really don't understand what you are trying to say. With debugging disabled, the timing in the original top loop is basically instantaneous in my testing. (i.e. <<microseconds)
What is the use for the array of references? For example if you use it to iterate over property nodes, each will probably run in the UI thread, I think
02-15-2024 02:47 PM
Example something.
Without doing "something" to the array of references, I think the compiler identifies the static references as useless and removes them
02-15-2024 03:04 PM - edited 02-15-2024 03:05 PM
Yes, I was just playing with something similar and now that the references are no longer "dead code eliminated" (because if generates an output!), I do see the dramatic slowdown of we build inside the loop.
Of course graphing 100k points on a graph that is a few hundred pixels wide is very expensive and is heavily taxing the UI thread. I simply would not do that ever.
Only NI can say what's going on here....
02-15-2024 11:49 PM
@altenbach wrote:
Only NI can say what's going on here....
I am not from NI, but I think this penalty caused by massive threads contexts switching between UI Thread and thread, which is assigned for standard execution system. UI Thread is single, the standard execution system may have up to 24 parallel threads by default as far as I remember.
The references (as well as property nodes) executing in UI thread (this also stated somewhere in documentation). So, every time when we obtaining the reference, it switched us to UI thread, get this reference, then switched back to standard thread and in fast loop (here 32 iterations for 10 references running in while loop) we have lot of such switches (why its not cached, another question). And all 10 references will be obtained sequentially, not parallel.
It is quite simple to demonstrate. Usually if I have some UI loops with references and property nodes mixed together with some loops which should be fast, then to control that I will not block UI thread in such loops by occasionally forgotten reference I placing another while loop which will suspend UI thread execution for some time and see what happened with my loops then:
I just put half second Sleep called from kernel32.dll from UI thread (not thread safe!) and another half second by Wait (ms), otherwise you will get troubles to stop this VI. Call the DLL in UI Thread will suspend execution of UI thread for half second, whole UI will be "frozen" and you will see that the loop, where we have Reference is also suspended for that time, we have only around 50 iterations per second, and not 100 as expected to have for the loop with 10 ms delay.
Now, if I will move the reference outside of this loop, then the cycle will be not blocked any longer, and will iterate around 100 times per second, as expected, that the difference:
But if I will use Property Node inside of my cycle, then penalty comes back, because Property Node will execute in UI thread and will therefore will wait for Sleep, its cannot be executed in parallel with DLL call in UI Thread as well:
So, even if placed references outside of the loop, but when you will start to work with Property Nodes, your "Fast!" loops will slow down again (not checked, but pretty sure).
It is possible to see these context switches in Process Explorer from SysInternals:
(you can also suspend the threads and check what happened).
And back to original VI — there lot of threads switches to UI Thread (which is single) and back caused by references, and the overall performance goes down significantly.
02-16-2024 04:38 AM
When i hear "The UI is slow/sluggish" i immidiately think of UI thread switching, and your example shows it clearly. It should be fairly easy to modify it to use "Get Control Value by Index" and it'd be interesting to see the difference!
02-16-2024 04:56 AM - edited 02-16-2024 05:12 AM
A static reference is still a reference. Static means that the underlying object is allocated/created at load time, not runtime and the object gets a marker to indicate to the Close Reference that it should do nothing on this object, since it will be deallocated at unload time.
All the rules for accessing VI objects still apply, including having to arbitrate for the UI thread when trying to access it. So the only big difference about static references to normal references is that retrieving such a reference does not increment the object refcount and passing it to a Close Reference node is a NOP! Other rules still apply including that any operation on them requires the UI thread to run it in, to guarantee no race conditions can occur.
Property nodes in fast or tight loops is ALWAYS a code smell. In fact I go as far as claiming that property nodes (and methods) in anything but your active UI loop are simply bad. Just don’t do it. If you need UI feedback from such locations, the solution is NOT to use control references but proper queue or similar based messaging to your UI loop! If that feels to cumbersome to implement, the according UI feedback for sure is not important enough to care about. 😀
I have long ago decided that the somewhat popular design model of acquiring all UI control references to pass to subVIs for updating of the UI is one of the most ultimate anti patterns. It seems easy to do but promotes terrible application design. It’s often employed by people who know enough about LabVIEW to be dangerous but not enough to know that they are dangerous.
02-16-2024 06:31 AM
@rolfk wrote:
I have long ago decided that the somewhat popular design model of acquiring all UI control references to pass to subVIs for updating of the UI is one of the most ultimate anti patterns. It seems easy to do but promotes terrible application design. It’s often employed by people who know enough about LabVIEW to be dangerous but not enough to know that they are dangerous.
Word!