这段代码能这段代码有办法做simd加速吗
07 Jul 2024
|
|
群友讨论第一期, 这个栏目要多谢群友交流
本期鸣谢群友 @核聚变引擎
@mwish
群友核聚变引擎 提问
这段代码有办法做simd加速吗,感觉无解 https://gcc.godbolt.org/z/GjjEfz1e8
代码如下
#include <cstdint>
const int M = (1 << 14);
uint16_t bins[64] = {0};
uint8_t data[M] = {0};
// 统计 data 中 0~63 的频数,不考虑高 2 位
void histgram(uint16_t *bins, uint8_t *data) {
for (int i = 0; i < M; i++) {
uint8_t x = data[i] & 0x3f;
bins[x]++;
}
}
这种计算是统计型,关键是统计M输入到64,不是多对1类型的计算,比如popcnt
优化角度无非是攒批/分片
使用chatgpt帮咱写了一个版本
void histgram_avx(uint16_t *bins, uint8_t *data) {
__m256i mask = _mm256_set1_epi8(0x3F);
for (int i = 0; i < M; i += 32) {
__m256i chunk = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(data + i));
chunk = _mm256_and_si256(chunk, mask);
uint8_t tmp[32];
_mm256_storeu_si256(reinterpret_cast<__m256i*>(tmp), chunk);
for (int j = 0; j < 32; ++j) {
bins[tmp[j]]++;
}
}
}
这种加速其实效果非常有限,M也不大,性能提升并不明显
群友mwish提供了一个分片的版本
using Bucket = std::array<uint16_t, 64>;
// 统计 data 中 0~63 的频数,不考虑高 2 位
std::array<Bucket, 8> histgram_mwish(uint16_t *bins, uint8_t *data) {
std::array<Bucket, 8> temp_bins;
for (int i = 0; i < M / 8; i++) {
for (int idx = 0; idx < 8; ++idx) {
uint8_t x = data[i * 8 + idx] % 64;
temp_bins[idx][x]++;
}
}
return temp_bins;
}
我压测了一版数据,机器是2019 mbp 性能一般,结果如下
2024-07-07T18:16:00+08:00
Running ./build/bm_histgram
Run on (12 X 2600 MHz CPU s)
CPU Caches:
L1 Data 32 KiB
L1 Instruction 32 KiB
L2 Unified 256 KiB (x6)
L3 Unified 12288 KiB
Load Average: 2.84, 3.07, 2.77
--------------------------------------------------------------------------------
Benchmark Time CPU Iterations
--------------------------------------------------------------------------------
BM_histgram_mwish/iterations:10000000 7065 ns 7019 ns 10000000
BM_histgram_base/iterations:10000000 6836 ns 6769 ns 10000000
BM_histgram_avx/iterations:10000000 7228 ns 7179 ns 10000000
能看到性能差异基本可以无视,优化反而还慢了
有什么错误或者有什么改善的思路,欢迎提出