_mm256_load_ps调试模式下导致谷歌/基准测试的分段错误


  • 以下代码可以在发布和调试模式下运行。
#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);
}
}

  • 以下代码只能在发布模式下运行,并在调试模式下获取分段错误。
#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();

您的数组未按 32 对齐。 您可以使用调试器进行检查。

#pragma pack(32)仅对齐结构/联合/类成员,如 MS 所述。 C++数组是一种不同类型的对象,完全不受该 MSVC 杂注的影响。 (我认为您实际上是在使用它的GCC或clang的版本,因为MSVC通常使用vmovups而不是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];

AVX:数据对齐:存储崩溃,存储,加载,加载u没有解释为什么根据优化级别存在差异:优化的代码会将一个加载折叠到内存源操作数中,用于vmulps(与SSE不同(不需要对齐。 (据推测,第一个数组恰好足够对齐。

未优化的代码将使用vmovaps对齐所需的负载单独执行_mm256_load_ps

(_mm256_loadu_ps将始终避免使用需要对齐的负载,因此,如果您无法保证数据对齐,请使用该负载。

最新更新