Ale*_*iev 2 c++ memory-alignment undefined-behavior stdatomic c++20
我在p0019r8上读到以下内容:
Run Code Online (Sandbox Code Playgroud)atomic_ref(T& obj);要求:引用的对象应与 对齐
required_alignment。
当未对齐时, cppreference将其解释为 UB:
如果 obj 未与 required_alignment 对齐,则行为未定义。
那么您期望实现如何处理它呢?
并且实现可以检查编译时 alignof,但实际上类型可能比对齐更对齐alignof。实现可以解释指针位并检查运行时对齐,但这是额外的运行时检查。
最终我看到以下选项:
alignof) 并在错误时发出警告alignof) ,如果错误不正确,则编译时失败,alignof),如果错误不正确,则在运行时失败,alignof) 并在错误时回退到基于锁TL:DR:永远不要默默地回退到锁定,没有人希望这样做,因为它违背了std::atomic. 将非无锁视为可移植性的后备方案,而不是可行的操作模式。
UB 使得编译器可以合法地简单地假设而不检查它是否对齐。能够在没有任何运行时检查的情况下进行假设是UB 概念的主要好处之一。这是大多数人在运行时在优化构建中想要/期望的,而不是使用可能回退到使用互斥体的条件分支来使代码膨胀。
选择是否(以及如何)在这里定义任何行为完全取决于实现,这是实现质量以及性能与调试之间权衡的问题。我认为您知道这一点,并且实际上是在询问用户希望编译器为这些 QoI 选择选择什么,这很好。
正如您链接的 P0019 提案所说,这一切都归结为 QOI 问题:
- 参考能力约束
原子引用引用的对象必须满足可能的特定于体系结构的约束。例如,对象可能需要在内存中正确对齐,或者可能不允许驻留在 GPU 寄存器内存中。我们不会枚举所有潜在的约束或指定违反这些约束时的行为。当违反约束时生成适当的信息是一个实施质量问题。
“生成适当的信息”措辞意味着他们希望实现在检测到违规时发出警告/错误,而不是回退到锁定。
尽管可以回退到锁定的实现可能会愚蠢地设置required_alignment为正确性的最小值 (1),而不是无锁的最小值。当然没有人希望这样,但这是一个 QoI 问题,而不是标准合规性问题。
我期望(或者至少希望)实现如下:
如果在任何小于 的atomic_ref对象上使用,则在编译时发出警告。您可能知道某个字节恰好是 8 字节对齐的,即使只有 1 或 4 个字节,因此这不应该是一个错误。alignofrequired_alignmentT *palignof(T)
一些当地的方法来消除警告将是一件好事。(替代方案:使用 GNU C 之类的东西来保证与编译器的对齐x = __builtin_assume_aligned(x, 16))
至少在编译时明确知道某个对象未对齐时发出警告,例如,其对齐方式已知的结构的子成员,或者声明可见但不包含的全局变量alignas。通过可能未对齐的指针进行访问的警告会产生更多噪音,应单独禁用。
超慢调试模式:运行时检查对齐情况,警告或中止原子性未对齐的特定对象。(例如gcc -fsanitize=undefined,或者 MSVC 的调试模式已经添加了诸如std::vector::operator[]边界检查之类的内容。我认为 GCC 的 UBSan 比 MSVC 调试模式做了更多的检查,例如签名溢出;我认为 MSVC 调试模式介于 和 之间gcc -O0。gcc -O0 -fsanitize=undefined)
“Release”模式:零检查,只发出asm,其正确性取决于正在对齐的对象。(还有-O0不带 UBSan 的 gcc,它允许一致的调试,但不添加额外的检查。)
没有人希望在编译时或运行时静默回退到互斥体。这种操作模式基本上就存在,因此 ISO C++ 可以要求该功能在所有地方都得到支持,而不会使其无法在某些目标上实现。
与关键部分的手动细粒度锁定(在为其设计的数据结构上同时执行一些相关的原子操作)相比,回退到锁定通常是非常次优的。人们使用atomic<T>(以及即将到来的atomic_ref<T>)来提高性能,而大部分性能都被锁定破坏了。尤其是读取端的可扩展性。
脚注 1:IIRCalignof()仅针对类型而不是对象指定,但在 GNU C++ 中它也适用于对象。我使用它作为编译器内部知识的简写,即某个对象用于alignas()过度对齐它。
| 归档时间: |
|
| 查看次数: |
332 次 |
| 最近记录: |