Measurement Studio for .NET Languages

cancel
Showing results for 
Search instead for 
Did you mean: 

Improving application speed

Hi,
 
I'm working on a complicated program with a lot of data input and output, spread over four PCI-6259 cards.  Acquired data undergoes a bit of post-processing and plotting, as well as saving to file.  The application isn't quite as streamlined and fast as I'd have hoped.  I recently switched to ActiveX graphs, instead of .NET controls, and that helps quite a bit.  Are there any other ways to improve speed?  For instance, should the UI and data acquisition be in separate threads?  Should file writing be in its own thread?
 
Thanks!
John
0 Kudos
Message 1 of 7
(4,040 Views)
Hi John,

First off I just wanted to let you know that we are aware of the peformance difference between the ActiveX graphs and .NET graphs.  Based on customer feedback, we did go back and do some hardcore profiling of our graph controls and did find several bottlenecks in our code.   So we are working on improving our existing .NET graphs as well as researching future high-speed graphs.  I just wanted to make you aware of that fact.

When you say that the application isn't streamlined and fast enough, are you talking about UI performance, data acquisition speed, analysis, etc? Maybe you could elaborate on this topic. 

Typical its best practice to separate your UI code from you analysi code from your data acquisition code. Setting up your application to be mult-threaded should help some.

As far as optimizing the ActiveX graph control, there's several little tips and tricks you can do such as:
  • Configure the graph to not immediately execute all pending paint requests to improve drawing performance. To configure this setting with the property pages, set Control refresh to Invalidation in the Graph property page (graph control) or set ImmediateUpdates to false in the All property page. 
  • Configure the graph to exclude axes to improve drawing performance. To configure this setting with the property pages, deselect Visible for each of the axes defined in the Axes
  • You could also play with the Chart Style and Chart Lenght properties as well as the plotting and chartting methods.  If you are using a ChartXY or ChartXvsY method, minimize the size of the chart history that each graph maintains. If you never need to scroll back through the data, you should reduce the chart history length to only what you want to display. If you are using ChartY, set the Chart history to automatic (Properties>>Graph>>Chart history
  • Configure the graph control to use a 2D frame rather than a 3D frame to improve drawing performance. To configure this setting with the property pages, deselect 3D Frame in the Style property page
  • Access graph plots via the Plot object instead of the Graph object.
Just some thoughts!

Also, is it common for you to visualize the data on the graph in real-time? I'm just trying to gauge what "high-speed" plotting helps you accomplish.

Best Regards,
Jonathan N.
National Instruments
0 Kudos
Message 2 of 7
(4,034 Views)

Thanks Jonathan,

To elaborate on the speed issues: My application is taking in 32 channels of analog data at 30 kHz and 32 channels at 2 kHz.  It is creating and outputting 2 100 kHz analog signals and 8 digital channels.  On top of this, I'm visualizing the analog data (either the 32 30 kHz channels or the 32 2 kHz channels; never both at once), running all the analog data through digital filters (1 filter for each channel), and then running the 32 30 kHz channels through another custom filter for artifact rejection and event detection.  In the background, all the data is being written to disk.  Each of these things tends to slow it down, in terms of updating the graphs and responding to user input.  In some cases, it can get bogged down enough to crash the program with a buffer overrun.  For an example of something that speeds it up, I reduce the number of points to plot by downsampling the acquired data, which helps a lot.  Or, if I don't write the data to disk, that also helps quite a bit.  Not using filters again improves the speed.

I've been considering, like I said, separating the UI thread from the acquisition thread and the file writing thread (they're all in the UI thread right now).  But I thought that asychronous IO took care of this (and I'm using asynch IO).  However, I've noticed that, at least for analog output executed from the main UI thread, asynchronous IO isn't all that asynchronous.  For example, if I try to write a long stretch of data to the AO while acquiring data, the application will crash since I'm not acquiring data fast enough from the cards (something in the call to BeginWrite blocks when it shouldn't).  If I separate the Write call into a backgroundWorker thread, the application is totally fine.  So that's really confusing to me, and is why I'm thinking that all AO should be in separate threads from the UI.

Thanks for the other plotting recommendations.  I hadn't seen these before!  I'll definitely start using them!

0 Kudos
Message 3 of 7
(4,013 Views)

Hi John,

Are you benchmarking the performance of your application in release or debug configuration? You should always base your application speed on release configurations and never debug as the debugger slows things down.  Also, make sure that when you do configure your application to release configuration, you make sure to Ctrl+F5 and not F5.  F5 (or Build >> Start Debugging) will still attach the VS debugger to the release code and use it to catch exceptions. Ctrl+F5 will not use the debugger.

Our asynchronous model does use a separate worker thread behind the scenes for the acquisition.  This is all discussed in the Asynchronously Reading and Writing with the NI-DAQmx .NET Class Library and the Events, Callbacks, and Thread Safety in Measurement Studio .NET Class Libraries help topics in the NI Measurement Studio Help. 

Now, it appears that you actually explicitly created your own thread ran your data acquisition code in there and you saw better performance than using our async model? Is that a correct assumption? If you use this method of explicit thread-creation, you as the user will need to take care of thread-safety and the marshalling of data back to the UI thread.  Our async model handles all that behind the scenes. However, if you see a large hit in performance, perhaps we need to look our own code and maybe do some profiling. 

Putting all your analysis in a separate thread should also help speed as well.

Let me know your thoughts

Best Regards,

Jonathan N.
National Instruments
0 Kudos
Message 4 of 7
(3,988 Views)

Thanks again Jonathan,

I'm compiling in Release configuration, but I haven't been using Ctrl+F5--I'll try that and see if it helps out.

I have definitely seen better performance when putting analog output code into my own thread (via the BackgroundWorker class), rather than using the async functions.  I haven't tried this for analog input yet.  It's very perplexing, so I'd be curious as to whether you or anyone else can reproduce it (I might have done something so bizarre that this is only a problem for my application and no one else's).

0 Kudos
Message 5 of 7
(3,984 Views)

Hi John,

I'll tell you want I have done, maybe this can help you. I'm using the WaveForm chart, but I assume that this configuration is valid for others types of XY graph.

My system produces on 8 channels 1 float every 1000Hz per channel. I can have fluid display by doing this:

PointHistory can be as much as 100 000 points per channels. (800 000 in total)

I'm stacking in one thread 100 points in one buffer per channel (A jagged array of 8 buffer).

Than I use BeginInvoke to send a copy of the buffer to the WaveForm graph using the function PlotAppendY(double [] y) with the buffer for each plot. Using PlotAppendY(float f) is simply impossible. This includes too many computinjg for a single point. Using BeginInvoke with the copy of the buffer is the best option because this doesn't block the data acquisition thread, and the WaveFormGraph, as stated previously when ImmediateUpdate set to false, seems to optimize its display rate depending on the load of the system.

So at 1000Hz of data coming in for 8 channels, I can have a WaveForm chart, showing all 8 channels data in StripChart mode. A total of 800 000 points are continously refresh using the techniques .

So according to my experiment, my advices are:

- Two separate thread.

- Never use PlotAppendY(double y) but PlotAppendY(doube [] y);

- Use BeginInvoke instead of Invoke so you data gathering thread is lock up by the display. The copy of the buffer is less expensive than to wait for the display to be refreshed.

 

Hope this helps,

Jean

 

0 Kudos
Message 6 of 7
(3,930 Views)
Thanks!  Those sound like great ideas--can't wait to try them out!
0 Kudos
Message 7 of 7
(3,914 Views)