I’m a big advocate for using the C++ standard library as much as possible. Modern library implementations are fast and stable today, they are easy to use, have clear interfaces, and have unified semantics. This wasn’t always the case but it has been over the past few years. here, I’m going to take a look at array-like collections in C++ — specifically the std::array and std::vector types.
Both of these types integrate with various standard library algorithms in similar ways. The first is purely static; however, while the second is dynamic. Today we’re going to look at static performance specifically.
You may also like: Arrays.hashCode() Vs. Objects.hash()
First, let’s take a look at overall usability. Frankly, as the STL has tons of support for legacy C types, they both work pretty well:
There are only small differences here — specifically when invoking an algorithm (i.e. std::min_element(.)). The std::array type has integrated support for iterators; we need to use adapter functions from the STL to generate an iterator from an array. This particular difference is trivial.
There’s another that’s not.
You can access data in an std::array using either bracket notation or via the std::array::at(.) method:
Std::array::at(.) accesses elements in an array with bounds checking. Bracket notation does not, and neither does native arrays. This is a big deal! Out of bounds errors in arrays is a major source of significant, exploitable security vulnerabilities in software. You can avoid those issues with std::array::at(.). That by itself is reason to use std::array.
What about performance?
Let’s look at a few specific operations — creation, assignment, retrieval, and copying. Here’s the code we’ll use:
The sample_max is 100, and the loop_max 1000. This function takes a lambda expression or function and evaluates it, timing accesses. print_statistics() will calculate a few statistics and print them (omitted).
Our creation tests are pretty straightforward:
This gives us:
STL based creation is equivalent to native array creation. Here, it seems slightly faster, but that’s likely an artifact of the state of my computer when running these tests. What about access? First, let’s look at array access:
Gives us these results:
Now looking at std::array access:
Now, I’m using the high-resolution clock in these measurements, and I’m not that interested in wall clock time. I am interested in comparative performance though. Based on these measurements, we can see that native array access is a bit over 40% slower than std::array access. Std::array::at(.) access is on the order of 20% slower than that.
On my system, these measures are in nanoseconds, so this is still pretty darn fast. Nevertheless, std::array use isn’t free.
What about the assignment?
Again, a bit of a range, with std::array::at(.) being the slowest. Let’s look at copying:
Clearly, in all cases except initial creation, the native array is more performant than the std::array type. Keep in mind, in each of these cases, I’m performing 100,000 operations, and we’re talking about a difference of a hair over two milliseconds between a native array and std::array time.
Which to use? Well, I’d suggest you use std::array in virtually all cases — at least, always start with it. You may need to transition to a native array in some cases, but frankly, you can usually squeeze more performance via algorithmic improvement than changing the data structure you use.