LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Preallocate array memory without initializing it

Solved!
Go to solution

Thanks to everyone for your answers. I will try to explain myself better.

I have an application that receives every second a bucket (1D array) of variables. All variables get acquired at the same frequency, so this length is fixed (86). Then, let's imagine that we have to stack 3600 buckets (1 hour). After that, the stack will be flushed for further analysis. The point is that depending on some parameters the read/flush of the stack can be done earlier. So it's the length of the stack that is variable, and not the “width”(86). So, the 2D format fits perfectly.

Then also I have to store the analyzed data. For example, storing the 1h averages from 1 year ago. This will be a 87600×86 buffer that when the first year is reached, the oldest value has to be removed from one extreme and the new added at the other one.

To meet this needs, as some as you said, I made it with queues, preallocating the memory with the width and the maximum length. The problem is that the read and flush operation was a bit slow because the output was in array of cluster of array format, while the analysis VIs expects the data in 2D format. The process of format change can take some time for large buffers, and it's a useless operation because all the rows have the same length.

So during this day I have tried to do the same with arrays to save the format change time. Why? Because with the same code that receives the data, I will have to reprocess 1 year of data with tens of millions of data buckets, and in the code there are several buffers that will slow down the reprocess.

I upload the code (the old with queues, the new with arrays and the benchmarking). Everything works good except the update mode(the equivalent to a lossy enqeue). I can't do it without creating a copy of the data or reallocating memory, which completely slows down the execution. (If you test the benchmarking code in update mode you will notice).

I have never worked with circular buffers, but I think that it won't be the solution because the analysis code needs an array of the correct length, and I think that a circular buffer will return a constant length array.

If something isn't clear enough yet, make me know. Also, make me know if you see other innefficiencies in the code.

Thanks for your time,

EMCCi

0 Kudos
Message 11 of 36
(1,745 Views)

I apologize that I don't completely understand your VI or what you are trying to do.

 

What I do understand is that you have memory problems. It looks like there are lots of areas in your code where buffer copies are being made. For example, "Delete from Array" makes a copy. Try to use Array Subset or Index Array instead. Rather than make a new array, try using the reshape array function, you may have a copy, but you may not.

 

Play the game "Wack the dots", look at the buffer allocations and try to reduce. Lastly, just because you flush a queue does not necessarily mean its memory is now free. You can try passing references of DVRs around instead.

 

mcduff

Message 12 of 36
(1,729 Views)

Hello mcduff,

 

Where are the parts that you don't understand?

 

It is the "Delete from Array" function of the update mode where I need help. I have tried to do it other ways but haven't achieve to make it without reallocating memory.

 

This way, if you run the benchmarking for 100k points with the update mode on the array buffers, the code will dramatically slow down when it reaches the 50k points (the preallocated memory size). Because the "updating" code (lossy enqueue) isn't working properly.

 

Also, I don't want to free memory. I want to keep it allocated for future writes.

 

Thanks for your time.

0 Kudos
Message 13 of 36
(1,723 Views)

Here is something quick that appears faster with less memory, this replaces your ABuffer Write VI. Different versions of LabVIEW, OSes, etc, your mileage may vary.

 

mcduff

snip.png

 

 

 

Message 14 of 36
(1,713 Views)
Solution
Accepted by topic author EMCCi

I could not run your code because of a missing subVI, but to emphasise what others have said you need to look into Array Subset.  You are using Delete From Array as a low-performance version of Array Subset.  Have look at what Weibe said about cyclic buffers being either slow on write or slow on read; I think your doing slow on Write by always keeping the array in order, rather than wrapping around.   I also thought the extra layer of "Action-Engine Clone VIs" was redundant and will slow you somewhat.  

 

Couldn't run your benchmark VI, but I ran a similar one for a Circular Buffer I have, in order to give you a comparison.  Set to 100x100k DBLs, I did 1M inserts of 100 DBLs in about 750 ms, or 750 ns/insert.  A read of the 100x100k array was about 50 ms.  

Message 15 of 36
(1,685 Views)
Solution
Accepted by topic author EMCCi

@EMCCi wrote:

I have never worked with circular buffers, but I think that it won't be the solution because the analysis code needs an array of the correct length, and I think that a circular buffer will return a constant length array.


There isn't an exact definition of a circular buffer. But although what you want doesn't seem to be circular at all, the code will be mostly the same. When a circular buffer is full, it overwrites the buffer from the start. You simply want to stop filling. That's minor.

 

Reading from the buffer can be done any way you want. If you fill 100 samples of the 1000 samples sized buffer, just get the 100 samples subset.

 

A big choice is whether you want this buffer to be by reference using a FGV, DVR, Queue, etc., or by value (in place element structures). Another choice is if it should be global (or a 'singleton'), or by wire. By ref\by wire is mutually exclusive with instance\singleton. Both will effect the buffer in it's core.

 

