From 04:00 PM CDT – 08:00 PM CDT (09:00 PM UTC – 01:00 AM UTC) Tuesday, April 16, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

COM library and SAFEARRAY overhead

Hi all,

I have a particular problem that seems to be an issue with labview
that I would like to know if there is a better solution for...

I have a COM DLL that requires the user to pass down a buffer, which
it fills with data and returns to the user. The function is declared
in the COM DLL as follows:

HRESULT FetchData([in, out] SAFEARRAY(float)* DataBuffer, [in] long
DesiredAmount, [out, retval] long* ReturnedAmount);

The overhead of the call itself is minimal and I have additionally
created a test DLL that just fills the buffer with known data and does
nothing else. Debugging statements within the DLL show the call taking
on the order of a few milliseconds even with pretty hefty arrays going
in. Additionally, I've tested the DLL under C++ clients and the entire
overhead of the call (including COM overhead) is still on the order of
milliseconds.

However, when this method is called within Labview, I get some odd
results. Labview shows the type of the first parameter (the SAFEARRAY)
as a Variant, not an array. I thought "no big deal", Labview wrapping
the SAFEARRAY into a Variant shouldn't take any appreciable amount of
time. As a result, it seems the only way to get at that data is to use
the Variant-to-Data block in Labview. Unfortunately, this call seems
to take a very large amount of time. The time it takes seems to
increase exponentially with the size of the array passed in. This
would be sort of expected if we were passing a SAFEARRAY containing
Variants, where each element would need to be individually converted.
However, the SAFEARRAY is declared to contain only floats. There
should only be a fixed (and very small) amount of overhead to access
that data from within the SAFEARRAY.

With this overhead of converting the data we can only seem to push a
few MB a second of data through Labview. Other programs in C++/.NET
can push easilly ten times this when calling the same COM DLL. NI
seems to be saying this is an unavoidable problem with using Variants
and COM, but as you can see our DLL isn't putting anything into
Variants.

To try to increase speed a bit I created a C DLL with the following
method:

BOOL CopySafeArrayToBuffer(VARIANT* pVar, DWORD bytesToCopy, float*
pDataBuff)

Since Labview seems put the returned SAFEARRAY into a Variant, I had
this method take a Varint directly. It simply goes into the Variant,
accesses the SAFEARRAY contained within it, and memcpy's the requested
number of bytes into the raw buffer passed in. I can directly wire
this method to the returned buffer from the COM call. Using this DLL
it improves the speed by at least 100%. I am still sure there may be
overhead passing Labview's buffer into the COM DLL to begin with, so I
may experiment with a DLL call to generate the SAFEARRAY there also to
see if that helps speed more.

Does anyone have any idea why Labview seems to incur so much overhead
when dealing with SAFEARRAYs? The actual overhead of calling the Win32
API calls to deal with them should be insignificant. As long as the
data within the SAFEARRAY is of a single simple type (not Variants) it
should be just as fast to access as a raw buffer that a C DLL returns.

Thanks for any suggestions you have!
-Eric Gross
Message 1 of 5
(3,594 Views)
There is one problem with this. SAFEARRAYS are not directly compatible with LabVIEW datatypes. So what is actually happening is that LabVIEW passes in a variant to the COM library and that takes it and puts in a SAFEARRAY. Then when you try to convert the SAFEARRAY variant into a LabVIEW array what actually happens is that LabVIEW creates a new native LabVIEW array handle and copies all the data from the SAFEARRAY variant into it and then disposes the COM variant altoghether.

There is no way LabVIEW could directly continue to use a SAFEARRAY in its diagram as the according memory management is quite different between COM objects and LabVIEW data types. So this copy is basically inavoidable due to different memory management paradigmas between COM and LabVIEW.

If you new the buffer size needed by your COM DLL beforehand and had a function where you could simply pass a LabVIEW preallocated float* array pointer into, this would be no problem at all.

Rolf Kalbermatter
Rolf Kalbermatter
My Blog
Message 2 of 5
(3,557 Views)

@rolfk wrote:
There is one problem with this. SAFEARRAYS are not directly compatible with LabVIEW datatypes. So what is actually happening is that LabVIEW passes in a variant to the COM library and that takes it and puts in a SAFEARRAY. Then when you try to convert the SAFEARRAY variant into a LabVIEW array what actually happens is that LabVIEW creates a new native LabVIEW array handle and copies all the data from the SAFEARRAY variant into it and then disposes the COM variant altoghether.

