11
\$\begingroup\$

At my workplace, we changed string type (which holds internationalized characters) for from std::wstring to std::u16string after VS 2015(Update 3) compiler upgrade.

Due to this, we are seeing loads of performance regressions such as this.

The profiler analysis reveals that std::u16string's std::char_traits<char16_t> operations such as copy, compare, find and assign are the most hit and are taking longer than std::wstring's std::char_traits<wchar_t> counterparts.

These std::char_traits<wchar_t> operations are written in terms of std::wmem* and std::char_traits<char16_t> operations are written in terms of for loops.

If we change these traits operations for char16_t type (or std::u16string) to use our own customized traits, we are seeing performance improvements with performance comparable to std::wstring.

We are planning to write our own custom traits (until MS fixes it for next version of VS) as follows

struct string_custom_traits : public std::char_traits<char16_t>
{
    static const char16_t * copy(char16_t* dest, const char16_t* src, size_t count)
    {
        return (count == 0 ? src : (char16_t*)std::memcpy(dest, src, count * sizeof(char16_t)));
    }

 };

Would that be OK? Are there any problems with this approach ?

\$\endgroup\$
  • \$\begingroup\$ Interesting. Should by I10n not i18n as the internationalization part is about translating the strings in different languages. The localization part is writing the code in such a way that it can be internationalized. \$\endgroup\$ – Martin York Apr 16 '17 at 16:02
  • \$\begingroup\$ @LokiAstari Why delete the answer you posted ? I was about to accept it... \$\endgroup\$ – Recker Apr 17 '17 at 3:57
  • 1
    \$\begingroup\$ Because a comment by @Martin R make me uncertain I was correct. \$\endgroup\$ – Martin York Apr 17 '17 at 4:18
3
\$\begingroup\$

Nothing in std::char_traits::copy says to return src when count is zero. Further, we must return a pointer to modifiable char_type, which also precludes returning src.

So I think we should write:

#include <cstring>
#include <string>

struct string_custom_traits : public std::char_traits<char16_t>
{
    static char_type * copy(char_type* dest, const char_type* src, size_t count)
    {
        return (char_type*)std::memcpy(dest, src, count * sizeof *src);
    }

    static char_type *move(char_type* dest, const char_type* src, size_t count)
    {
        return (char_type*)std::memmove(dest, src, count * sizeof *src);
    }
};

I've used the template-provided alias char_type for consistency and in case you want to turn this traits class into a template itself.

Make sure you understand and accept the differences between UCS-2 and UTF-16 if you handle characters outside the BMP.

\$\endgroup\$
  • \$\begingroup\$ Well, using std::copy_n is a bit more C++-y, and avoids the need for casting and such. \$\endgroup\$ – Deduplicator Jan 15 '18 at 15:44
  • \$\begingroup\$ Good point - I followed the question's lead (with the ugly cast) instead of thinking from scratch. std::copy_n is a bit more tricky, as it has undefined behaviour if source and destination overlap. Fixing that might reintroduce the performance problems that motivated the code in the first place! \$\endgroup\$ – Toby Speight Jan 15 '18 at 15:46
  • \$\begingroup\$ Well, the behavior is expected to be UB in that case, so nothing lost. \$\endgroup\$ – Deduplicator Jan 15 '18 at 20:34

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.