当然,为了使典型的现代处理器架构(如x86_64)执行原子加载或存储,需要对齐要读取/写入的数据。
但是这个要求是如何通过C ++ 11 <atomic>
变量实际实现/实施的?
假设我有一个支持16字节比较和交换(双字CAS)的架构,因此它可以原子地读/写16字节值,并且我定义了一个16字节类型:
struct double_word
{
std::uint64_t x;
std::uint64_t y;
};
现在,假设我将std::atomic<double_word>
包含为类的成员字段:
class foo
{
public:
std::atomic<double_word> dword;
};
我如何知道foo::dword
实际上是在16字节边界上对齐的?我怎么知道对dword.load()
的调用实际上是原子?
实际上,我最初开始问这个问题是因为我在foo::dword
之前添加了另一个数据成员时发生了一件奇怪的事情。我将foo
定义为:
class foo
{
public:
std::uint64_t x;
std::atomic<double_word> dword;
};
当我在foo::dword
上实际执行原子加载,并在运行Debian Linux的x86_64机器上使用GCC 4.7.2编译和运行时,它实际上给了我一个分段错误!
完整计划:
#include <atomic>
#include <cstdint>
struct double_word
{
std::uint64_t x;
std::uint64_t y;
};
class foo
{
public:
std::uint64_t x;
std::atomic<double_word> dword; // <-- not aligned on 16-byte boundary
};
int main()
{
foo f;
double_word d = f.dword.load(); // <-- segfaults with GCC 4.7.2 !!
}
这实际上是f.dword.load()
上的段错误。起初我不明白为什么,但后来我意识到dword
不 在16字节边界上对齐。因此,这导致了许多问题:如果原子变量未对齐并且我们尝试以原子方式加载它,编译器应该怎么做?是不确定的行为?为什么程序只是段错误?
其次,C ++ 11标准对此有何看法?编译器是否应确保double_word
在16字节边界上自动对齐?如果是这样,这是否意味着GCC在这里只是错误?如果不是 - 似乎用户需要确保对齐,在这种情况下任何时间我们使用大于一个字节的std::atomic<T>
,看起来我们 使用std::aligned_storage
来确保它正确对齐,(A)看起来很麻烦,而且(B)是我在实践中或在任何示例/教程中从未真正看到过的东西。
那么,使用C ++ 11 <atomic>
的程序员应如何处理这样的对齐问题呢?
答案 0 :(得分:1)
这是一个GCC错误https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65147
只需添加alignas(16)
就可以解决问题。
#include <atomic>
#include <cstdint>
struct double_word
{
std::uint64_t x;
std::uint64_t y;
};
class foo
{
public:
std::uint64_t x;
alignas(16) std::atomic<double_word> dword; // <-- not aligned on 16-byte boundary
};
int main()
{
foo f;
double_word d = f.dword.load(); // <-- segfaults with GCC 4.7.2 !!
}
答案 1 :(得分:0)
我认为程序员有责任确保dword是16字节对齐。在64位平台上,除非明确指定,否则数据将以64位边界对齐。