DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Arrays in JSON: Modeling, Querying and Indexing Performance
  • Understanding IEEE 802.11(Wi-Fi) Encryption and Authentication: Write Your Own Custom Packet Sniffer
  • How to Implement Linked Lists in Go
  • Recursive Feature Elimination in Practice

Trending

  • GitHub Copilot's New AI Coding Agent Saves Developers Time – And Requires Their Oversight
  • Scaling Microservices With Docker and Kubernetes on Production
  • Rust, WASM, and Edge: Next-Level Performance
  • Monolith: The Good, The Bad and The Ugly
  1. DZone
  2. Data Engineering
  3. Data
  4. Native Array or Std::Array; That Is the Question!

Native Array or Std::Array; That Is the Question!

Native array is more static, which std::array is dynamic. This article focuses on static performance specifically.

By 
Christopher Lamb user avatar
Christopher Lamb
·
Feb. 03, 20 · Tutorial
Likes (4)
Comment
Save
Tweet
Share
21.2K Views

Join the DZone community and get the full member experience.

Join For Free

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:

C++
 




x
15


 
1
for(const auto& i : std_numbers)
2
{
3
    std::cout << i << std::endl;
4
}
5

          
6
for(const auto& i : a_numbers)
7
{
8
    std::cout << i << std::endl;
9
}
10

          
11
auto min = std::min_element(std_numbers.begin(), std_numbers.end());
12
std::cout << "std Min: " << *min << std::endl;
13

          
14
min = std::min_element(std::begin(a_numbers), std::end(a_numbers));
15
std::cout << "a Min: " << *min << std::endl;



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:

C++
 




xxxxxxxxxx
1
17


 
1
std_numbers[0] = 100;
2
for(const auto& i : std_numbers)
3
{
4
    std::cout << i << std::endl;
5
}
6

          
7
std_numbers.at(0) = 1000;
8
for(const auto& i : std_numbers)
9
{
10
    std::cout << i << std::endl;
11
}
12

          
13
a_numbers[0] = 100;
14
for(const auto& i : a_numbers)
15
{
16
    std::cout << i << std::endl;
17
}



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:

C++
 




xxxxxxxxxx
1
17


 
1
template<typename T>
2
void evaluation_engine(const T& f, const std::string& label = "Container")
3
{
4
    std::vector<double> samples;
5
    for (auto i = 0; i < sample_max; ++i)
6
    {
7
        const auto start_time = std::chrono::high_resolution_clock::now();
8
        for (auto j = 0; j < loop_max; ++j)
9
        {
10
            f();
11
        }
12
        const auto stop_time = std::chrono::high_resolution_clock::now();
13
        const auto duration = stop_time — start_time;
14
        samples.push_back(duration.count());
15
    }
16
    print_statistics(samples, label);
17
}



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:

C++
 




xxxxxxxxxx
1


 
1
evaluation_engine([]()
2
{
3
    constexpr std::array<int, 5> numbers {0, 1, 2, 3, 4};
4
}, "STL Creation");
5
evaluation_engine([]()
6
{
7
    constexpr int numbers[] {0, 1, 2, 3, 4};
8
}, "Array");



This gives us:

Plain Text
 




xxxxxxxxxx
1


 
1
Statistics for STL Creation
2
    minimum: 1787; maximum: 1859; mean: 1824.66; std: 27.2845
3
      
4
Statistics for Array
5
    minimum: 1830; maximum: 1910; mean: 1862.86; std: 27.3357



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:

C++
 




xxxxxxxxxx
1


 
1
evaluation_engine([&a_numbers]()
2
{
3
    const auto value = a_numbers[3];
4
}, "Array Access");



Gives us these results:

Plain Text
 




xxxxxxxxxx
1


 
1
Statistics for Array Access
2
    minimum: 2147; maximum: 2241; mean: 2160.32; std: 11.3568



Now std::array:

C++
 




xxxxxxxxxx
1


 
1
evaluation_engine([&std_numbers]()
2
{
3
    const auto value = std_numbers[3];
4
}, "STL Access");


Plain Text
 




xxxxxxxxxx
1


 
1
Statistics for STL Access
2
    minimum: 3629; maximum: 3998; mean: 3760.79; std: 121.434



Using std::array::at(.)

Plain Text
 




xxxxxxxxxx
1


 
1
Statistics for STL at() Access
2
    minimum: 4320; maximum: 5094; mean: 4684.56; std: 242.694



Now looking at std::array access:

C++
 




xxxxxxxxxx
1


 
1
evaluation_engine([&std_numbers]()
2
{
3
    const auto value = std_numbers.at(3);
4
}, "STL at() 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?

Plain Text
 




xxxxxxxxxx
1


 
1
Statistics for Array assignment
2
    minimum: 2057; maximum: 2775; mean: 2205.31; std: 81.6836
3
      
4
Statistics for STL assignment
5
    minimum: 3323; maximum: 3556; mean: 3403.62; std: 28.7593
6

          
7
Statistics for STL at() Assignment
8
    minimum: 3640; maximum: 3882; mean: 3705.12; std: 42.9675



Again, a bit of a range, with std::array::at(.) being the slowest. Let's look at copying:

Plain Text
 




xxxxxxxxxx
1


 
1
Statistics for Array copy
2
    minimum: 2091; maximum: 2156; mean: 2102.45; std: 11.4301
3

          
4
Statistics for STL copy
5
    minimum: 2608; maximum: 2729; mean: 2626.83; std: 17.4178



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.


Further Reading

Two Lines of Code and Three C++17 Features: The Overload Pattern

Hashing in Java vs. C++

How to Prevent Your Java Collections From Wasting Memory

Data structure Plain text

Opinions expressed by DZone contributors are their own.

Related

  • Arrays in JSON: Modeling, Querying and Indexing Performance
  • Understanding IEEE 802.11(Wi-Fi) Encryption and Authentication: Write Your Own Custom Packet Sniffer
  • How to Implement Linked Lists in Go
  • Recursive Feature Elimination in Practice

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!