08-07-2026 05:31 AM
Hello there,
Yesterday I faced the following situation :
1. I need to reorder an array of cluster regarding one of the element of the cluster.
2. Each element in the array can store quite a bit of data. So I prefer not to make copies.
After a lot of tries, I ended at the solution in the attached VIs (LV2020 SP1).
Although I'm pretty confident in the malleable VI as a substitute for the OpenG "Reorder Array2" VI, I'm not as confident about the previous parts.
It feels odd to have to build an intermediate array so that LabVIEW can reorder it separately.
Do you have any ideas on how I could have done this ?
Solved! Go to Solution.
08-07-2026 10:23 AM
This looks pretty elegant and the extra code is worth it when dealing with massive arrrays.
@PinguX wrote:
It feels odd to have to build an intermediate array so that LabVIEW can reorder it separately.
Note that the malleable "sort 1D array" has an input where you can wire a reference that defines "less than", but doing that makes your code even slower than the openG variant 😮
08-07-2026 11:05 AM - edited 08-07-2026 11:08 AM
@PinguX wrote:
Hello there,
Yesterday I faced the following situation :
1. I need to reorder an array of cluster regarding one of the element of the cluster.
2. Each element in the array can store quite a bit of data. So I prefer not to make copies.
And is there no way for you to just swap the ID and the large data within the cluster (by design), so the sorting id will be first?
snippet.png
In that case, a simple sort of a 1D array should do the job for you. Or am I misunderstanding something?
08-07-2026 02:14 PM - edited 08-07-2026 02:14 PM
@Andrey_Dmitriev a écrit :
@PinguX wrote:
Hello there,
Yesterday I faced the following situation :
1. I need to reorder an array of cluster regarding one of the element of the cluster.
2. Each element in the array can store quite a bit of data. So I prefer not to make copies.
And is there no way for you to just swap the ID and the large data within the cluster (by design), so the sorting id will be first?
In that case, a simple sort of a 1D array should do the job for you. Or am I misunderstanding something?
In case if that's impossible by design, second question: do all the large data arrays have exactly same length, or do they have different sizes?
I oversimplified the problem, sorry.
There isn't just one sorting criterion; just as one might want to sort by name, timestamp, options, etc.
The end user may want to sort its data by various criteria.
These "large data" do not all have exactly the same length. Behind this array example we have strings, among others things.
(one of these strings will be the main criterion used for sorting, that's why I mentioned "one element" in my message)
Currently, the next step I can think of, if my solution isn't enough, is to rethink the data structure. Like building a small database in RAM.
But that would come at a cost elsewhere. First of all, in terms of complexity. So, I might just stick with the current solution, may it be improvable or not.
08-07-2026 03:06 PM
@PinguX wrote:
I oversimplified the problem, sorry.
Ah, OK, got this. Well, if I attack this problem, I’ll probably do it with the help of a DLL. The fact is that for sorting such array elements inside a Cluster, we only need to swap the handles and leave the large allocated arrays untouched — they are already allocated by LabVIEW, and it makes no sense to move them. Therefore, the swap operation is just exchanging addresses:
static void swapElems(ClustElem* a, ClustElem* b)
{
/* Swap only pointers + id, not the large data contents */
ClustElem tmp = *a;
*a = *b;
*b = tmp;
}
I attached two solutions — one in C compiled with CVI and one in Rust. They are slightly faster:
image-20260807215806604.png
Rust is faster by a factor of 7, and it’s also quite elegant from a language point of view — the entire sort is just a single line (the last one). The rest is simply passing LabVIEW data into it:
#[repr(C)]
pub struct LargeArr {
pub dimSize: i32,
pub element: [u8; 1],
}
pub type LargeArrHdl = *mut *mut LargeArr;
#[repr(C)]
pub struct ClustElem {
pub largeData: LargeArrHdl,
pub id: i32,
}
#[repr(C)]
pub struct ClustArr {
pub dimSize: i32,
pub cluster: [ClustElem; 1],
}
pub type ClustArrHdl = *mut *mut ClustArr;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sortCluster2ptr(array: *mut ClustArrHdl) {
unsafe {
let arr = match array
.as_ref()
.and_then(|hdl| hdl.as_ref())
.and_then(|arr| arr.as_ref())
{
Some(arr) => arr,
None => return,
};
let n = arr.dimSize as usize;
if n <= 1 {
return;
}
let ptr = arr.cluster.as_ptr() as *mut ClustElem;
let slice = std::slice::from_raw_parts_mut(ptr, n);
slice.sort_unstable_by(|a, b| a.id.cmp(&b.id));
}
}
Something like that. A few rusty nails, but it should work.
08-07-2026 05:01 PM
Well I guess I'll have to learn another language, and learn a bit more about handles and pointers.
But before starting anything, I see that you have written some flags in the config.toml file, for customizing your Rust build.
I have practically no knowledge on this subject, but my guess is that it could affect the portability of the application ?
What would have been the result for the dll built from Rust without these flags ?
08-08-2026 02:13 AM
@PinguX wrote:
Well I guess I'll have to learn another language, and learn a bit more about handles and pointers.
But before starting anything, I see that you have written some flags in the config.toml file, for customizing your Rust build.
I have practically no knowledge on this subject, but my guess is that it could affect the portability of the application ?
What would have been the result for the dll built from Rust without these flags ?
Good catch! The code was completely AI‑generated, but the configuration was copied from another project.
Let me explain:
"-C", "target-cpu=native",
"-C", "target-feature=+avx2",
"-C", "target-feature=+fma",
"-C", "target-feature=+bmi2",
"-C", "target-feature=+lzcnt"
The most "dangerous" option here is "target-cpu=native", because the compiler will take the supported instruction set from my CPU. If I compile it on a Xeon with AVX‑512, you might run into problems on a CPU that doesn’t support those instructions.
The rest is supported on most modern processors — everything has AVX2. +fma is fused multiply‑add, allowing you to compute a * b + c in a single instruction, +bmi2 is the Bit Manipulation Instruction Set 2, and +lzcnt enables Leading Zero Count instructions.
But let’s try removing the custom configuration completely, leaving the default settings suitable for every CPU, and repeat the test:
noopt.PNG
It’s still below 20 ms, which means the optimizations have little effect here.
I also can ran it "as is" on my kitchen laptop, which is a 13‑year‑old Haswell:
i7 mq Screenshot 2026-08-08 07.27.24.png
This code can be easily enhanced. For example, you said you’re trying to achieve something like a “small database in RAM”, may be with multiple ids and different sorting criterias. Let’s add an additional id to the cluster and a parameter to choose the sort key. Then the structure becomes:
pub struct ClusterElement {
pub large_data: LargeArrHdl,
pub id1: i32,
pub id2: i32,
}
And the sort function will have an additional parameter and a switch:
pub unsafe extern "C" fn sort_cluster(array: *mut ClusterArrHdl, key: i32) {
// Skipped
let ptr = arr.cluster.as_ptr() as *mut ClusterElement;
let slice = std::slice::from_raw_parts_mut(ptr, n);
match key {
1 => slice.sort_unstable_by(|a, b| a.id1.cmp(&b.id1)),
2 => slice.sort_unstable_by(|a, b| a.id2.cmp(&b.id2)),
_ => {} // extend with more keys if needed
}
}
}
This kind of “syntax sugar” with "match" is what I really love in Rust.
Now in LabVIEW:
id1id2.png
To be sure everything is fine with the “large” arrays, I filled them with random data of random lengths. And it works.
Learning a text‑based programming language makes sense, especially nowadays, because LLMs can easily generate such code for you. I recommend both C and Rust.
C is fairly simple and old‑school; Rust is very modern (and not easy to learn, to be honest).
Just so you understand the magic of this line:
slice.sort_unstable_by(|a, b| a.id1.cmp(&b.id1));
`sort_unstable_by` is a method on slice, which pointed to the memory. Unstable sort means that equal elements may change their relative order, which is often faster than stable sorting. The `|a, b|` part is called a closure — something C doesn’t have. Here, a and b are &ClusterElement (references to two elements in the slice). a.id1.cmp(&b.id1) calls the cmp (compare) method on the id1 field, returning an ordering (Less, Equal, or Greater). This tells sort_unstable_by how to order any two elements based on id1. So much happening in a single line — just beautiful.
The demo project and complete source is in the attachment.
You can easily compile Rust code — all you need is the Rust compiler, which is free.
Once downloaded and installed, you can build the new DLL with the command:
cargo build --release
As a development environment, I recommend JetBrains RustRover (free for non‑commercial use), or Zed, or VS Code with the Rust plugin or any text editor you like.
The example above is slightly advanced, because we are passing an array of clusters containing another array, and doing it by pointer. To get started, I recommend beginning with a trivial DLL that adds two integers (a + b), then play with strings, then arrays, then resizing arrays inside the DLL, then clusters, etc.
08-08-2026 02:58 AM
Thanks a lot for the effort you put into explaining. I hesitated to mark your message as solution, as others members looking for a solution to a similar problem may not accept to switch from LabVIEW to another language.
But as long as it meets my needs, I think I can say that it is a solution.
If anyone has another solution using pure LabVIEW, I would gladly accept it as well. 😊
08-11-2026 11:33 AM
You can implement a linked list purely in LabVIEW using DVR's: https://forums.ni.com/t5/Example-Code/Advanced-Data-Structures-in-LabVIEW/ta-p/3519662
I doubt it'll be faster than a specific, custom-built dll but it might get you "good enough". The performance penalties come from moving stuff between your two data types, so if you could change your storage type to natively use a linked list you won't have to copy anything at all.
You could wrap all of this up in an "array" class that uses linked lists under the hood.
All that said- it sounds like what you need is a database. This library is excellent, and you can even make the database "in memory" so you don't need an extra file.
08-11-2026 04:32 PM - edited 08-11-2026 04:44 PM
@BertMcMahan a écrit :
You can implement a linked list purely in LabVIEW using DVR's: https://forums.ni.com/t5/Example-Code/Advanced-Data-Structures-in-LabVIEW/ta-p/3519662
I tried to play a bit with the example shared by NI. There is too much bugs, so I ended up doing my own implementation in LabVIEW (see attached, saved for LV2020).
I used the Merge Sort algorithm.
It seems I am encountering an issue with recursive subVI calls.
During the first execution of "_Main.vi", the number of calls to the subVIs are "normal". But then, for each subsequent execution of "_Main.vi" (as long as I do not close it), the number of calls increases explosively. Like x3 for each successive run.
At the end I may have the following error :
PinguX_0-1786483978409.png