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;
}