LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

CIN readback of an array given its pointer -- possible without copying?

>but why not simply allocate the memory for the framegrabber by using LabVIEW's initialize array primitive? Then, when the data is returned, simply index into the array to find the data of interest.

ah, and besides: I want to allocate once the buffer(s) and to write there at video speed. Not to reallocate and initialize them at every call of the grabbing routine. This is what my "working solution" does currently, using some 10 ms on a fast PC (profile). Given that a new video field is available every 20 ms, and that I'd like to use cpu for online image processing, I'm trying to squeeze.

Enrico
0 Kudos
Message 11 of 25
(1,931 Views)


@enrico Segre wrote:
>but why not simply allocate the memory for the framegrabber by using LabVIEW's initialize array primitive? Then, when the data is returned, simply index into the array to find the data of interest.

ah, and besides: I want to allocate once the buffer(s) and to write there at video speed. Not to reallocate and initialize them at every call of the grabbing routine. This is what my "working solution" does currently, using some 10 ms on a fast PC (profile). Given that a new video field is available every 20 ms, and that I'd like to use cpu for online image processing, I'm trying to squeeze.

Enrico




Ahh, here comes the monkey out of the jacket. You can't do that! Once you want to read the data in LabVIEW itself IT HAS TO BE in a LabVIEW data handle, which LabVIEW will deallocate whenever it pleases. You simply can't operate on external data pointers with LabVIEW functions and nodes.
Only C gives you this amount of control over memory (because it also requires you to worry all the time about proper memory allocation/deallocation).

Rolf Kalbermatter
Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 12 of 25
(1,927 Views)
>You can't do that! Once you want to read the data in LabVIEW itself IT HAS TO BE in a LabVIEW data handle, which LabVIEW will deallocate whenever it pleases.

If there is no way there is no way, and that closes the issue, then.... It was not obvious to me from all I was able to extract from the documentation. But isn't there a way to allocate a Labview data handle AND to use the resulting pointer? To beg labview for not deallocating it (see my reply #7 above)?
Thanks for clarifying, Enrico
0 Kudos
Message 13 of 25
(1,925 Views)
Hi Enrico,

Your problem intrigued me, so I took 45 minutes today to look into it some more. Attached is a VI and a DLL which I wrote. The DLL has three functions and replicates your described functionality. CreateBuffer creates a "handle" (just a pointer to a structure, in this case) and stores in that handle information about a buffer passed into it. FillBuffer writes data into the previously stored buffer, and Free just deletes the structure.

The VI allocates a buffer using Initialize Array and passes it to the CreateBuffer function. After calling FillBuffer, the output is displayed on the FP. The additional functions which waste memory and perform other calculations are just there to be hard on the system and try to get it to "misbehave."

However, it seems to work perfectly.

One thing that comes to mind, which may be why your previous attempt to work it this way failed. If I had branced the array between initialize and the CreateBuffer call, and attempted to read that array, there would be no data in it. This is because the DLL call copies the data before sending into the DLL function IF the output terminal is wired (as it is, in this case).

Let me know if you see something substantially different in my code than in your situation, or if this code doesn't work for you.

Best of luck,
Jason
0 Kudos
Message 14 of 25
(1,899 Views)
I don't think that this would work for the OP. First of all the pointer you pass in CreateBuffer to the DLL is only valid as long as the array wire is valid and not branched anywhere at all. Basically the pointer is deallocated (in fact reused here) at the moment the Array Subset function is executed and definitely deallocated in the terminal. Also reused does not necessarily mean that the pointer does not change, as LabVIEW might decide to actually resize the handle in Array Subset and this could theoretically cause the array to be reallocated resulting in a different pointer value. While for this specific case it is unlikely that this is done, you would be relying on a specific behaviour, which can change with any new LabVIEW version for a number of reasons.

Another issue is that the capture library will likely fill the buffer at some asynchronous time and you need to add additional protection to avoid letting LabVIEW work on the array while the grabber tries to update the buffer content.

