Community Documents

cancel
Showing results for 
Search instead for 
Did you mean: 

A Look at Race Conditions

I was working on a presentation for an internal user group and started playing around with race conditions and how to avoid them.  This VI was the result.

 

So this VI has two FOR loops that each run 25 times in parallel.  Each loop attempts to increment the variable by doing a Read, Increment, Write process.  I did this using local variables, global variables, a Set/Get Functional Global Variable, an Action Engine with Get/Set/Increment actions, and a Data Value Reference.  These two loops were ran 100,000 times and benchmarks were done out of curiosity on performance.

 

This VI also shows one of my pet peeves: the Set/Get FGV.  As I have argued with people, you do not avoid race conditions just by using a FGV.  In fact, you just made things worse by using the FGV over a simple Global Variable (just look at the execution times).  It is only when you protect the entire Read, Modify, Write as an entire entity do you avoid the race conditions.  Race Conditions and Functional Global Variables in LabVIEW

 

So feel free to try out my code.  Tell me if I did something stupid or if there is something else you would like compared in here.

 

Race Conditions.png

 

Code saved in LabVIEW 2014.  The main VI is Race Conditions.vi

 

Revision 1: Added a case for Semaphores to protect the global variable to prevent the race condition.  It should be clear that Semaphores are SLOW.


GCentral
There are only two ways to tell somebody thanks: Kudos and Marked Solutions
Unofficial Forum Rules and Guidelines
"Not that we are sufficient in ourselves to claim anything as coming from us, but our sufficiency is from God" - 2 Corinthians 3:5
Comments
samsharp99
Member
Member
on

In my mind (and from NI's courses IIRC from when I did them a few years ago now), an AE and an FGV are the same thing under different names. I see the distinction in your code (get, modify, set and then increment as atomic operations) but I thought the 'increment' was what made an FGV functional.

I guess the point you're making though holds that only the AE and DVR actually prevent against race conditions but they come with a (to be expected) performance hit.

Out of curiosity, I also tried using a semaphore (the other way to prevent race conditions) around your FGV example to fix the race conditions:

FGV - 4.15s

FGV with Semaphore - 21.24s

Maybe not...

crossrulz
Knight of NI Knight of NI
Knight of NI
on

Sam_Sharp wrote:


                       

In my mind (and from NI's courses IIRC from when I did them a few years ago now), an AE and an FGV are the same thing under different names. I see the distinction in your code (get, modify, set and then increment as atomic operations) but I thought the 'increment' was what made an FGV functional.                   

There has been a small push within some of the community to actually set a distinction between a FGV and an AE.  But at least you seem to get my point.

Sam_Sharp wrote:

I guess the point you're making though holds that only the AE and DVR actually prevent against race conditions but they come with a (to be expected) performance hit.                  

Performance hit?  Check out the time for the AE.  It was actually better than the local variables.  The DVR is a different story.

Sam_Sharp wrote:

Out of curiosity, I also tried using a semaphore


                   

I didn't think about trying to semaphore.  I might have to add that when I find some down time.


GCentral
There are only two ways to tell somebody thanks: Kudos and Marked Solutions
Unofficial Forum Rules and Guidelines
"Not that we are sufficient in ourselves to claim anything as coming from us, but our sufficiency is from God" - 2 Corinthians 3:5
samsharp99
Member
Member
on

crossrulz wrote:       

Performance hit?  Check out the time for the AE.  It was actually better than the local variables.  The DVR is a different story.


                   

Argh! After registering the AE/FGV distinction in my mind originally, I then forgot about it when comparing the performance!

LucianM
Active Participant
Active Participant
on

I was surprised by the high execution time when semaphores are used so I extended the original code with 2 additional cases and used a single element queue (the same concept used by semaphores) and I obtained about half of the execution time:

. SEQ.png

 

SEQ2.png

 The timings obtained using LabVIEW 2014 on a i7-6700K:

Semaphore: 10.8

AE: 0.53

DVR: 1.93

SEQ: 5.19

SEQ2: 5.07

The lesson learned by me was to never use semaphores, especially when performance is a concern!

 

Lucian
CLA
crossrulz
Knight of NI Knight of NI
Knight of NI
on

I wouldn't say "never".  I am sure there is a use somewhere.  I thought I had one with handling a VISA Resource, but then I learned there is a locking mechanism you can do with VISA.  Have not revisited it to see how the performance works out though.

 

But thanks for reminding me about the SEQ.  I did discuss it at my NI Week presentation, but it looks like I failed to update this document to include it.  So I might as well provide the link: Are Global Variables Truly Evil?


GCentral
There are only two ways to tell somebody thanks: Kudos and Marked Solutions
Unofficial Forum Rules and Guidelines
"Not that we are sufficient in ourselves to claim anything as coming from us, but our sufficiency is from God" - 2 Corinthians 3:5
ZyXI
Member
Member
on

It appears that OP benchmark occupies too long time for initialization. I have moved all of those magic 25 constants (number of iterations of inner cycles) outside, near the 100 000 constant (number of times initialize-use-destroy-style tests are performed) so that it may easily be changed and found out that:

 

With 100 000 repeats and 25 iterations (almost original):

local   0.69

global  0.12

FGV     3.45

Sem    12.94

AE      0.61

DVR     2.00

With 10 000 repeats and 250 iterations:

local   0.59

global  0.05

FGV     6.21

Sem    13.78

AE      2.97

DVR     2.54

 

Apparently, DVR initialization and destruction takes too much time and AE overhead on synchronization is not that small like it appears: note that AE now runs approximately two times slower then FGV which looks like expected.

 

Also here are results with 1 000 repeats and 2 500 iterations, they are not much different:

local   0.67

global  0.08

FGV     6.49

Sem    14.00

AE      3.22

DVR     2.55

And 10 000 000 repeats and zero iterations: i.e. testing only initialization and destruction:

local   0.38

global  0.41

FGV     1.57

Sem     0.39

AE      1.59

DVR    34.07

 

The conclusion is basically: data value reference is better choice as I can hardly imagine a situation where you not only need just tens of iterations, but also care about performance. Also DVR takes hell lot time of initialize so do not do that often.

 

By the way, semaphore code is utterly incorrect for taking initialization into account: semaphore allocation is outside of the sequence where timing is tested (which means that they are initialized and destroyed only once), all other initializations, including DVR allocation, are inside. Since semaphores are bad even with that advantage I do not see any sense in fixing that, but you need to know that to understand why semaphore initialization takes that small amount of time.

 

I am using LabVIEW 2015 SP1, 32-bit, in a 64-bit Windows 7, i3-4170, 16 GiB memory.

 

For reference, code with all 25 constants moved out: https://yadi.sk/d/LO1auF2j3adew6.

Contributors