VS 2015 std::char_traits<char16_t> operations

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 ?

• 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. – Martin York Apr 16 '17 at 16:02
• @LokiAstari Why delete the answer you posted ? I was about to accept it... – Recker Apr 17 '17 at 3:57
• Because a comment by @Martin R make me uncertain I was correct. – Martin York Apr 17 '17 at 4:18

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.

• Well, using std::copy_n is a bit more C++-y, and avoids the need for casting and such. – Deduplicator Jan 15 '18 at 15:44
• 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! – Toby Speight Jan 15 '18 at 15:46
• Well, the behavior is expected to be UB in that case, so nothing lost. – Deduplicator Jan 15 '18 at 20:34