I still think it would be possible by using my earlier approach. To give at least some idea what I mean, I made a possible skeletton (not tested, nor even compiled but it is a starting point):

typedef struct
{
int32 len;
uInt8[0];
} MyArray, **MyArrayHdl;

MyArrayHdl gHandle = NULL;

MgErr ExchangeHandle(Refnum yourDevice, MyArrayHdl *handle)
{
int32 len = CalculateRequiredSize(yourDevice);
MyArrayHdl temp;

if (!handle || !*handle || (**handle)->len < len)
{
err = NumericArrayResize(uB, 1, handle, len);
if (err)
return err;
(**handle)->len = len;
}

/* Store the handle somewhere the capture function can get at. We also need
additional code here to protect from swapping the handle/pointer under
the nose of the capture function. How that is to be implemented is an
exercise very dependant on the actual external library to interface to. */
temp = gHandle;
gHandle = *handle;
TriggerDevice(yourDevice, gHandle);

/* On the first execution, create a new handle to pass back to LabVIEW
otherwise return the now hopefully filled in buffer. */
if (temp)
*handle = temp;
else
*handle = DSNewHdlClr(sizeof(int32));
return noErr;
}

This does create a new buffer for every image to be returned to LabVIEW but that
is simply something you can absolutely not avoid. If you try to use a single
pointer you will have to copy the data at the moment you want to return the
data to a LabVIEW diagram, but this approach would save at least this copying
of the data. It is basically a simple double buffered approach used here.

Rolf Kalbermatter

Message Edited by rolfk on 06-19-2005 05:49 PM

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
0 Kudos
Message 15 of 25
(1,901 Views)

@rolfk wrote:
I don't think that this would work for the OP. First of all the pointer you pass in CreateBuffer to the DLL is only valid as long as the array wire is valid and not branched anywhere at all. Basically the pointer is deallocated (in fact reused here) at the moment the Array Subset function is executed and definitely deallocated in the terminal.


Actually, the buffer passed to CreateBuffer isn't used at all. It is the copy that is created inside the call that is used. Secondly, simply branching an array does not cause any buffer manipulation to happen. One of the branches must be modified (say with a replace array subset element). Then, the buffer will be copied, not deallocated.


Also reused does not necessarily mean that the pointer does not change, as LabVIEW might decide to actually resize the handle in Array Subset and this could theoretically cause the array to be reallocated resulting in a different pointer value. While for this specific case it is unlikely that this is done, you would be relying on a specific behaviour, which can change with any new LabVIEW version for a number of reasons.


Again, I disagree. The Array Subset function allocates a new buffer, yes. However, LabVIEW cannot deallocate the original buffer because it doesn't know what will be done with it on future iterations. As far as LabVIEW is concerned, the original buffer is allocated (in the DLL call) and continuously read (in the loop), but never modified. Since it is in continual use it cannot be deallocated. Since it is not resized, it will not be moved.

Because it is ultimately a handle, it is possible that LabVIEW could move the entire buffer for reasons other than resizing it. However, the only reason I can come up with to do that is some sort of "memory defragmentation" operation in order to clear up a sufficiently large block of memory. As this would generally be a terrible inefficiency, however, I doubt that LabVIEW does this. Furthermore, with swapspace being plentiful, it's rare that programs run out of memory these days. They just get slower and slower and slower :). Actually, you can see this behavior in my example if you turn on the memory waster. Incidently, the main purpose of the memory waster was to see if LabVIEW would move the original block at all. As far as I can tell, it doesn't.

Ultimately, you are correct when you say that this relies on a behavior of LabVIEW that might change. The LabVIEW developers might decide to start moving allocated buffers around for some reason other than resizing them. I doubt it, but the might. Were it my code, I'd take the chance. particularly seeing as how this is the approach that Greg McKaskle suggested in the parallel thread that Enrico mentioned a few messages ago.


