High-Speed Digitizers

cancel
Showing results for 
Search instead for 
Did you mean: 

Improving NI-SCOPE performance

Hi,

I have been exploring various program architectures to improve performance.
A fetch-only program I wrote for testing could achieve a sustained transfer
of 50 Msample/sec using niScope_FetchBinary16() but has overruns at
100 Msample/sec (the next higher speed) using a PXI-5122 digitizer. If
niScope_FetchBinary8() is used instead, there is no improvement. Since
there is only half as much data being transferred, I had expected the
100 Msample/sec to work in this case. After trying a few optimization on
the program (aligning the buffer on a page boundary, boosting process and
thread priorities, locking the buffer receiving the card data and metadata
into physical RAM, and rewriting the loop slightly to so that more efficient
object code was generated by the compiler), there was no significant
improvement. So I became curious about what the driver was doing and
began stepping through its object code. I noticed that for each fetch, it
was doing a lot of time-consuming things such as creating mutexes before
it even got to trying to fetch the data.

Is there a more efficient way to do the fetching if I am willing to give
up some error checking regarding program correctness and guarantee that
only one process will access the card? Thanks for any information.

Jim Monte
0 Kudos
Message 1 of 14
(11,224 Views)
Hi Jim,

Beginning with the NI-SCOPE 2.8 release, we have added a new attribute that allows you to change the binary sample width, named appropriately "NISCOPE_ATTR_BINARY_SAMPLE_WIDTH". You can set this attribute to "8" and that actually changes the data that is stored into the onboard memory into 8 bit samples. Continue using the niScope_FetchBinary8 function to fetch the data from the board. You should see the bandwidth (MB/s) remain the same as you speculated.
Best Regards,
Josh
0 Kudos
Message 2 of 14
(11,216 Views)
Thanks for the reply. I am currently using version 2.7, but I
will try 2.8 after I complete testing of some things so the
same driver is used throughout. However my bigger concern is
getting access to the data more efficiently. Before looking at
what niScope_FetchBinary8() or niScope_FetchBinary16() was
doing, I was concerned about extra register loads and stores
in my fetch loop. I had expected the fetches to have a few
instructions followed by a DMA transfer to move the data.
Instead there appears to be thousands of instructions that
have nothing directly to do with the fetch. While stepping
through the call, I entered three NI DLLs and two system
DLLs, and most were entered multiple times. While I did not
step through everything, in what I did look there were several
bus locks and transitions between ring 3 and ring 0. I was hoping
that there would be a more direct way to fetch the data without
as much overhead.

Jim Monte
0 Kudos
Message 3 of 14
(11,190 Views)
I haven't gone to the lengths that Jim has, but one thing bothers me. When I load niscope.lib, that results in 48 dlls being loaded, many of which clearly have no reason for being loaded. Is there a way to control this? I can't see that it would be good for performance, and the startup time is rather long.
0 Kudos
Message 4 of 14
(11,120 Views)
What environment are you loading the niscope.lib in? How are you determining how many DLLs are loaded and whether they are related or not? Regards, Scott T Applications Engineer National Instruments
0 Kudos
Message 5 of 14
(11,065 Views)
Scott,
I am using Visual Studio 2003.  When I debug the application, the dll's that are loaded are listed in the debug window.  There are 48 dll's loaded that are directly supplied by NI, and some of these seem to cause Windows dll's to be loaded.  When I get to work tomorrow, I'll see if I can cut and paste the list.  I am only linking directly to niscope.lib, it loads the rest.


