News for hackers. Stuff that matters.
Re:Effortless Performance Improvements in C++: std:vector by cmovq on Wednesday March 08, 2023 @04:40PM (#35075128)
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<std::string_view> with each string_view pointing to part of the original text.

Re:Effortless Performance Improvements in C++: std:vector by Negitivefrags on Wednesday March 08, 2023 @04:48PM (#35075206)
I like to implement an iterator that returns string_view when dereferenced. You can make the ergonomics quite nice with a helper class.

for( auto value : from_csv( str ) ) { … }

Re:Effortless Performance Improvements in C++: std:vector by jeffbee on Wednesday March 08, 2023 @05:11PM (#35075451)
Right? Effortless performance improvements without having to make it a "journey" and write 50 blog posts: absl::StrSplit will stop this vector reallocation and get rid of those string copies, and if you want you can just iterate over it without storing anything.
Re:Effortless Performance Improvements in C++: std:vector by rhdunn on Wednesday March 08, 2023 @06:10PM (#35076077)
That follows one of the core optimization strategies: "do less work", or more precisely, "don't do work you aren't going to use, and don't repeat work you've already done". Here, the extra work is that of copying the token text to create new string objects.
Re:Effortless Performance Improvements in C++: std:vector by quadrature on Wednesday March 08, 2023 @06:16PM (#35076118)
based on the final paragraph i'm guessing this will be the next blogpost in the series.
Re:Effortless Performance Improvements in C++: std:vector by returningfory2 on Wednesday March 08, 2023 @06:22PM (#35076164)
A complementary idea is to reuse the vector of strings across different calls to `tokenize`. The main loop in the code is of the form:

    {
        std::vector<std::string> tokens = tokenize(line);
        // do work with tokens
    }
which you could change to

    std::vector<std::string> tokens;
    {
        tokens.clear();
        tokenize(line, &tokens);
        // do work with tokens
    }
Combined with your suggestion to use std::string_view, this would mean only O(1) allocations across the program for this part of the code.
Re:Effortless Performance Improvements in C++: std:vector by jstimpfle on Wednesday March 08, 2023 @06:23PM (#35076165)
Just store the offsets into the corresponding source file. Why even fool around with a silly string view? A 32 bit integer per token is sufficient in many circumstances.
Re:Effortless Performance Improvements in C++: std:vector by TylerGlaiel on Wednesday March 08, 2023 @06:30PM (#35076221)
> 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 would be an improvement here

Re:Effortless Performance Improvements in C++: std:vector by zabzonk on Wednesday March 08, 2023 @06:39PM (#35076301)
> std::string can store <=22 character strings without needing to allocate

implementation dependent - the c++ standard says nothing on this

Re:Effortless Performance Improvements in C++: std:vector by TylerGlaiel on Wednesday March 08, 2023 @07:29PM (#35076741)
sure but this is how pretty much every implementation does it these days, so you should be able to safely rely on that (or just drop in your own version that provides that guarantee if you are really concerned about it)
Re:Effortless Performance Improvements in C++: std:vector by Calavar on Wednesday March 08, 2023 @11:39PM (#35078377)
I would expect that std::string_view would still be significantly faster. Copying or moving an std::string with small string optimization is likely going to boil down to a branch (to check if the instance is using the small string optimization) and a memcpy. As opposed to copying or moving an std::string_view, which should be two MOV instructions.
Re:Effortless Performance Improvements in C++: std:vector by jeffbee on Thursday March 09, 2023 @09:36AM (#35081574)
I don’t see why moving std::string needs to branch. You just copy the source and then zero it out, unconditionally.
Re:Effortless Performance Improvements in C++: std:vector by ckennelly on Thursday March 09, 2023 @11:44PM (#35090479)
It depends on your STL implementation's representation of string: https://godbolt.org/z/nMYGYoWbq

* libstdc++ has an internal reference to its own address for the SSO. If the moved-from string was referencing its SSO buffer, the moved-to string needs to use its own address. The branch is differentiating the SSO state from a heap-allocated state.

* libc++ string move can be implemented this way, but the branch ends up happening on access to the string. It still needs to discard the old heap allocated buffer, if need-be as well.

Re:Effortless Performance Improvements in C++: std:vector by TylerGlaiel on Friday March 10, 2023 @03:11AM (#35091820)
string view is theoretically going to be faster, but its the type of thing you'd really need to profile in actual context to see to what extent that is true or not. I was mainly just pointing out that (small) strings are actually way faster than people would think since they don't actually need to allocate memory

if I was actually tasked with hyper-optimizing a tokenizer I would probably skip past string view and do a pair of U16 indexes instead assuming the input file is less than 65k characters [with a "slow path" that uses U32 instead]. I just think that its probably not actually going to be a whole order of magnitude faster than just using string (unless there's long tokens)

Re:Effortless Performance Improvements in C++: std:vector by ddtaylor on Wednesday March 08, 2023 @07:36PM (#35076796)
I looked at the original code from Part 1 and it really makes me confused at why you would implement most of this in this way - even if your goal is to simply test the bottlenecks of various STL implementations. I don't think it's a terrible series and I think the author has the right idea, but the choices in implementing things like taking in a large string and returning a large string when you're basically saying the use case is CSV record parsing is strange.