Another issue is that the capture library will likely fill the buffer at some asynchronous time and you need to add additional protection to avoid letting LabVIEW work on the array while the grabber tries to update the buffer content.

Yes, but that's the synchronization issue that can be handled with occurences, or whatever. Different topic.
0 Kudos
Message 16 of 25
(1,902 Views)

@Jason S wrote:
Actually, the buffer passed to CreateBuffer isn't used at all. It is the copy that is created inside the call that is used.


Ohh, and how it is used!

(*ppData)->buffer = pBuffer;

This assigns the pointer in the handle created on the LabVIEW diagram as a member to your "handle" structure. No data buffer is created inside the DLL call at all, only your handle of 12 bytes. Since the wire going into the Call Library Node does not branch, LabVIEW will simply pass in the internal pointer of that handle to the DLL function as an optimization. And since the output terminal of the Call Library Node is wired LabVIEW also will assume that the actual array pointer might have been modified in the DLL function but it will simply reuse the handle here too.

Secondly, simply branching an array does not cause any buffer manipulation to happen. One of the branches must be modified (say with a replace array subset element). Then, the buffer will be copied, not deallocated.


This very much will depend on the functions connected to the wire. And LabVIEW functions will in general either reuse the buffer (of course only one can reuse an actual buffer and Replace Array is indeed such a function) or copy the data. No need to copy the date if a buffer is reused (other than if buffers are resized which might result in a reallocation of the handle at a different address).

The actual Array Subset function used in your example for instance will reuse the array most definitely. Just enable the Tools->Advanced->Show Buffer Allocations tool! This is because the array is not used anywhere else and also because the actual offset of 0 you are using makes it the ideal canditate for reuse. If there is no function which can reuse the buffer (such as only one or more index array functions) the entire array would be simply deallocated altogether rendering your pointer in your handle invalid.
What happens here (at least for LabVIEW 7.1) is that the buffer is actually reused in Array Subset and resized but since the resizing is to a smaller size no reallocation is made. Then LabVIEW copies the array into the data buffer of the front panel control. Now comes a particular optimization which is most probably only present in recent versions. The same array just having been resized to a smaller size is again reused in the next loop iteration with its original size as the wire coming in from the left. Honestly I don't think that your example would work in earlier LabVIEW versions such as 6.0 or 6.1 as it does here. At least the Array Subset function would create an extra copy of the data.

Again, I disagree. The Array Subset function allocates a new buffer, yes.

No it doesn't, as is shown by the Show Buffer Allocation Tool. However recent changes to the optimization in LabVIEW make it seem to be able to reause even that resized buffer for the original array again.

Rolf Kalbermatter
Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 17 of 25
(1,894 Views)
However, LabVIEW cannot deallocate the original buffer because it doesn't know what will be done with it on future iterations. As far as LabVIEW is concerned, the original buffer is allocated (in the DLL call) and continuously read (in the loop), but never modified. Since it is in continual use it cannot be deallocated. Since it is not resized, it will not be moved.

