-2

It's encouraged to use STL over dynamic arrays but I'm curious what could be the bottleneck? MAX_PACKET_LENGTH is 32768.

---------------------------------------------------------------------------
Benchmark                                    Time           CPU Iterations
---------------------------------------------------------------------------
BM_VectorInit                             1335 ns       1193 ns     497778
BM_RawInit                                  58 ns         55 ns   10000000


static void BM_VectorInit(benchmark::State &state)
{
    for (auto _ : state) 
    {
        std::vector<char> a(MAX_PACKET_LENGTH);
    }
}

static void BM_RawInit(benchmark::State &state)
{
    for (auto _ : state) 
    {
        auto a = new char[MAX_PACKET_LENGTH];
        delete[] a;
    }
}
9
  • 6
    Not sure but a reasonable optimizer would remove both pieces of code. Commented May 15, 2018 at 21:24
  • 1
    "It's encouraged to use STL over dynamic arrays" yes, this is for you the programmer and anyone else who ever has to look at your code. The price you pay for this is a little overhead. Commented May 15, 2018 at 21:25
  • 1
    What about auto a = new char[MAX_PACKET_LENGTH]{};? vector will zero initialize the array. Commented May 15, 2018 at 21:26
  • 2
    Also, the equivalent raw array version would be auto a = new char[MAX_PACKET_LENGTH]{}; Commented May 15, 2018 at 21:26
  • 1
    As @juan says, you need to actually do something with the vector/array to prevent the compiler from optimising the code away - I have a short blog article on the topic at latedev.wordpress.com/2011/10/15/the-joy-of-benchmarks Commented May 15, 2018 at 21:27

2 Answers 2

4

First, as @juanchopanza suggests - we can't reproduce your figures if you don't provide a proper test program; you've just given us two functions which do nothing and won't result in anything in the binary.

Anyway, it seems the overhead is due to std::vector zero-initializing the char values.

If you want to avoid that happening, or just to level the playing field for benchmarking, use a struct which wraps a char and doesn't initialize it as the vector element type, and run your tests again. I wouldn't recommend actually using such a silly struct in production code though - use the types you actually need.

And speaking of using what you need - it's perfectly ok to use:

auto buffer_ptr = std::make_unique<char[]>(MAX_PACKET_LENGTH);

and then maybe:

auto buffer = std::span<char>(raw_buffer.get(), MAX_PACKET_LENGTH);

and you can use spans almost everywhere you can use std::vectors. (Note, though, that std::span is only coming in C++20 and for now you'll need a GSL implementation).

Sign up to request clarification or add additional context in comments.

2 Comments

It could be noted that std::vector<char> a; a.reserve(MAX_PACKET_LENGTH); would be a closer match in behavior.
@DrewDormann: Well, yes, but that means OP would need to resize the vector later on.
-1

You are comparing the cost of constructing a std::vector with the cost of allocating an array on the heap. When you create a std::vector, it internally allocates an array on the heap, but there is additional overhead since the std::vector is itself an object that needs to be constructed and stored (possibly on the stack or on the heap). Therefore, it should take longer to create a std::vector than it takes to create a new char[].

That being said, there is another difference between BM_RawInit and BM_VectorInit. When you construct a std::vector with one integer argument, as in std::vector<char> a(MAX_PACKET_LENGTH), two things happen. Space will be allocated on the heap for MAX_PACKET_LENGTH items and those items will also be default constructed. On the other hand, when you do new char[MAX_PACKET_LENGTH], only allocation occurs.

For a better comparison, try to create a third benchmark that only allocates space, like this:

static void BM_VectorReserve(benchmark::State &state)
{
    for (auto _ : state) 
    {
        std::vector<char> a;
        a.reserve(MAX_PACKET_LENGTH);
    }
}

This isn't a perfect comparison, though, because a small amount of memory will be allocated on the first line where we declare a, and then when we call reserve, that initial memory will be released. After that, the std::vector will allocate enough space for MAX_PACKET_LENGTH items. In real code, this is often negligible, but for the sake of your benchmark, it would partially explain why BM_VectorReserve takes longer than BM_RawInit.

13 Comments

No, a vector is really not at all a wrapper around arrays.
VectorInit 1426ns, VectorReserve 229ns
@DamianKalinowski Cool, so you're seeing that VectorReserve is much closer to RawInit, but you still pay some penalty for using the STL container, rather than the raw array
@einpoklum: I think you're being overly pedantic. It's totally normal to talk about using malloc to allocate an array, or to say that a function accepts an array when the args are really pointer,length and you don't care where the array lives. (At least that's the case in C, where you don't have the option of wrapping it up in an STL container). And BTW, arrays can live in static storage, too, not automatic storage. If you're going to be super-pedantic about C++ terminology, don't assume there's a stack or a heap.
@einpoklum You and I have a fundamental difference of opinion here. You're saying that char a[5] is an array, but that char* b = new char[5] is not an array.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.