cha*_*ink 0 c++ simd segmentation-fault avx google-benchmark
#include <immintrin.h>
constexpr int n_batch = 10240;
constexpr int n = n_batch * 8;
#pragma pack(32)
float a[n];
float b[n];
float c[n];
#pragma pack()
int main() {
for(int i = 0; i < n; ++i)
c[i] = a[i] * b[i];
for(int i = 0; i < n; i += 4) {
__m128 av = _mm_load_ps(a + i);
__m128 bv = _mm_load_ps(b + i);
__m128 cv = _mm_mul_ps(av, bv);
_mm_store_ps(c + i, cv);
}
for(int i = 0; i < n; i += 8) {
__m256 av = _mm256_load_ps(a + i);
__m256 bv = _mm256_load_ps(b + i);
__m256 cv = _mm256_mul_ps(av, bv);
_mm256_store_ps(c + i, cv);
}
}
Run Code Online (Sandbox Code Playgroud)
#include <immintrin.h>
#include "benchmark/benchmark.h"
constexpr int n_batch = 10240;
constexpr int n = n_batch * 8;
#pragma pack(32)
float a[n];
float b[n];
float c[n];
#pragma pack()
static void BM_Scalar(benchmark::State &state) {
for(auto _: state)
for(int i = 0; i < n; ++i)
c[i] = a[i] * b[i];
}
BENCHMARK(BM_Scalar);
static void BM_Packet_4(benchmark::State &state) {
for(auto _: state) {
for(int i = 0; i < n; i += 4) {
__m128 av = _mm_load_ps(a + i);
__m128 bv = _mm_load_ps(b + i);
__m128 cv = _mm_mul_ps(av, bv);
_mm_store_ps(c + i, cv);
}
}
}
BENCHMARK(BM_Packet_4);
static void BM_Packet_8(benchmark::State &state) {
for(auto _: state) {
for(int i = 0; i < n; i += 8) {
__m256 av = _mm256_load_ps(a + i); // Signal: SIGSEGV (signal SIGSEGV: invalid address (fault address: 0x0))
__m256 bv = _mm256_load_ps(b + i);
__m256 cv = _mm256_mul_ps(av, bv);
_mm256_store_ps(c + i, cv);
}
}
}
BENCHMARK(BM_Packet_8);
BENCHMARK_MAIN();
Run Code Online (Sandbox Code Playgroud)
您的数组未按 32 对齐。您可以使用调试器进行检查。
#pragma pack(32)只对齐 struct/union/class 成员,如 MS 所述。C++ 数组是一种不同类型的对象,完全不受 MSVC 编译指示的影响。(不过,我认为您实际上使用的是 GCC 或 clang 的版本,因为 MSVC 通常使用vmovupsnot vmovaps)
对于静态或自动存储(非动态分配)中的数组,在 C++11 及更高版本中对齐数组的最简单方法是alignas(32). 这是完全可移植的,不像 GNU C__attribute__((aligned(32)))或任何 MSVC 的等价物。
alignas(32) float a[n];
alignas(32) float b[n];
alignas(32) float c[n];
Run Code Online (Sandbox Code Playgroud)
AVX:数据对齐:存储崩溃、存储、加载、加载并没有解释为什么根据优化级别存在差异:优化代码会将一个加载折叠到一个内存源操作数vmulps中(与 SSE 不同)不需要对齐。(大概第一个数组恰好足够对齐。)
未优化的代码将_mm256_load_ps单独执行需要vmovaps对齐的加载。
(_mm256_loadu_ps将始终避免使用需要对齐的负载,因此如果您不能保证您的数据是对齐的,请使用它。)