QString::toLatin1: make the 32-byte loop to a single 32-btye load

Instead of two 16-byte loads, if AVX2 is present. Otherwise, it's
exactly the same.

Because of the way the SIMD instructions were extended to 256-bit in
AVX2, we gain nothing doing two 32-byte loads, aside from the loop
unrolling.

Change-Id: Ib48364abee9f464c96c6fffd152e531925814ac2
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
bb10
Thiago Macieira 2018-05-13 14:58:11 -07:00
parent a9074779cf
commit e03c6c19fc
1 changed files with 20 additions and 0 deletions

View File

@ -543,8 +543,15 @@ static void qt_to_latin1_internal(uchar *dst, const ushort *src, qsizetype lengt
uchar *e = dst + length;
qptrdiff offset = 0;
# ifdef __AVX2__
const __m256i questionMark256 = _mm256_broadcastw_epi16(_mm_cvtsi32_si128('?'));
const __m256i outOfRange256 = _mm256_broadcastw_epi16(_mm_cvtsi32_si128(0x100));
const __m128i questionMark = _mm256_castsi256_si128(questionMark256);
const __m128i outOfRange = _mm256_castsi256_si128(outOfRange256);
# else
const __m128i questionMark = _mm_set1_epi16('?');
const __m128i outOfRange = _mm_set1_epi16(0x100);
# endif
auto mergeQuestionMarks = [=](__m128i chunk) {
// SSE has no compare instruction for unsigned comparison.
@ -579,6 +586,18 @@ static void qt_to_latin1_internal(uchar *dst, const ushort *src, qsizetype lengt
// we're going to write to dst[offset..offset+15] (16 bytes)
for ( ; dst + offset + 15 < e; offset += 16) {
# if defined(__AVX2__)
__m256i chunk = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + offset));
if (Checked) {
// See mergeQuestionMarks lambda above for details
chunk = _mm256_min_epu16(chunk, outOfRange256);
const __m256i offLimitMask = _mm256_cmpeq_epi16(chunk, outOfRange256);
chunk = _mm256_blendv_epi8(chunk, questionMark256, offLimitMask);
}
const __m128i chunk2 = _mm256_extracti128_si256(chunk, 1);
const __m128i chunk1 = _mm256_castsi256_si128(chunk);
# else
__m128i chunk1 = _mm_loadu_si128((const __m128i*)(src + offset)); // load
if (Checked)
chunk1 = mergeQuestionMarks(chunk1);
@ -586,6 +605,7 @@ static void qt_to_latin1_internal(uchar *dst, const ushort *src, qsizetype lengt
__m128i chunk2 = _mm_loadu_si128((const __m128i*)(src + offset + 8)); // load
if (Checked)
chunk2 = mergeQuestionMarks(chunk2);
# endif
// pack the two vector to 16 x 8bits elements
const __m128i result = _mm_packus_epi16(chunk1, chunk2);