8

I am writing a function which requires an array to be created at runtime. The array will be of small size so I am not worried about unsafe code, however, I want to write 'proper' code. As such I am considering three alternatives:

  1. char array[len];
  2. char array = new char(len);
  3. std::vector array(len);

Using Compiler Explorer to compare them with -O3. The results were as such:

  1. 12 instructions, 0 calls to new
  2. 21 instructions, 1 call to new
  3. 118 instructions, 2+ calls to new

Am I missing an optimisation for std::vector<> or is the 'proper' c++ way slower or have I entirely missed a way of coding this?

edit: I forgot to delete the heap-allocated array

Test code:

code 1:

#include <string.h>

void populate_array(char* arr);
int compute_result(char* arr);

int str_to_arr(const char* str)
{
    auto len = strlen(str);
    char array[len];
    populate_array(array);
    return compute_result(array);
}

code 2:

#include <string.h>

void populate_array(char* arr);
int compute_result(char* arr);

int str_to_arr(const char* str)
{
    auto len = strlen(str);
    char* array = new char[len];
    populate_array(array);
    auto result = compute_result(array);
    delete[] array;
    return result;
}

code 3:

#include <string.h>
#include <vector>

void populate_array(std::vector<char> arr);
int compute_result(std::vector<char> arr);

int str_to_arr(const char* str)
{
    auto len = strlen(str);
    std::vector<char> array(len);
    populate_array(array);
    return compute_result(array);
}
Shayan Shafiq
  • 1,447
  • 5
  • 18
  • 25
Ebony Ayers
  • 350
  • 1
  • 3
  • 9
  • 1
    The vector code also frees the memory which the other code does not do. – Richard Critten Jan 10 '21 at 11:29
  • There's also `unique_ptr`. – HolyBlackCat Jan 10 '21 at 11:29
  • Code 1 does not compile for me and it shouldn't. The value you pass as array size needs to be a `constexpr`, but here clearly it isn't. – fabian Jan 10 '21 at 11:41
  • Also `std::vector test` case should only be doing 1 heap allocation unless there is code we are not seeing that increases it's size. – Richard Critten Jan 10 '21 at 11:43
  • 2
    Here's a few options: https://godbolt.org/z/Gv1oGM. Some errors in your code: The vector one copies the entire vector because you pass by value isn't comparable to the other ones (either pass by reference or pass the `data()` pointer). You should be doing `new char[len]` to allocate `len` bytes of chars. You have a memory leak in code 2 since you don't `delete[] array`. – Artyer Jan 10 '21 at 11:46
  • `boost::container::small_vector`? Basically, choose an upper bound on `len` and then you can use an array (not VLA) of that size. – Marc Glisse Jan 10 '21 at 12:16
  • @fabian the code does compile for me, both on compiler explorer and locally. I am using CMake on Arch Linux which I believe is compiling with ```g++ -std=c++20 ...``` as well as basically every warning under the sun, and that works for me. – Ebony Ayers Jan 10 '21 at 12:31
  • @Artyer Thank you so much for those suggestions. Two questions: 1. is there a reason you use ```static_cast(operator new(len))``` instead of ```new char[len]```? 2. Why is the use of alloca marked as POSIX only? – Ebony Ayers Jan 10 '21 at 12:40
  • @EbonyAyers `alloca` is a POSIX function (which is similar to a VLA), not a standard C or C++ function. `operator new` is just a direct replacement for `malloc` that I would use in C code for a similar situation to this, but `new char[]` works just as well – Artyer Jan 10 '21 at 12:52

1 Answers1

7

There are a few issues in the code, that may be leading you astray in the comparison.

  1. new char(len) allocates a single char, initialized with the value len. You'd be after new char[len] to allocate len chars. There should be a matching delete [], too.
  2. The std::vector<char> object is passed to populate_array by value, making a copy (and consequently not actually populating the array you want), and similarly for compute_result. These copies will engender new allocations. Passing by reference would be appropriate here.
  3. Without using a custom allocator, std::vector will value-initialize all its elements. Effectively, it means that every element in this vector is set to zero. This is not performed by new char[len].

VLAs are not part of C++, but may be provided as an extension. While in this instance, for small len, the compiler has the option of allocating the space for the array on the stack, they are probably best avoided because of their non-standard nature; even in C, they are not required to be supported.

halfflat
  • 1,584
  • 8
  • 11