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");
}
Mast
13.8k12 gold badges57 silver badges127 bronze badges
asked May 31, 2018 at 7:15
\$\endgroup\$
3
  • 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\$ Commented May 31, 2018 at 9:46
  • \$\begingroup\$ i ran it 10000 times \$\endgroup\$ Commented May 31, 2018 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\$ Commented May 31, 2018 at 11:47

1 Answer 1

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);
}
answered May 31, 2018 at 9:02
\$\endgroup\$
4
  • \$\begingroup\$ i did a small test with total_len and found that std::string is 20X slower than string_view \$\endgroup\$ Commented May 31, 2018 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\$ Commented May 31, 2018 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\$ Commented May 31, 2018 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\$ Commented May 31, 2018 at 12:19

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.