Why do you think you allocated the buffer in the DLL? You only allocated the "handle" which maintains the pointer inside the array handle created on the diagram. Even when you would have been trying to overwrite the assignment operator in C++ to actually create a copy of the pBuffer this wouldn't work as the C++ compiler has no way of knowing how big the array to copy would be at runtime. And for LabVIEW is the call to CreateBuffer simply history so that won't be used in the buffer reallocation/optimization strategy at all. Another way you might possibly try to get the handle to survive in a loop through multiple iterations would be to wire it into a shift register.
Still this is not something you could rely upon as LabVIEW is still free to decide that it will rather reuse the handle in the Array Subset function instead of in the shift register (unlikely nowadays but certainly a very likely situation in earlier LabVIEW version and still absolutely true if you would use a Feeback node instead of a shift register eventhough they are supposed to have equivalent functionality, except that the LabVIEW developers "forgot" to apply the same optimization strategies as they use for shift registers.
Because it is ultimately a handle, it is possible that LabVIEW could move the entire buffer for reasons other than resizing it. However, the only reason I can come up with to do that is some sort of "memory defragmentation" operation in order to clear up a sufficiently large block of memory. As this would generally be a terrible inefficiency, however, I doubt that LabVIEW does this.

LabVIEW never has reassigned DS handles ever on its own, only AZ handles. DS handles are guranteed to stay locked as long as no explicit resizing is performed on them, but it is very difficult to assert when and how an array is resized/reused in a LabVIEW diagram and basically not something you can assume to stay the same across LabVIEW versions.

Your example works since LabVIEW does some remarkable optimizations in a way which happen to benefit your particular setup. But for more complex diagrams it would be very hard to guarantee this same behaviour and you would end up with a situation where it either does not avoid the additional copy of the data in the Array Subset function or renders the buffer at latest in the Array Subset function useless for the DLL. It's either one or the other but you can't have both except in the limited example created by you and with a very high uncertainety about in which LabVIEW version this will break anyhow.

Rolf Kalbermatter
Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 18 of 25
(1,899 Views)

@rolfk wrote:
No data buffer is created inside the DLL call at all...

I must sheepishly admit that I did not check the buffer allocations on the final version of the VI. In a previous version, a buffer was allocated by LabVIEW on the DLL node, and that is the allocation to which I was refering. I'm not sure what the difference between this version and the previous was that caused LabVIEW to decide it didn't need to copy the buffer before sending it off to the DLL.
The same array just having been resized to a smaller size is again reused in the next loop iteration with its original size as the wire coming in from the left. ... At least the Array Subset function would create an extra copy of the data.

You're correct about the optimization of the Array Subset, except for in one critical detail. In terms of actual memory, the array is not resized at Array Subset. If LabVIEW were to resize it here, it would risk having the rest of the buffer wiped out be some other unrelated memory call. It can't do that because it's going to need the entire original array at the beginning of the next iteration of the loop. That is the critical part of the entire example. Because the unmodified array (unmodified as far as LabVIEW knows, that is) must be available at the beginning of every iteration of the loop, it will not be disturbed. If it were moved, LabVIEW would have to copy it in its entirity, which would be an unnecessary waste of time in every instance I can imagine.

@rolfk wrote:

Another way you might possibly try to get the handle to survive in a loop through multiple iterations would be to wire it into a shift register.
Still this is not something you could rely upon as LabVIEW is still free to decide that it will rather reuse the handle in the Array Subset function instead of in the shift register...

See previous statements about same data be necessary at beginning of loop, thereby good optimization ensuring that it is the modified buffer, not the "unmodified" buffer that gets moved.

I guess I was also implicitly assuming that Enrico was just reading the data, and not modifying it in any way, in which case there are no worries at all. Well, a lot fewer. He seems to have lost interest in our little discussion, anyway. Sigh.

Your example works since LabVIEW does some remarkable optimizations in a way which happen to benefit your particular setup. But for more complex diagrams it would be very hard to guarantee this same behaviour and you would end up with a situation where it either does not avoid the additional copy of the data in the Array Subset function or renders the buffer at latest in the Array Subset function useless for the DLL.

My example works because I rely on LabVIEW not to move what it thinks is an unmodified buffer while it is still in use. LabVIEW does have some impressive optimizations, but that's hardly one of them. The buffer copy, or lack thereof, in the Array Subset is irrelevent.

If you can prove me wrong, please do so. Please edit my VI, or create an equivelent that breaks. The critical things that must not change are:


  1. The wire that is passed out of the CreateBuffer function must be wired to the "data acquisition" loop directly and with no branches.
  2. This same wire must be accessed, in some fashion, to read or manipulate the data.


I assert that any manipulation of the data inside the loop will leave the original buffer unmoved. It will certainly work everytime in the current version of LabVIEW. I believe that it will work as far back as 5.0 (that's as far as my experience goes), and I am confident that it will continue to work in the future, though I wouldn't sell an application that relied on it.

I'm not trying to be rude or unduly challenging. I just think that my method will work, and for my own benefit and those who read this forum, would be interested to know if I'm wrong.
Message 19 of 25
(1,889 Views)
I must sheepishly admit that I did not check the buffer allocations on the final version of the VI. In a previous version, a buffer was allocated by LabVIEW on the DLL node, and that is the allocation to which I was refering. I'm not sure what the difference between this version and the previous was that caused LabVIEW to decide it didn't need to copy the buffer before sending it off to the DLL.

Which would prove my point that this method is rather unreliable in terms of guarantees that a particular behaviour can be enforced across more or less complex diagrams or LabVIEW versions 🙂
You're correct about the optimization of the Array Subset, except for in one critical detail. In terms of actual memory, the array is not resized at Array Subset. If LabVIEW were to resize it here, it would risk having the rest of the buffer wiped out be some other unrelated memory call. It can't do that because it's going to need the entire original array at the beginning of the next iteration of the loop. That is the critical part of the entire example. Because the unmodified array (unmodified as far as LabVIEW knows, that is) must be available at the beginning of every iteration of the loop, it will not be disturbed. If it were moved, LabVIEW would have to copy it in its entirity, which would be an unnecessary waste of time in every instance I can imagine.

Yes, it is not really resized but LabVIEW does modify the (*handle)->len value inside the handle in the Array Subset function to make sure the FP control only gets to see the data it needs to see but then is smart enough to reset this size again in order to be able to reuse the same buffer in the next iteration. While you may feel this is logical and anything else would be stupid I find this a remarkable piece of optimization which almost certainly wasn't present in earlier versions maybe even in 6.x but quite sure in 5.x. I don't say your method wouldn't seem to work in those versions, but in order to be able to reuse the array buffer in the next iteration LabVIEW would simply have created a new array buffer in Array Subset and copied all the data necessary, only to create even a new copy for the front panel control in really old versions.
My example works because I rely on LabVIEW not to move what it thinks is an unmodified buffer while it is still in use. LabVIEW does have some impressive optimizations, but that's hardly one of them. The buffer copy, or lack thereof, in the Array Subset is irrelevent.

Well it is not irrelevant as far as the original poster was concerned, since it was exactly this copying of data he wanted to avoid. His earlier method did work, but was to slow since he had to take the pointer which was filled in by his external Library and copy its contents into a LabVIEW handle to be used on the diagram. He felt that it was stupid that the capture function was copying the data into the pointer buffer and that he had then to copy the data again into a LabVIEW handle. The only way to avoid this for sure and without much guesswork about LabVIEW version behaviour, would be to use a double buffer scheme as I have shown in an earlier post.

  1. The wire that is passed out of the CreateBuffer function must be wired to the "data acquisition" loop directly and with no branches.
  2. This same wire must be accessed, in some fashion, to read or manipulate the data.

I assert that any manipulation of the data inside the loop will leave the original buffer unmoved. It will certainly work everytime in the current version of LabVIEW. I believe that it will work as far back as 5.0 (that's as far as my experience goes), and I am confident that it will continue to work in the future, though I wouldn't sell an application that relied on it.

Basically you sum it all up. That are quite some conditions and limitations you will have to keep in mind all the time during development and later maintenance. Also you do know these things and are familiar with C and memory management but most other LabVIEW users have little notice of these things and this example is some black magic to them, nor can they understand easily that a single branch in the array wire might completly break it.
I'm not trying to be rude or unduly challenging. I just think that my method will work, and for my own benefit and those who read this forum, would be interested to know if I'm wrong.

It will work in all recent LabVIEW versions provided you observe above mentioned rules and limits. But performance may vary due to more or less optimized LabVIEW buffer allocations.
And I didn't feel you were rude or something, just somewhat challenging, but that is fine with me 😉 It's not every day that you can have a discussion about such topics here on Developer Exchange.

Rolf Kalbermatter
Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
0 Kudos
Message 20 of 25
(1,880 Views)