3
\$\begingroup\$

On some video from CPPcon, some one said that there should be variadic overload for operator+ as it can solve allocation when there are multiple + with the containers like std::string. So I just gave it a try, but I am not seeing a major performance boost. Are there any other ways to improve this?

#include <string>
#include <string_view>
#include <string.h>

using string_type = std::string_view;

inline std::string::size_type total_len(const string_type& str) {
    return str.length();
}

template<typename... String>
inline std::string::size_type total_len(const string_type& str, const String... strs) {
    return str.length() + total_len(strs...);
}

inline void append(size_t l, std::string& str1, const string_type& str2) {
    memcpy(str1.data() + l, str2.data(), str2.size());
    l += str2.size();
}

template<typename... String>
void append(size_t l, std::string& str1, const string_type& str2, String... strs) {
    void * p1 = str1.data() + l;
    const void * p2 = str2.data();
    memcpy(p1,p2, str2.size());
    l += str2.size();
    append(l, str1, strs...);
}

template<typename... String>
std::string StrCat(const string_type& str1, const string_type& str2, const String... strs) {
    std::string answer(total_len(str1, str2, strs...), 0);
    append(0, answer, str1, str2, strs...);
    return answer;
}

int main()
{
  StrCat("Hello, ","name","!\n");
}
\$\endgroup\$
  • 2
    \$\begingroup\$ You say you're not seeing a "major performance boost", but you're concatenating three very short strings, and we can't see how you're timing this or what you compared it to. It's going to be hard to get a solid answer on the benchmarking side. \$\endgroup\$ – Useless May 31 '18 at 9:46
  • \$\begingroup\$ i ran it 10000 times \$\endgroup\$ – bluedragon May 31 '18 at 11:20
  • 5
    \$\begingroup\$ ... and what was the total time for that? And what was the variance per run? And how was it scheduled, and did you check how the time scales with number of strings and/or length of strings, and what did you compare it against? \$\endgroup\$ – Useless May 31 '18 at 11:47
5
\$\begingroup\$

Benchmarking is a tricky business. I'll focus more on the code part, you tell me if you get an improvement on the performance part too.

Containers or c-style strings?

with containers like std::string

If you do want to work with containers, you can simplify your code a lot and avoid the use of std::string_view, which I believe triggers a good amount of conversions in your code.

For instance, your total_len function can be written as concisely as:

template<typename... String>
std::string::size_type total_len(const String&... strs) {
    return (strs.size() + ...);
}

Besides, I'm a bit skeptical about using memcpy on data() and such when you don't really need to. You won't beat std::string implementations that way, and you won't bring much flexibility either because at the end of the line, the product is still a std::string, not a newly malloced char*. But if a small error has gotten into your code, you'll be the guy who reinvented the wheel one time too many again.

Folding the right left way

Fold expressions are by the way more powerful than you seem to think. You don't have to be explicit about recursion. If you hang on to your std::strings, you can write a left folding in three lines:

template <typename... String>
void my_strcat_impl(String&&... strs) {
   (... += strs);
}
// <=> (((str1 += str2) += str...) += strN)

That means that the complete working example is rather short too:

#include <string>   
using namespace std::string_literals;

template<typename... String>
std::string::size_type total_len(const String&... strs) {
    return (strs.size() + ...);
}

template <typename... String>
void my_strcat_impl(String&&... strs) {
    (... += strs);
}

template <typename... String>
std::string my_strcat(const String&... strs) {
    std::string result;
    result.reserve(total_len(strs...));
    my_strcat_impl(result, strs...);
    return result;
}

int main()  {
    auto test = my_strcat("Hello, "s,"name"s,"!\n"s);
}
\$\endgroup\$
  • \$\begingroup\$ i did a small test with total_len and found that std::string is 20X slower than string_view \$\endgroup\$ – bluedragon May 31 '18 at 11:23
  • \$\begingroup\$ I'm pretty sure it's biased in some way, because the size method on strings and string_views should be completely equivalent (returning an already known number) \$\endgroup\$ – papagaga May 31 '18 at 12:01
  • \$\begingroup\$ just did some test and StrCat function is 2X slower when used with all std::string arguments compared to using bared operator+ for same all std::string args \$\endgroup\$ – bluedragon May 31 '18 at 12:06
  • 3
    \$\begingroup\$ "just did some test" doesn't mean much. Again, it seems really curious since a compiler is meant to translate the fold expression into a kind of unrolled loop with the bare operator+ at its heart. \$\endgroup\$ – papagaga May 31 '18 at 12:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.