4 ms·
> Release performance also slightly increased across the board - this is because for many of our arrays, the default initialization performed by std::vector’s c
by Ragnarork 8y ago
> Release performance also slightly increased across the board - this is because for many of our arrays, the default initialization performed by std::vector’s constructor is redundant as we’re going to fill the array anyway. With std::vector, you can either resize a large array and then compute the items (which requires default-initializing every item redundantly), or reserve and push_back repeatedly (which requires a bit more code for adding each item, and this overhead can also add up). With a custom container it’s easy to have an option to skip initialization - in fact, in our replacement that’s the only option since it’s easy to memset the array manually if necessary.
I have a hard time to get the rationale here. Is the author implying that using memset is inherently better than initializing an std::vector with the constructor that takes a count and a default value, or resetting it with the same variant of .assign()? Skimming through the code that's linked in the article, it looks like there's no use case where either the former or the latter wouldn't fit.
- kbsletten 8y agoI read it as the author almost never uses the uninitialized data and when they do need a zeroed array it's simple to do so why pay the cost to initialize the elements when you're about to set them anyway.
- cf141q5325 8y agoThere is also the approach to just copy objects existing objects to safe yourself the cost of running the constructor in order to shorten your startup time.
- kllrnohj 8y agoBut you can easily avoid that cost on std::vector, too. std::vector doesn't default-initialize unless you ask it to via resize(). reserve() doesn't initialize, though, it just allocates. Take this toy example: #include <vector> #include <cstdio> struct MyFoo { MyFoo() { printf("default ctor\n"); } MyFoo(int i) { printf("Initialized with %d\n", i); } MyFoo(const MyFoo& foo) {} }; int main() { std::vector<MyFoo> myvec; myvec.reserve(100); printf("resize(2)\n"); myvec.resize(2); printf("emplace_back()\n"); myvec.emplace_back(); printf("emplace_back(1)\n"); myvec.emplace_back(1); printf("push_back(MyFoo{1})\n"); myvec.push_back(MyFoo{2}); return 0; } It will print: resize(2) default ctor default ctor emplace_back() default ctor emplace_back(1) Initialized with 1 push_back(MyFoo{1}) Initialized with 2 Only the resize() call did a default initialization "silently", nothing else did.
- thestoicattack 8y agoThe tradeoff is that emplace_back and push_back need to check the capacity (even though they won't reallocate because of reserve). Whereas with resize, assignments into the initialized area need no checks at all.