0 Kudos
Message 6 of 14
(11,075 Views)
These are the dll's that are loaded between the time I call niScope_init() and the time the scope is configured. The windows dll's seem to be related to the ni dll's that are loaded just before them. I'm programming in C++ with MFC, and there are no warnings in my compile. As you can see, there is a first chance exception in this listing
'Ultrasonic.exe': Loaded 'E:\Program Files\IVI\Bin\niscope_32.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nimhwcfu.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nimdbgu.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\niorbu.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nipalu.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nipalut.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nipal32.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\setupapi.dll', Symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nirpc.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\wsock32.dll', Symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nimstsu.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nidmxfu.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nimxdfu.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nidimu.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nimru2u.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nimxpu.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nispylog.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\mfc42.dll', Symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\Ivi.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\Program Files\IVI\Bin\IviFloat.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\Program Files\IVI\Bin\IviCShared.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\Program Files\IVI\Bin\IviConfigServerCAPI.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\clbcatq.dll', Symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\comres.dll', Symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\version.dll', Symbols loaded.
'Ultrasonic.exe': Loaded 'E:\Program Files\IVI\Bin\IviConfigServer.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\msxml4.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\msi.dll', Symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\sxs.dll', Symbols loaded.
First-chance exception at 0x77e6d756 (kernel32.dll) in Ultrasonic.exe: Microsoft C++ exception: long @ 0x0012e738.
That's not all, they all wouldn't fit in one message
0 Kudos
Message 7 of 14
(11,045 Views)
Here is the rest of the dll's being loaded, note that before niscope.dll was loaded, all the normal windows dll's have been loaded
'Ultrasonic.exe': Loaded 'E:\Program Files\National Instruments\MAX\mxs.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\Program Files\National Instruments\MAX\mxsutils.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\msvcp60.dll', Symbols loaded.
'Ultrasonic.exe': Loaded 'E:\Program Files\National Instruments\MAX\mxsout.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\Program Files\National Instruments\MAX\mxsxport.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\niini32.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nismslu.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nicfq32.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nipsm.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\niScopeDAQ2u.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nidaq32.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\niSTCu.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nibffru.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nimdsu.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\NIScale.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nimxprxu.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\nidmmu.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\visa32.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\NiViSv32.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\VXIPNP\WinNT\bin\NiVi488.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\VXIPNP\WinNT\bin\NiViAsrl.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\VXIPNP\WinNT\bin\NiViEnet.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\wininet.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\crypt32.dll', Symbols loaded.
'Ultrasonic.exe': Loaded 'E:\WINDOWS\system32\msasn1.dll', Symbols loaded.
'Ultrasonic.exe': Loaded 'E:\VXIPNP\WinNT\bin\NiViEnetAsrl.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\VXIPNP\WinNT\bin\NiViFW.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\VXIPNP\WinNT\bin\NiViGpvx.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\VXIPNP\WinNT\bin\NiViPxi.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\VXIPNP\WinNT\bin\NiViRpc.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\VXIPNP\WinNT\bin\NiViUsb.dll', No symbols loaded.
'Ultrasonic.exe': Loaded 'E:\VXIPNP\WinNT\bin\NiViVxi.dll', No symbols loaded.

Scope configured successfully
0 Kudos
Message 8 of 14
(11,039 Views)
Before answering the dll questions, let me point out a few things more explicitly:
1. At 100MS/s, the 5122 will be generating 200MB/s per channel.  No matter how optimized your program or the driver is, the PCI link is a limiting factor.  Holding on at 50MS/s means you are sustaining 100+MB/s across the PCI bus.  If you require higher sustained sampling rates, you can externally clock your device to get a sampling rate inbetween 50MS and 100MS.
2. There is no appreciable difference when using the 8bit fetch as the digitizer is still configured for storing 16bit data, and the device can only return the data raw from its internal buffers.  If you know ahead of time that you'll only be fetching 8bit data, use the Binary Sample Width attribute as mentioned previously.  With this attribute, the device will only store and return data of the specified sample width.

As far as the dlls are concerned:
1) All those DLLs are required
2) There is no way for a user to change those dependencies
3) We've tried to optimize as much as possible by rebasing them into reserved address ranges.

Thanks,
Josh

0 Kudos
Message 9 of 14
(11,007 Views)

Hi Josh,

Thanks for the reply.  Fetching one channel at 50 MS/s in a fetch-only program can be done on a sustained basis without any buffer overruns.  That sampling rate looks like it will be more than adequate for my application.  But like any useful program, it will be doing more than fetching.  I was concerned that the fetch-only program is using significant CPU power just to fetch.  The CPU usage increases at a much higher rate as the sampling rate approaches 50 MS/s.  (I have not looked into reasons for this yet.  Any ideas?)  I would like to reduce the usage so that there is more computational power available for other things.  Also, and in some ways more importantly, I am concerned about things like the numerous bus locks and mutexes.  These would increase the likelihood of an occasional long delay. (Windows is not a real-time operating system and does not specify a maximum latency.)  The occasional long delay would cause an overrun even when there would be enough capacity on the PCI bus and power in the processor to maintain a given rate on average.

Regarding the DLLs, it does seem like a large number of DLLs when all someone wants to do is fetch data from the digitizer card.  There are some advantages to just loading everything that you might use.  At least it is not necessary to test if something is loaded.  However, in general compilers do a better job if related functions are kept not only in the same DLL but also in the same file.  I think I recall seeing two different compliers in the object code (based on "style"), probably a Microsoft complier and one from Intel.  I know MS and probably Intel as well will do things like automatic inlining of functions to improve performance.  But this cannot be done across DLLs with conventional obj files.  And calls between functions in different DLLs are suprisingly awkward.  During compilation the compiler does not know where the external reference will be resolved, so you end up with jumps to areas to fixed up by the linker, which is not good at all for pipelining.

Is there a list of reserved addresses so someone does not have to view each of these DLLs to find where NI set the base addresses?  NI's work to avoid conficts when the DLLs are loaded will be thwarted by the user creating and loading a few DLLs which overlap.

 

Jim Monte

0 Kudos
Message 10 of 14
(10,997 Views)