04-12-2011 02:46 AM
I was trying to profile application fetching data from NI 5124 PXI digitizer.
Starting from example code in BinaryAcquisition and using VisualStudio 2008 C++ Net Express edition I made test application
and measured the time spent in niScope_FetchBinary16 called in loop.
The result is strange to me. I have summarized it in table.
Sample rate (Sr) || Namber of point to fetch (N) || Averaged time spent in function (T), ms || theoretical time of scan = N/Sr, ms
200 Ms/s || 2000 || 0.22 ms || 0.01ms
20 Ms/s || 2000 || 0.22 ms || 0.1ms
2 Ms/s || 2000 || 8.9 ms || 1 ms
200 Ks/s || 2000 || 19.1 ms || 10 ms
20 Ks/s || 2000 || 109.78 ms || 100ms
It looks to me that difference of two times ( T - N/Sr ) should be approximately same for all sampling rates because number of points N is constant.
That contradicts with measured results.
Can some body reproduce these measurements / explain results to me?
The following is code I have used:
// test.cpp : main project file. #include "stdafx.h" #include <string> #include "niScope.h" #define MAX_STRING_SIZE 100 using namespace System; ViStatus _VI_FUNC niScope_GenericBinaryAcquisition (void); int main(array<System::String ^> ^ /*args*/) { Console::WriteLine(L"Start Test niScope_GenericBinaryAcquisition"); System::Diagnostics::Stopwatch^ stopwatch = gcnew System::Diagnostics::Stopwatch(); stopwatch->Stop(); stopwatch->Reset(); stopwatch->Start(); ViSession vi; // Variables used to get values from the GUI ViChar resourceName[MAX_STRING_SIZE] = "Dev1"; ViChar channelName[MAX_STRING_SIZE] = "0"; ViChar ansiOptionString[MAX_STRING_SIZE] = "Simulate=0,RangeCheck=1,QueryInstrStatus=1,Cache=1"; ViReal64 verticalRange = 10.0; ViReal64 verticalOffset = 0.0; ViReal64 minSampleRate = 200000000.0; ViInt32 minRecordLength = 2000; ViInt32 binaryDataType = 2; // bin16 ViReal64 refPosition = 50.0; ViReal64 timeout = 15.0; // seconds ViInt32 numWaveform; ViInt32 actualRecordLength; ViReal64 actualSampleRate; // Waveforms struct niScope_wfmInfo * wfmInfoPtr = NULL; ViReal64 * scaledWfmPtr = NULL; void * binaryWfmPtr = NULL; // Open the NI-SCOPE instrument handle if( VI_SUCCESS != niScope_InitWithOptions( resourceName, NISCOPE_VAL_FALSE, NISCOPE_VAL_FALSE, ansiOptionString, &vi )) return 0; // Configure the vertical parameters if( VI_SUCCESS != niScope_ConfigureVertical( vi, channelName, verticalRange, verticalOffset, NISCOPE_VAL_DC, 1.0, NISCOPE_VAL_TRUE ) ) return 0; // loop over sampling rate for(int i = 0; i < 10; i++) { Int64 mss1; Int64 tiks1; Int64 mss2; Int64 tiks2; Int64 dmss = 0; Int64 dtiks = 0; String^ mess; // Configure the horizontal parameters if( VI_SUCCESS != niScope_ConfigureHorizontalTiming( vi, minSampleRate, minRecordLength, refPosition, 1, VI_TRUE ) ) return 0; // Configure the trigger if( VI_SUCCESS != niScope_ConfigureTriggerImmediate( vi ) ) return 0; // Find out the number of waveforms, based on # of channels and records if( VI_SUCCESS != niScope_ActualNumWfms( vi, channelName, &numWaveform ) ) return 0; // Query the coerced record length if( VI_SUCCESS != niScope_ActualRecordLength( vi, &actualRecordLength ) ) return 0; // Query the coerced record length if( VI_SUCCESS != niScope_SampleRate( vi, &actualSampleRate ) ) return 0; // Allocate space for the waveform ,waveform info, and binary waveform // according to the record length and number of waveforms wfmInfoPtr = (struct niScope_wfmInfo*) malloc (sizeof (struct niScope_wfmInfo) * numWaveform); binaryWfmPtr = (void*) malloc (binaryDataType * actualRecordLength * numWaveform); scaledWfmPtr = (ViReal64*) malloc (sizeof (ViReal64) * actualRecordLength * numWaveform); // Check is memory allocations succeeded. if ( scaledWfmPtr == NULL || wfmInfoPtr == NULL || binaryWfmPtr == NULL ) return 0; mess = L"S/s: " + actualSampleRate.ToString() + L"; N: " + actualRecordLength.ToString(); Console::WriteLine( mess ); // 50 repetitions of calls to niScope_FetchBinary16 function for(int j=0; j<50; j++) { // Initiate the acquisition if( VI_SUCCESS != niScope_InitiateAcquisition( vi ) ) return 0; mss1 = stopwatch->ElapsedMilliseconds; tiks1 = stopwatch->ElapsedTicks; mess = L"\t" + mss1.ToString() + L"ms" + L" (" + tiks1.ToString() + L")"; //Console::WriteLine( mess ); // Fetch the data according to the obtained data type // Use the 16 bit fetching function if( VI_SUCCESS != niScope_FetchBinary16( vi, channelName, timeout, actualRecordLength, (ViInt16*) binaryWfmPtr, wfmInfoPtr ) ) return 0; mss2 = stopwatch->ElapsedMilliseconds; tiks2 = stopwatch->ElapsedTicks; mess = "\t" + mss2.ToString() + L"ms" + L" (" + tiks2 + L")"; //Console::WriteLine( mess ); dmss = dmss + mss2 - mss1; dtiks = dtiks + tiks2 - tiks1; } mess = " Delta: " + dmss/50.0+ L"ms" + L" (" + dtiks/50.0 + L")"; Console::WriteLine( mess ); minSampleRate = minSampleRate / 10; } // Close the session if (vi) niScope_close (vi); return 0; }
04-13-2011 01:14 AM
I have recompiled the test application as win32 console application using VisualStudio 2008 C++ Express edition.
The results are same.
The spent in fetching function time smaller by order for 20 Ms/s sampling rate than for 2 Ms/s.
If some body has ideas why is that share it with me please.
Sample rate (Sr) || Number of points to fetch (N) || Averaged time spent in function (T), ms || theoretical time of scan = N/Sr, ms
200 Ms/s || 2000 || 0.25 ms || 0.01ms
20 Ms/s || 2000 || 0.22 ms || 0.1ms
2 Ms/s || 2000 || 9.95 ms || 1 ms
200 Ks/s || 2000 || 20.02 ms || 10 ms
20 Ks/s || 2000 || 110.92 ms || 100ms
The code of test is following (this forum rejects cpp code to be attached).
// ltdtestwin32.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include "niScope.h"
#include "Windows.h"
#define MAX_STRING_SIZE 100
int _tmain(int argc, _TCHAR* argv[])
{
std::cout << "Start Test niScope" << std::endl;
ViSession vi;
// Variables used to get values from the GUI
ViChar resourceName[MAX_STRING_SIZE] = "Dev1";
ViChar channelName[MAX_STRING_SIZE] = "0";
//ViChar ansiOptionString[MAX_STRING_SIZE] = "Simulate=1,DriverSetup=Noise:4.0;Model:5124;BoardType:PXI;";
ViChar ansiOptionString[MAX_STRING_SIZE] = "Simulate=0,RangeCheck=1,QueryInstrStatus=1,Cache=1";
ViReal64 verticalRange = 10.0;
ViReal64 verticalOffset = 0.0;
ViReal64 minSampleRate = 200000000.0;
ViInt32 minRecordLength = 2000;
ViInt32 binaryDataType = 2; // bin16
ViReal64 refPosition = 50.0;
ViReal64 timeout = 15.0; // seconds
ViInt32 numWaveform;
ViInt32 actualRecordLength;
ViReal64 actualSampleRate;
// Waveforms
struct niScope_wfmInfo * wfmInfoPtr = NULL;
ViReal64 * scaledWfmPtr = NULL;
void * binaryWfmPtr = NULL;
// Open the NI-SCOPE instrument handle
// niScope_init( resourceName, NISCOPE_VAL_FALSE, NISCOPE_VAL_FALSE, &vi );
if( VI_SUCCESS != niScope_InitWithOptions( resourceName, NISCOPE_VAL_FALSE, NISCOPE_VAL_FALSE, ansiOptionString, &vi ))
return 0;
// Configure the vertical parameters
if( VI_SUCCESS != niScope_ConfigureVertical( vi, channelName, verticalRange, verticalOffset, NISCOPE_VAL_DC, 1.0, NISCOPE_VAL_TRUE ) )
return 0;
LARGE_INTEGER tiks1;
LARGE_INTEGER tiks2;
LARGE_INTEGER dtiks;
//calibrate QueryPerformanceCounter()
int num_of_ms = 1000;
QueryPerformanceCounter( &tiks1 );
Sleep( num_of_ms );
QueryPerformanceCounter( &tiks2 );
dtiks.QuadPart = tiks2.QuadPart - tiks1.QuadPart;
double tiks_per_ms = 1.0 * dtiks.QuadPart / num_of_ms;
int num_of_rep = 100;
for(int i = 0; i < 10; i++)
{
// Configure the horizontal parameters
if( VI_SUCCESS != niScope_ConfigureHorizontalTiming( vi, minSampleRate, minRecordLength, refPosition, 1, VI_TRUE ) )
return 0;
// Configure the trigger
if( VI_SUCCESS != niScope_ConfigureTriggerImmediate( vi ) )
return 0;
// Find out the number of waveforms, based on # of channels and records
if( VI_SUCCESS != niScope_ActualNumWfms( vi, channelName, &numWaveform ) )
return 0;
// Query the coerced record length
if( VI_SUCCESS != niScope_ActualRecordLength( vi, &actualRecordLength ) )
return 0;
// Query the coerced record length
if( VI_SUCCESS != niScope_SampleRate( vi, &actualSampleRate ) )
return 0;
// Allocate space for the waveform ,waveform info, and binary waveform
// according to the record length and number of waveforms
wfmInfoPtr = (struct niScope_wfmInfo*)
malloc (sizeof (struct niScope_wfmInfo) * numWaveform);
binaryWfmPtr = (void*) malloc (binaryDataType * actualRecordLength * numWaveform);
scaledWfmPtr = (ViReal64*) malloc (sizeof (ViReal64) * actualRecordLength * numWaveform);
// Check is memory allocations succeeded.
if ( scaledWfmPtr == NULL || wfmInfoPtr == NULL || binaryWfmPtr == NULL )
return 0;
std::cout << "S/s: " << actualSampleRate << "; N: " << actualRecordLength << std::endl;
dtiks.QuadPart = 0;
for(int j=0; j<num_of_rep; j++)
{
// Initiate the acquisition
if( VI_SUCCESS != niScope_InitiateAcquisition( vi ) )
return 0;
QueryPerformanceCounter( &tiks1 );
// Fetch the data according to the obtained data type
// Use the 16 bit fetching function
if( VI_SUCCESS != niScope_FetchBinary16( vi, channelName, timeout, actualRecordLength, (ViInt16*) binaryWfmPtr, wfmInfoPtr ) )
return 0;
QueryPerformanceCounter( &tiks2 );
//std::cout << " t1: " << tiks1.QuadPart << " t2: " << tiks2.QuadPart << std::endl;
dtiks.QuadPart = dtiks.QuadPart + tiks2.QuadPart - tiks1.QuadPart;
}
std::cout << " Delta: " << dtiks.QuadPart / tiks_per_ms / num_of_rep << " ms" << std::endl;
minSampleRate = minSampleRate / 10;
}
// Close the session
if (vi)
niScope_close (vi);
return 0;
}