There is no way LabVIEW could directly continue to use a SAFEARRAY in its diagram as the according memory management is quite different between COM objects and LabVIEW data types. So this copy is basically inavoidable due to different memory management paradigmas between COM and LabVIEW.

If you new the buffer size needed by your COM DLL beforehand and had a function where you could simply pass a LabVIEW preallocated float* array pointer into, this would be no problem at all.

Rolf Kalbermatter




Rolf:
Thanks for your insight to the problem. I didn't see your message earlier it seems because it didn't go through the forum<->news gateway. I posted that message via the newsgroups and it went through into the NI forums fine, but your response did not come back through the gateway.

The temporary solution we had implemented was basically using a C DLL that took the Variant datatype from COM along with an array and copied the data from one to the other. The C prototype was as below:
SA_CONVERT_API BOOL CopySafeArrayToBuffer(VARIANT* pVar, DWORD bytesToCopy, float* pDataBuff)

This method worked fine, but obviously has a bit of overhead copying the data from one to the other (but it still was easily several times faster than labview's internal variantToData functions.

Now I am trying to use your suggestion of exposing a method on a Labview-specific interface that takes a raw array pointer directly without using SafeArrays. However, I ran into a bit of a problem with this...

Under C, passing an array is the same as passing a single item by reference. Labview sorts out the types by letting the labview programmer manually specify the types of all the arguments to the function. (Note that the C prototype listed as you specify the arguments will look identical whether you specify passing a single item by reference or an array of those items---its up to the programmer to know what the C DLL is expecting).

Our new COM method on this interface is shown below:
HRESULT FetchDataLV([in, out] float* DataBuffer, [in, defaultvalue(0)] long DesiredScans, [out, retval] long* ReturnedScans);

The problem is that Labview thinks the first parameter is a reference to a single item as opposed to an array. Since it is a COM method, there seems to be no way to specify the argument types as you can when calling a C DLL---everything is determined by the type library. Is there any way I can convince Labview that the first parameter is an array of floats rather than a a single float passed by reference?

Thanks for any insight you may have,
Eric Gross
0 Kudos
Message 3 of 5
(3,491 Views)


Our new COM method on this interface is shown below:
HRESULT FetchDataLV([in, out] float* DataBuffer, [in, defaultvalue(0)] long DesiredScans, [out, retval] long* ReturnedScans);

The problem is that Labview thinks the first parameter is a reference to a single item as opposed to an array. Since it is a COM method, there seems to be no way to specify the argument types as you can when calling a C DLL---everything is determined by the type library. Is there any way I can convince Labview that the first parameter is an array of floats rather than a a single float passed by reference?

Thanks for any insight you may have,
Eric Gross




The problem is the type library generation and that is done by your compiler environment. LabVIEW takes whatever the type library says. I have no experience with type libraries nor COM programming myself.

One thing I could think of is declaring your function as:

HRESULT FetchDataLV([in, out] float DataBuffer[], [in, defaultvalue(0)] long DesiredScans, [out, retval] long* ReturnedScans);

In C

float DataBuffer[] == float* DataBuffer

but the type library generator MAY make a difference on these two.

Rolf Kalbermatter
Rolf Kalbermatter
My Blog
0 Kudos
Message 4 of 5
(3,475 Views)

@rolfk wrote:
The problem is the type library generation and that is done by your compiler environment. LabVIEW takes whatever the type library says. I have no experience with type libraries nor COM programming myself.

One thing I could think of is declaring your function as:

HRESULT FetchDataLV([in, out] float DataBuffer[], [in, defaultvalue(0)] long DesiredScans, [out, retval] long* ReturnedScans);

In C

float DataBuffer[] == float* DataBuffer

but the type library generator MAY make a difference on these two.

Rolf Kalbermatter




Thanks for the great suggestion Rolf. I didn't think the MIDL compiler would make a difference between the two notations, but sure enough it does. It retains the array ([]) notation in the compiled type library and shows up using that notation in COM object browsers. I thought for sure Labview would now realize I am trying to pass an array now...

Unfortunately, it still shows the same behavior as before and thinks I am passing a single float by reference. Any more ideas? I have to believe there is some way we can efficiently pass data to/from a COM DLL....

Thanks again for all your help,
Eric Gross
0 Kudos
Message 5 of 5
(3,455 Views)