рдкрд┐рдЫрд▓реЗ рднрд╛рдЧ рдиреЗ рдПрдХ рдЧрд░реНрдо рдЪрд░реНрдЪрд╛ рдХрд╛ рдХрд╛рд░рдг рдмрдирд╛, рдЬрд┐рд╕рдХреЗ рджреМрд░рд╛рди рдпрд╣ рдкрддрд╛ рдЪрд▓рд╛ рдХрд┐ AVX / AVX2 рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдбреЗрд╕реНрдХрдЯреЙрдк CPUs рдореЗрдВ рд╣реИ, рди рдХреЗрд╡рд▓ AVX212ред рдЗрд╕рд▓рд┐рдП, рд╣рдо SIMD рд╕реЗ рдкрд░рд┐рдЪрд┐рдд рд╣реЛрдирд╛ рдЬрд╛рд░реА рд░рдЦрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдЗрд╕рдХреЗ рдЖрдзреБрдирд┐рдХ рднрд╛рдЧ - AVX рдХреЗ рд╕рд╛рдеред рдФрд░ рднреА рд╣рдо рдХреБрдЫ рдЯрд┐рдкреНрдкрдгрд┐рдпреЛрдВ рдХрд╛ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХрд░реЗрдВрдЧреЗ:
- рдХреНрдпрд╛
_mm256_load_si256
рд╕реАрдзреА рдореЗрдореЛрд░реА рдПрдХреНрд╕реЗрд╕ рд╕реЗ рдзреАрдореА рд╣реИ? - SSE рд░рдЬрд┐рд╕реНрдЯрд░реЛрдВ рдкрд░ AVX рдХрдорд╛рдВрдб рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рд╕реЗ рдЧрддрд┐ рдкреНрд░рднрд╛рд╡рд┐рдд рд╣реЛрддреА рд╣реИ?
- рдХреНрдпрд╛
_popcnt
рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдмреБрд░рд╛ рд╣реИ?
AVX рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдереЛрдбрд╝рд╛ рд╕рд╛
AVX / AVX2 SSE рдХрд╛ рдПрдХ рдЕрдзрд┐рдХ рд╢рдХреНрддрд┐рд╢рд╛рд▓реА рд╕рдВрд╕реНрдХрд░рдг рд╣реИ рдЬреЛ рдЕрдзрд┐рдХрд╛рдВрд╢ 128-рдмрд┐рдЯ SSE рд╕рдВрдЪрд╛рд▓рди рдХреЛ 256 рдмрд┐рдЯ рддрдХ рдмрдврд╝рд╛рддрд╛ рд╣реИ, рд╕рд╛рде рд╣реА рдХрдИ рдирдП рдирд┐рд░реНрджреЗрд╢ рднреА рд▓рд╛рддрд╛ рд╣реИред
рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреА рд╕реВрдХреНрд╖реНрдорддрд╛рдУрдВ рд╕реЗ, рд╣рдо рдпрд╣ рднреЗрдж рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдХреЛрдбрд╛рдВрддрд░рдХ рд╕реНрддрд░ рдкрд░ AVX 3 рддрд░реНрдХреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИ, рдЬреЛ рдкрд╣рд▓реЗ рджреЛ рдореЗрдВ рдбреЗрдЯрд╛ рдХреЛ рдирд╖реНрдЯ рдирд╣реАрдВ рдХрд░рдиреЗ рджреЗрддрд╛ рд╣реИред SSE рдкрд░рд┐рдгрд╛рдо рдХреЛ рдПрдХ рддрд░реНрдХ рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рддрд╛ рд╣реИред
рдпрд╣ рднреА рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП рдХрд┐ рдкреНрд░рддреНрдпрдХреНрд╖ рдкрддреЗ рдХреЗ рд╕рд╛рде, рдбреЗрдЯрд╛ рдХреЛ 32 рдмрд╛рдЗрдЯреНрд╕ рджреНрд╡рд╛рд░рд╛, рдПрд╕рдПрд╕рдИ рдореЗрдВ 16 рджреНрд╡рд╛рд░рд╛ рд╕рдВрд░реЗрдЦрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред
рдмреЗрдВрдЪрдорд╛рд░реНрдХ рдХрд╛ рд╕рдВрд╡рд░реНрдзрд┐рдд рд╕рдВрд╕реНрдХрд░рдг
рдкрд░рд┐рд╡рд░реНрддрди:
- рддрддреНрд╡реЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ 10,000 рдЧреБрдирд╛ (10,240,000 рддрдХ) рдмрдврд╝рд╛рдИ рдЧрдИ рд╣реИ, рддрд╛рдХрд┐ рдкреНрд░реЛрд╕реЗрд╕рд░ рдХреИрд╢ рдореЗрдВ рдлрд┐рдЯ рди рд╣реЛред
- AVX рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рдВрд░реЗрдЦрдг 16 рдмрд╛рдЗрдЯреНрд╕ рд╕реЗ 32 рддрдХ рдмрджрд▓ рдЧрдпрд╛ред
- SSE рдХреЗ рд╕рдорд╛рди AVX рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдЬреЛрдбрд╝рд╛ рдЧрдпрд╛ред
рдмреЗрдВрдЪрдорд╛рд░реНрдХ рдХреЛрдб #include <benchmark/benchmark.h> #include <x86intrin.h> #include <cstring> #define ARR_SIZE 10240000 #define VAL 50 static int16_t *getRandArr() { auto res = new int16_t[ARR_SIZE]; for (int i = 0; i < ARR_SIZE; ++i) { res[i] = static_cast<int16_t>(rand() % (VAL * 2)); } return res; } static auto arr = getRandArr(); static int16_t *getAllignedArr() { auto res = aligned_alloc(32, sizeof(int16_t) * ARR_SIZE); memcpy(res, arr, sizeof(int16_t) * ARR_SIZE); return static_cast<int16_t *>(res); } static auto allignedArr = getAllignedArr(); static void BM_Count(benchmark::State &state) { for (auto _ : state) { int64_t cnt = 0; for (int i = 0; i < ARR_SIZE; ++i) if (arr[i] == VAL) ++cnt; benchmark::DoNotOptimize(cnt); } } BENCHMARK(BM_Count); static void BM_SSE_COUNT_SET_EPI(benchmark::State &state) { for (auto _ : state) { int64_t cnt = 0; auto sseVal = _mm_set1_epi16(VAL); for (int i = 0; i < ARR_SIZE; i += 8) { cnt += _popcnt32( _mm_movemask_epi8( _mm_cmpeq_epi16( sseVal, _mm_set_epi16(arr[i + 7], arr[i + 6], arr[i + 5], arr[i + 4], arr[i + 3], arr[i + 2], arr[i + 1], arr[i]) ) ) ); } benchmark::DoNotOptimize(cnt >> 1); } } BENCHMARK(BM_SSE_COUNT_SET_EPI); static void BM_SSE_COUNT_LOADU(benchmark::State &state) { for (auto _ : state) { int64_t cnt = 0; auto sseVal = _mm_set1_epi16(VAL); for (int i = 0; i < ARR_SIZE; i += 8) { cnt += _popcnt32( _mm_movemask_epi8( _mm_cmpeq_epi16( sseVal, _mm_loadu_si128((__m128i *) &arr[i]) ) ) ); } benchmark::DoNotOptimize(cnt >> 1); } } BENCHMARK(BM_SSE_COUNT_LOADU); static void BM_SSE_COUNT_DIRECT(benchmark::State &state) { for (auto _ : state) { int64_t cnt = 0; auto sseVal = _mm_set1_epi16(VAL); for (int i = 0; i < ARR_SIZE; i += 8) { cnt += _popcnt32( _mm_movemask_epi8( _mm_cmpeq_epi16( sseVal, *(__m128i *) &allignedArr[i] ) ) ); } benchmark::DoNotOptimize(cnt >> 1); } } BENCHMARK(BM_SSE_COUNT_DIRECT); #ifdef __AVX2__ static void BM_AVX_COUNT_LOADU(benchmark::State &state) { for (auto _ : state) { int64_t cnt = 0; auto avxVal = _mm256_set1_epi16(VAL); for (int i = 0; i < ARR_SIZE; i += 16) { cnt += _popcnt32( _mm256_movemask_epi8( _mm256_cmpeq_epi16( avxVal, _mm256_loadu_si256((__m256i *) &arr[i]) ) ) ); } benchmark::DoNotOptimize(cnt >> 1); } } BENCHMARK(BM_AVX_COUNT_LOADU); static void BM_AVX_COUNT_LOAD(benchmark::State &state) { for (auto _ : state) { int64_t cnt = 0; auto avxVal = _mm256_set1_epi16(VAL); for (int i = 0; i < ARR_SIZE; i += 16) { cnt += _popcnt32( _mm256_movemask_epi8( _mm256_cmpeq_epi16(avxVal, _mm256_load_si256((__m256i *) &allignedArr[i]) ) ) ); } benchmark::DoNotOptimize(cnt >> 1); } } BENCHMARK(BM_AVX_COUNT_LOAD); static void BM_AVX_COUNT_DIRECT(benchmark::State &state) { for (auto _ : state) { int64_t cnt = 0; auto avxVal = _mm256_set1_epi16(VAL); for (int i = 0; i < ARR_SIZE; i += 16) { cnt += _popcnt32( _mm256_movemask_epi8( _mm256_cmpeq_epi16( avxVal, *(__m256i *) &allignedArr[i] ) ) ); } benchmark::DoNotOptimize(cnt >> 1); } } BENCHMARK(BM_AVX_COUNT_DIRECT); #endif BENCHMARK_MAIN();
рдирдП рдкрд░рд┐рдгрд╛рдо рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддреЗ рд╣реИрдВ (-O0):
--------------------------------------------------------------------- Benchmark Time CPU Iterations --------------------------------------------------------------------- BM_Count 17226622 ns 17062958 ns 41 BM_SSE_COUNT_SET_EPI 8901343 ns 8814845 ns 79 BM_SSE_COUNT_LOADU 3664778 ns 3664766 ns 185 BM_SSE_COUNT_DIRECT 3468436 ns 3468423 ns 202 BM_AVX_COUNT_LOADU 2090817 ns 2090796 ns 343 BM_AVX_COUNT_LOAD 1904424 ns 1904419 ns 364 BM_AVX_COUNT_DIRECT 1814875 ns 1814854 ns 385
рдХреБрд▓ рдХреБрд▓ рддреНрд╡рд░рдг 9+ рдмрд╛рд░ рд╣реИ, рдПрд╡реАрдПрдХреНрд╕ рдПрд╕рдПрд╕рдИ рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рд▓рдЧрднрдЧ 2 рдЧреБрдирд╛ рддреЗрдЬ рд╣реЛрдиреЗ рдХреА рдЙрдореНрдореАрдж рд╣реИред
рдХреНрдпрд╛ _mm256_load_si256
рд╕реАрдзреА рдореЗрдореЛрд░реА рдПрдХреНрд╕реЗрд╕ рд╕реЗ _mm256_load_si256
рд╣реИ?
рдЗрд╕рдХрд╛ рдХреЛрдИ рдирд┐рд╢реНрдЪрд┐рдд рдЙрддреНрддрд░ рдирд╣реАрдВ рд╣реИред C -O0
рд╕реАрдзреА рдкрд╣реБрдВрдЪ рд╕реЗ рдзреАрдореА рд╣реИ, рд▓реЗрдХрд┐рди _mm256_loadu_si256
рддреБрд▓рдирд╛ рдореЗрдВ _mm256_loadu_si256
:
--------------------------------------------------------------------- Benchmark Time CPU Iterations --------------------------------------------------------------------- BM_AVX_COUNT_LOADU 2090817 ns 2090796 ns 343 BM_AVX_COUNT_LOAD 1904424 ns 1904419 ns 364 BM_AVX_COUNT_DIRECT 1814875 ns 1814854 ns 385
C _mm256_loadu_si256
рдбрд╛рдпрд░реЗрдХреНрдЯ рдореЗрдореЛрд░реА рдПрдХреНрд╕реЗрд╕ рд╕реЗ рддреЗрдЬ рд╣реИ, рд▓реЗрдХрд┐рди рдлрд┐рд░ рднреА рдЙрдореНрдореАрдж рд╕реЗ рдХрдо рд╣реИ _mm256_loadu_si256
ред
--------------------------------------------------------------------- Benchmark Time CPU Iterations --------------------------------------------------------------------- BM_AVX_COUNT_LOADU 992319 ns 992368 ns 701 BM_AVX_COUNT_LOAD 956120 ns 956166 ns 712 BM_AVX_COUNT_DIRECT 1027624 ns 1027674 ns 730
рдЙрддреНрдкрд╛рджрди рдХреЛрдб рдореЗрдВ, рдкреНрд░рддреНрдпрдХреНрд╖ рдкрд╣реБрдВрдЪ рдХреЗ рдмрдЬрд╛рдп _mm256_load_si256
рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдЕрднреА рднреА рдмреЗрд╣рддрд░ рд╣реИ; рд╕рдВрдХрд▓рдХ рдЗрд╕ рд╡рд┐рдХрд▓реНрдк рдХреЛ рдмреЗрд╣рддрд░ рдврдВрдЧ рд╕реЗ рдЕрдиреБрдХреВрд▓рд┐рдд рдХрд░ рд╕рдХрддрд╛ рд╣реИред
рдХреНрдпрд╛ AVX рдХрдорд╛рдВрдб рдХрд╛ рдЙрдкрдпреЛрдЧ SSE рд░рдЬрд┐рд╕реНрдЯрд░реЛрдВ рдХреА рдЧрддрд┐ рдХреЛ рдкреНрд░рднрд╛рд╡рд┐рдд рдХрд░рддрд╛ рд╣реИ?
рд╕рдВрдХреНрд╖рд┐рдкреНрдд рдЙрддреНрддрд░ рдирд╣реАрдВ рд╣реИ ред рдкреНрд░рдпреЛрдЧ рдХреЗ рд▓рд┐рдП, рдореИрдВрдиреЗ -mavx2
рдФрд░ -mavx2
рд╕рд╛рде рдмреЗрдВрдЪрдорд╛рд░реНрдХ рд╕рдВрдХрд▓рд┐рдд рдФрд░ рдЪрд▓рд╛рдпрд╛ред
-mavx2
_popcnt32(_mm_movemask_epi8(_mm_cmpeq_epi16(...)))
рдмрджрд▓ рдЬрд╛рддрд╛ рд╣реИ
vpcmpeqw %xmm1,%xmm0,%xmm0 vpmovmskb %xmm0,%edx popcnt %edx,%edx
рдкрд░рд┐рдгрд╛рдо:
------------------------------------------------------------ Benchmark Time CPU Iterations ------------------------------------------------------------ BM_SSE_COUNT_SET_EPI 9376699 ns 9376767 ns 75 BM_SSE_COUNT_LOADU 4425510 ns 4425560 ns 159 BM_SSE_COUNT_DIRECT 3938604 ns 3938648 ns 177
-msse4.2
_popcnt32(_mm_movemask_epi8(_mm_cmpeq_epi16(...)))
рдмрджрд▓ рдЬрд╛рддрд╛ рд╣реИ
pcmpeqw %xmm1,%xmm0 pmovmskb %xmm0,%edx popcnt %edx,%edx
рдкрд░рд┐рдгрд╛рдо:
------------------------------------------------------------ Benchmark Time CPU Iterations ------------------------------------------------------------ BM_SSE_COUNT_SET_EPI 9309352 ns 9309375 ns 76 BM_SSE_COUNT_LOADU 4382183 ns 4382195 ns 159 BM_SSE_COUNT_DIRECT 3944579 ns 3944590 ns 176
рдмреЛрдирд╕
AVX рдХрдорд╛рдВрдб _popcnt32(_mm256_movemask_epi8(_mm256_cmpeq_epi16(...)))
рдмрджрд▓ рдЬрд╛рддреА рд╣реИ
vpcmpeqw %ymm1,%ymm0,%ymm0 vpmovmskb %ymm0,%edx popcnt %edx,%edx
рдХреНрдпрд╛ _popcnt
рдЗрддрдирд╛ рдмреБрд░рд╛ рд╣реИ?
рдЯрд┐рдкреНрдкрдгрд┐рдпреЛрдВ рдореЗрдВ рд╕реЗ рдПрдХ рдореЗрдВ Antervis рдиреЗ рд▓рд┐рдЦрд╛ рд╣реИ:
рдФрд░ рдлрд┐рд░ рднреА, рдЖрдкрдиреЗ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХреЛ рдереЛрдбрд╝рд╛ рджреЛрд╖ рджрд┐рдпрд╛ рд╣реИред рдЗрд╕реЗ рдореВрд╡рдорд╕реНрдХ + рдкреЙрдкрдХрдВрдЯ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдХреНрдпреЛрдВ рдХрд░рддреЗ рд╣реИрдВ? 2 ^ 18 рддрддреНрд╡реЛрдВ рд╕реЗ рдЕрдзрд┐рдХ рдХреЗ рд╕рд░рдгрд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП, рдЖрдк рдкрд╣рд▓реЗ рддрддреНрд╡-рдмрд╛рдп-рдПрд▓реАрдореЗрдВрдЯ рд░рд╛рд╢рд┐ рдПрдХрддреНрд░ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:
рдСрдЯреЛ cmp = _mm_cmpeq_epi16 (sseVal, sseArr);
cmp = _mm_and_si128 (cmp, _mm_set1_epi16 (1));
sum = _mm_add_epi16 (sum, cmp);
рдФрд░ рдлрд┐рд░, рдЪрдХреНрд░ рдХреЗ рдЕрдВрдд рдореЗрдВ, рдПрдХ рдХреНрд╖реИрддрд┐рдЬ рдЬреЛрдбрд╝ рдмрдирд╛рдПрдВ (рдЕрддрд┐рдкреНрд░рд╡рд╛рд╣ рдирд╣реАрдВ рднреВрд▓рдирд╛)ред
рдореИрдВрдиреЗ рдПрдХ рдмреЗрдВрдЪрдорд╛рд░реНрдХ рдмрдирд╛рдпрд╛
static void BM_AVX_COUNT_DIRECT_WO_POPCNT(benchmark::State &state) { auto avxVal1 = _mm256_set1_epi16(1); for (auto _ : state) { auto sum = _mm256_set1_epi16(0); auto avxVal = _mm256_set1_epi16(VAL); for (int i = 0; i < ARR_SIZE; i += 16) { sum = _mm256_add_epi16( sum, _mm256_and_si256( avxVal1, _mm256_cmpeq_epi16( avxVal, *(__m256i *) &allignedArr[i]) ) ); } auto arrSum = (uint16_t *) ∑ size_t cnt = 0; for (int j = 0; j < 16; ++j) cnt += arrSum[j]; benchmark::DoNotOptimize(cnt >> 1); } }
рдФрд░ рдпрд╣ c -O0
рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рдзреАрдорд╛ -O0
:
--------------------------------------------------------------------- Benchmark Time CPU Iterations --------------------------------------------------------------------- BM_AVX_COUNT_DIRECT 1814821 ns 1814785 ns 392 BM_AVX_COUNT_DIRECT_WO_POPCNT 2386289 ns 2386227 ns 287
рдФрд░ -O3
рд╕рд╛рде рдереЛрдбрд╝рд╛ рддреЗрдЬ:
--------------------------------------------------------------------- Benchmark Time CPU Iterations --------------------------------------------------------------------- BM_AVX_COUNT_DIRECT 960941 ns 960924 ns 722 BM_AVX_COUNT_DIRECT_WO_POPCNT 948611 ns 948596 ns 732