diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2021-12-20 00:00:39 -0300 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2022-12-04 17:56:45 -0800 |
commit | 3ef43ca837b36cd0aefc925ea08234395dcf49e2 (patch) | |
tree | 7ea94b4d2acf0dc9a15a138c6c6659e7f96c580e /src/corelib | |
parent | 2b9d4afc95a6e716f7bb1839df4041e454aa52af (diff) |
QString::fromLatin1: improve the sub-16-character case
For both the [4, 7] and [8,15] length cases, we can perform the same
technique: perform two overlapped loads, zero-extend, then perform two
overlapped stores. The 8-character case could be done in a single
load/store pair, but is not worth the extra conditionals. And it should
have the exact same performance numbers whether we use non-overlapping
4-character operations or completely-overlapping 8-character ones (I
*think* the full overlap is actually better).
The 4-character operation is new in this commit. That reduces the
non-vectorized, unrolled to at most 3 characters.
Change-Id: Ib42b3adc93bf4d43bd55fffd16c257ada774236a
Reviewed-by: Lars Knoll <lars@knoll.priv.no>
Diffstat (limited to 'src/corelib')
-rw-r--r-- | src/corelib/text/qstring.cpp | 34 |
1 files changed, 21 insertions, 13 deletions
diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp index f41e9377eb..bce00325b9 100644 --- a/src/corelib/text/qstring.cpp +++ b/src/corelib/text/qstring.cpp @@ -804,6 +804,7 @@ Q_CORE_EXPORT void qt_from_latin1(char16_t *dst, const char *str, size_t size) n */ #if defined(__SSE2__) // we're going to read str[offset..offset+15] (16 bytes) + const __m128i nullMask = _mm_setzero_si128(); auto processOneChunk = [=](qptrdiff offset) { const __m128i chunk = _mm_loadu_si128((const __m128i*)(str + offset)); // load if constexpr (UseAvx2) { @@ -813,8 +814,6 @@ Q_CORE_EXPORT void qt_from_latin1(char16_t *dst, const char *str, size_t size) n // store _mm256_storeu_si256((__m256i*)(dst + offset), extended); } else { - const __m128i nullMask = _mm_set1_epi32(0); - // unpack the first 8 bytes, padding with zeros const __m128i firstHalf = _mm_unpacklo_epi8(chunk, nullMask); _mm_storeu_si128((__m128i*)(dst + offset), firstHalf); // store @@ -826,8 +825,8 @@ Q_CORE_EXPORT void qt_from_latin1(char16_t *dst, const char *str, size_t size) n }; const char *e = str + size; - qptrdiff offset = 0; if (size >= sizeof(__m128i)) { + qptrdiff offset = 0; for ( ; str + offset + sizeof(__m128i) <= e; offset += sizeof(__m128i)) processOneChunk(offset); if (str + offset < e) @@ -836,17 +835,26 @@ Q_CORE_EXPORT void qt_from_latin1(char16_t *dst, const char *str, size_t size) n } # if !defined(__OPTIMIZE_SIZE__) - // we're going to read str[offset..offset+7] (8 bytes) - if (str + offset + 8 <= e) { - const __m128i unpacked = mm_load8_zero_extend(str + offset); - _mm_storeu_si128(reinterpret_cast<__m128i *>(dst + offset), unpacked); - offset += 8; + if (size >= 4) { + // two overlapped loads & stores, of either 64-bit or of 32-bit + if (size >= 8) { + const __m128i unpacked1 = mm_load8_zero_extend(str); + const __m128i unpacked2 = mm_load8_zero_extend(str + size - 8); + _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), unpacked1); + _mm_storeu_si128(reinterpret_cast<__m128i *>(dst + size - 8), unpacked2); + } else { + const __m128i chunk1 = _mm_cvtsi32_si128(qFromUnaligned<quint32>(str)); + const __m128i chunk2 = _mm_cvtsi32_si128(qFromUnaligned<quint32>(str + size - 4)); + const __m128i unpacked1 = _mm_unpacklo_epi8(chunk1, nullMask); + const __m128i unpacked2 = _mm_unpacklo_epi8(chunk2, nullMask); + _mm_storel_epi64(reinterpret_cast<__m128i *>(dst), unpacked1); + _mm_storel_epi64(reinterpret_cast<__m128i *>(dst + size - 4), unpacked2); + } + return; + } else { + size = size % 4; + return UnrollTailLoop<3>::exec(qsizetype(size), [=](int i) { dst[i] = (uchar)str[i]; }); } - - size = size % 8; - dst += offset; - str += offset; - return UnrollTailLoop<7>::exec(qsizetype(size), [=](qsizetype i) { dst[i] = (uchar)str[i]; }); # endif #endif #if defined(__mips_dsp) |