这个问题是在编写一些与计算机图形相关的代码时发现的,代码的简化版本如下所示:
#include <bits/stdc++.h>
#define __AVX__ 1
#define __AVX2__ 1
#pragma GCC target("avx,avx2,popcnt,tune=native")
#include <immintrin.h>
namespace with_avx {
class vec {
public:
vec(double x = 0, double y = 0, double z = 0, double t = 0) {
vec_data = _mm256_set_pd(t, z, y, x);
}
__m256d vec_data;
};
} // namespace with_avx
namespace without_avx {
class vec {
public:
vec(double x = 0, double y = 0, double z = 0, double t = 0) {
vec_data[0] = x, vec_data[1] = y, vec_data[2] = z, vec_data[3] = t;
}
double vec_data[4];
};
} // namespace without_avx
#ifdef USE_AVX
using namespace with_avx;
#else
using namespace without_avx;
#endif
vec same(vec x) { return x; }
std::function<vec(vec)> stdfunc = same;
int main() {
vec rand_vec(rand(), rand(), rand());
vec ret = stdfunc(rand_vec);
std::cout<<(double)ret.vec_data[0];
}
如果我编译带有USE_AVX
标志的代码,如下所示:
g++-12 stdfunction_test.cpp -o ../build/unit_test -D USE_AVX -g
g++将输出一些警告:
In file included from /usr/include/c++/12/functional:59,
from /usr/include/x86_64-linux-gnu/c++/12/bits/stdc++.h:71,
from stdfunction_test.cpp:2:
/usr/include/c++/12/bits/std_function.h: In member function ‘_Res std::function<_Res(_ArgTypes ...)>::operator()(_ArgTypes ...) const [with _Res = with_avx::vec; _ArgTypes = {with_avx::vec}]’:
/usr/include/c++/12/bits/std_function.h:587:7: note: the ABI for passing parameters with 32-byte alignment has changed in GCC 4.6
587 | operator()(_ArgTypes... __args) const
| ^~~~~~~~
然后,如果我运行代码,有时会导致分段错误,输出如下:
[1] 12710 segmentation fault ../build/unit_test
有时会抛出bad_function_call,并显示以下输出:
terminate called after throwing an instance of 'std::bad_function_call'
what(): bad_function_call
[1] 12678 IOT instruction ../build/unit_test
当执行此行时,会发生这两个错误:
vec ret = stdfunc(rand_vec);
然后我使用gdb进行回溯:
(gdb) bt
#0 0x00007ffff7e35521 in __cxa_throw () from /lib/x86_64-linux-gnu/libstdc++.so.6
#1 0x00007ffff7e2c6f4 in std::__throw_bad_function_call() () from /lib/x86_64-linux-gnu/libstdc++.so.6
#2 0x000055555555558b in std::function<with_avx::vec (with_avx::vec)>::operator()(with_avx::vec) const (this=0x7fffffffda74,
__args#0=...) at /usr/include/c++/12/bits/std_function.h:590
#3 0x000055555555528d in main () at stdfunction_test.cpp:39
但是,如果我不添加标志,代码将正常运行。
我想这可能是由某种对齐问题引起的,比如警告说我不知道如何解决这个问题。
下面列出了我的环境,希望它们会有用:
- g++版本:
g++-12 (Ubuntu 12-20220319-1ubuntu1) 12.0.1 20220319 (experimental) [master r12-7719-g8ca61ad148f]
- 操作系统:Ubuntu
1条答案
按热度按时间l0oc07j21#
在文件的中途更改目标架构导致了您的问题。可能是
std::function
的部分实现随目标架构而更改。将编译指示移到文件的开头可以解决此问题:https://godbolt.org/z/WP5ah38WP如果您通过编译器命令行(例如
-mavx2
)设置架构目标,通常会更安全,这将确保您的所有代码都使用相同的架构进行编译:https://godbolt.org/z/z5j79c5eh或者更好的方法是,使用
-march=haswell
或-march=native
来设置调整选项并启用相关的伊萨功能(如BMI 1/2),因为Why doesn't gcc resolve _mm256_loadu_pd as single vmovupd?当AVX可用时,传递
double __attribute__((vector_size(32)))
(如__m256d
)的调用约定会发生变化。正如你在Godbolt上看到的,没有AVX,它是通过一个隐藏的指针(在RDI中)返回到返回值对象的。假设AVX调用约定的调用者不会将RDI设置为一个有效的指针,只需在YMM 0中传递它。(对于按值传递,在堆栈上与在YMM 0中将导致错误的数据,但不会直接导致segfault。)
定义
std::function
成员函数时 * 没有 * AVX,因为您在pragma
之前包含了C++标准头文件。但您后面的代码将在__m256d
中使用它。