Tokenizing by storing strings in a vector is almost never what you want for high performance code, as it will result in an allocation for each token. If you can keep the original source string around, consider using std::vector with each string_view pointing to part of the original text. An even better approach is to avoid using an intermediary vector altogether if all you need is to process the tokens one-by-one and…
> Tokenizing by storing strings in a vector is almost never what you want for high performance code, as it will result in an allocation for each token. not quite, std::string can store <=22 character strings without needing to allocate (in 64 bit mode at least) (look up short string optimization), 22 characters is actually quite a lot in the context of tokenization, so its not a given that switching to string views w…
Effortless Performance Improvements in C++: std:vector
21–30 of 55 posts
Re: Effortless Performance Improvements in C++: std:vector
#22[0] https://github.com/mediakind-video/effortless-performance-im...
Re: Effortless Performance Improvements in C++: std:vector
#23Earlier quoted context omitted.
> The cost of vector dynamic reallocation has gone down dramatically since C++11 introduced move constructors 1. It's gone down, but it's still very high. 2. It hasn't gone down for types types like std::string_view, for which moving and copying take about the same amount of effort.
Vector reallocations result in an average of one* move (which yes, is effectively a copy for types like string_view) per element under normal operation. If you're concerned about that single copy (primarily: if profiling has demonstrated that this is a real bottleneck), and you know the target size a priori, then yes, by all means, use reserve. Just be warned that if you do it wrong, you can literally make your progr…
The whole point of move construction is that moving a string is almost as cheap as moving a string_view. The moved-to string now owns the external storage and the moved-from string does not.
Re: Effortless Performance Improvements in C++: std:vector
#24 -3,6 +3,8
std::vector tokenize(const std::string& s)
{
std::vector result;
+ // Expect four fields or less in our input.
+ result.reserve(4);
std::string::size_type f = 0;
std::string::size_type p = s.find(':');
I wonder why not: -3,6 +3,7
std::vector tokenize(const std::string& s)
{
- std::vector result;
+ // Expect four fields or less in our input.
+ std::vector result(4);
std::string::size_type f = 0;
std::string::size_type p = s.find(':');
?It's not a big difference, but vectors have a constructor that takes an initial reservation size in order to facilitate pre-allocation.
Edit: No it doesn't. I've not written C++ in anger in... actually quite a bit longer than I thought. And it shows. Doh. Thanks, wirelessgigabit.
Re: Effortless Performance Improvements in C++: std:vector
#25I also love using flat_map etc which implements a map as a sorted vector. Look up is blazing fast. And perhaps surprisingly, allocating a new vector and copying everything over is actually pretty fast too.
The best "look up" of tokens is to just lex them given an offset into the source file. No need to have a permanent token storage. It's a waste of space.
Re: Effortless Performance Improvements in C++: std:vector
#26IMO any discussion of std::vector::reserve should be accompanied by warnings that it can actually make your program slower if used improperly. https://en.cppreference.com/w/cpp/container/vector/reserve > Correctly using reserve() can prevent unnecessary reallocations, but inappropriate uses of reserve() (for instance, calling it before every push_back() call) may actually increase the number of reallocations (by caus…
Re: Effortless Performance Improvements in C++: std:vector
#27From the article, the proposed change: -3,6 +3,8 std::vector tokenize(const std::string& s) { std::vector result; + // Expect four fields or less in our input. + result.reserve(4); std::string::size_type f = 0; std::string::size_type p = s.find(':'); I wonder why not: -3,6 +3,7 std::vector tokenize(const std::string& s) { - std::vector result; + // Expect four fields or less in our input. + std::vector result(4); std…
#include
#include
#include
template
std::ostream & operator &v)
{
s.put ('[');
char comma[3] = { '\0', ' ', '\0' };
for (const auto & e:v)
{
s with_reservation;
with_reservation.reserve (4);
std::cout via_ctor (4);
std::cout
Yields with_reservation: []
via_ctor: [, , , ]
https://onlinegdb.com/GUlVHoqC5zRe: Effortless Performance Improvements in C++: std:vector
#28Earlier quoted context omitted.
The best "look up" of tokens is to just lex them given an offset into the source file. No need to have a permanent token storage. It's a waste of space.
That's not as cache-friendly. The "best" way in terms of performance is probably to intern them adjacently in a single string, then have offsets into that.
In many scenarios you need token spans / token strings only for diagnostics, in other words you almost never need them. When any individual data item is rarely looked up, being economical about memory footprint will in fact improve your overall cache hit rate. You only want to store enough information to retrieve the tokens just in case, so storing file offsets is exactly the right call.
Re: Effortless Performance Improvements in C++: std:vector
#29Earlier quoted context omitted.
Vector reallocations result in an average of one* move (which yes, is effectively a copy for types like string_view) per element under normal operation. If you're concerned about that single copy (primarily: if profiling has demonstrated that this is a real bottleneck), and you know the target size a priori, then yes, by all means, use reserve. Just be warned that if you do it wrong, you can literally make your progr…
> arbitrary amounts of underlying storage from a string The whole point of move construction is that moving a string is almost as cheap as moving a string_view. The moved-to string now owns the external storage and the moved-from string does not.
Re: Effortless Performance Improvements in C++: std:vector
#30From the article, the proposed change: -3,6 +3,8 std::vector tokenize(const std::string& s) { std::vector result; + // Expect four fields or less in our input. + result.reserve(4); std::string::size_type f = 0; std::string::size_type p = s.find(':'); I wonder why not: -3,6 +3,7 std::vector tokenize(const std::string& s) { - std::vector result; + // Expect four fields or less in our input. + std::vector result(4); std…