As I see it the 1 year buffer is exactly the same as the 1 hour buffer. If the buffer isn't a singleton, you should be able to simply make two instances of the same object. If it's  singleton, you need either a copy of the code or a complicating indexing mechanism.

Message 16 of 36
(1,648 Views)

Yes I think you are right. I think that I have to add the circular buffer method to do the "update"(lossy enqueue) mode without reallocating memory. I will try it today.

 

Thank you so much!

0 Kudos
Message 17 of 36
(1,632 Views)

wiebe@CARYA wrote:

 

A big choice is whether you want this buffer to be by reference using a FGV, DVR, Queue, etc., or by value (in place element structures). Another choice is if it should be global (or a 'singleton'), or by wire. By ref\by wire is mutually exclusive with instance\singleton. Both will effect the buffer in it's core.

 


I would suggest just doing a by-value buffer, as it is easy to then use this inside a DVR or other by-reference method of one's choice (including the possibility of a singleton).

0 Kudos
Message 18 of 36
(1,620 Views)

@drjdpowell  ha escrito:

wiebe@CARYA wrote:

 

A big choice is whether you want this buffer to be by reference using a FGV, DVR, Queue, etc., or by value (in place element structures). Another choice is if it should be global (or a 'singleton'), or by wire. By ref\by wire is mutually exclusive with instance\singleton. Both will effect the buffer in it's core.

 


I would suggest just doing a by-value buffer, as it is easy to then use this inside a DVR or other by-reference method of one's choice.


To be honest, I don't understand anything here. Neither your message nor wiebe@CARYA quotation. Could I get more information? What's the differente between having the buffer by reference or by value? I have never work with DVR.

 

Thanks for your time

0 Kudos
Message 19 of 36
(1,612 Views)

@EMCCi wrote:

@drjdpowell  ha escrito:

wiebe@CARYA wrote:

A big choice is whether you want this buffer to be by reference using a FGV, DVR, Queue, etc., or by value (in place element structures). Another choice is if it should be global (or a 'singleton'), or by wire. By ref\by wire is mutually exclusive with instance\singleton. Both will effect the buffer in it's core.


I would suggest just doing a by-value buffer, as it is easy to then use this inside a DVR or other by-reference method of one's choice.


To be honest, I don't understand anything here. Neither your message nor wiebe@CARYA quotation. Could I get more information? What's the differente between having the buffer by reference or by value? I have never work with DVR.


OK, here it goes. I'll try to be objective, although I'm not Smiley Wink.

 

So a reference refers to something. In this context, let's say you have a reference or reference based buffer. You'd have a wire, and when you split the wire, you get two wires referring to the same buffer. That means if you add data at one place, you can read it in the other.

 

Data Value References do that. Queues would also have that effect. The init creates a queue\DVR, and a cluster, class or raw reference (queue\DVR) will copy the reference, but not the data it points to.

 

That's by reference.

 

A simple 2D array on the other hand, acts completely different. (In fact, it works exactly the same*.) Splitting the wire (even when embedded in a class or cluster) conceptually makes a copy of the data. (LV doesn't copy data if it can be avoided.) So when adding data in one place, this data does not magically appear in the other place.

 

That's by value.

 

*The trick is that the reference is the data, and it's copied exactly the same as the 2D array. So it is very consistent, but the effect is completely different.

 

When something is by wire, you need the wire to refer to that object. If the wire (e.g. a class or cluster) contains a reference, you still need the wire to get the reference.

 

That's by wire.

 

A Functional Global on the other hand, is not by wire. It's a VI, and there's nothing to refer to it. As a consequence, you can't reuse the FG for different purposes. If you want to reuse it, you need to save a copy (which is usually a bad thing). Not needing a wire is sometimes perceived as a good thing (notice my bias against it Smiley Tongue). There are two sides of the coin. It's convenient, easy and fast to use (+), it's more difficult to search (-), tight coupling lurks (-) and copying\reuse is harder (-).

 

That's a singleton\global.

 

So you can have:

by value\by ref

by wire\ global

 

For a global, by value \ by ref is mood. A global doesn't need to be passed, so a wire nor a reference is needed

By value \ by wire is 'plain' LabVIEW.

By ref \ by wire is for instance a queue reference in a cluster.

 

By value \ by wire is often considered more difficult when scaling up. The moment you get parallel loops\processes, you'll want to get data from one to the other loop. By reference solves that, and I think people find it more difficult to do this by value. However, values can be send from one loop to the other, for instance by queue, with (user) events, or by messaging libraries.

 

By reference can be more difficult to debug (the reference dies when the main VI stops running) and they can cause memory leaks more easily.

Message 20 of 36
(1,595 Views)