atomic_ref 当外部基础类型未按要求对齐时
atomic_ref when external underlying type is not aligned as requested
我在 p0019r8 上阅读了以下内容:
atomic_ref(T& obj);
Requires: The referenced object shall be aligned to required_alignment
.
cppreference 未对齐时将其解释为 UB:
The behavior is undefined if obj is not aligned to required_alignment.
那么您希望实现如何处理它?
并且实现可以检查 编译时 alignof
,但实际上一个类型可能比 alignof
更对齐。一个实现可以解释指针位并检查运行时间对齐,但它是额外的运行时间检查。
最终我看到以下选项:
- 什么都不做 - 以不愉快的方式实现 运行时间未定义的行为,只支持正确使用
- 检查编译时对齐 (
alignof
) 并在错误时发出警告
检查编译时对齐(alignof
),如果错误不正确,则在编译时失败,因为实际对齐可能比静态类型可见的要大
检查编译时对齐 (alignof
) 并在 运行 时失败,如果错误 不正确,因为实际对齐可能比可见的更大静态类型
- 检查编译时对齐 (
alignof
),如果错误则回退到基于锁
- 检查 运行 时间对齐(指针位),如果错误则在 运行 时间失败
- 检查运行时间对齐(指针位),如果错误则回退到基于锁
TL:DR:永远不要默默地退回到锁定,没有人想要那样,因为它违背了 std::atomic
的主要目的。 将非无锁视为一种可移植性回退,而不是一种可行的操作模式。
作为 UB 使得编译器 合法 可以简单地假设而不检查它是否对齐。能够假设没有任何 运行 时间检查是 one of the major benefits of the concept of UB。这是大多数人在 运行 优化构建时想要/期望的,而不是使用可能回退到使用互斥锁的条件分支使代码膨胀。
这里是否(以及如何)定义任何行为的选择完全取决于实现,作为 实现质量 和权衡性能与性能的问题。调试。我想您知道这一点,并且实际上是在询问用户希望编译器为这些 QoI 选择选择什么,这很好。
正如您链接的 P0019 提案所说,这一切都归结为 QOI 问题:
- Reference-ability Constraints
An object referenced by an atomic reference must satisfy possibly architecture-specific constraints. For example, the object might need to be properly aligned in memory or might not be allowed to reside in GPU register memory. We do not enumerate all potential constraints or specify behavior when these constraints are violated. It is a quality-of-implementation issue to generate appropriate information when constraints are violated.
"generate appropriate information" 措辞暗示他们希望实现在检测到违规时发出警告/错误,而不是回退到锁定。
尽管可以退回到锁定 的实现可能 愚蠢地将 required_alignment
设置为正确性的最小值 (1),而不是锁定自由的最小值。当然没有人想要那样,但这是 QoI 问题而不是标准合规性问题。
我希望(或至少希望)实现如下所示:
编译时发出警告 如果 atomic_ref
用于 alignof
小于 required_alignment
的任何对象。您可能知道某个 T *p
恰好是 8 字节对齐的,即使 alignof(T)
只是 1 或 4,所以这不应该是一个错误。
采用某种本地方式消除警告是件好事。 (替代方案:承诺使用类似 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,其正确性取决于对齐的对象。 (还有没有 UBSan 的 gcc -O0
,它允许一致的调试但不添加额外的检查。)
没有人希望在编译时或 运行 时静默回退到互斥锁 。这种操作模式基本上只是存在,因此 ISO C++ 可以要求该功能在任何地方都得到支持,而不会使其无法在某些目标上实现。
与关键部分的手动细粒度锁定相比,锁定的回退通常是非常次优的,关键部分同时在为其设计的数据结构上执行一些相关的原子操作。人们使用 atomic<T>
(以及即将推出的 atomic_ref<T>
)来提高性能,而大部分性能都被锁定破坏了。特别是读取端的可扩展性。
脚注 1:IIRC,alignof()
仅针对类型而非对象指定,但在 GNU C++ 中它也适用于对象。我将其用作 shorthand 是为了编译器的内部知识,即某个对象使用 alignas()
过度对齐。
我在 p0019r8 上阅读了以下内容:
atomic_ref(T& obj);
Requires: The referenced object shall be aligned to
required_alignment
.
cppreference 未对齐时将其解释为 UB:
The behavior is undefined if obj is not aligned to required_alignment.
那么您希望实现如何处理它?
并且实现可以检查 编译时 alignof
,但实际上一个类型可能比 alignof
更对齐。一个实现可以解释指针位并检查运行时间对齐,但它是额外的运行时间检查。
最终我看到以下选项:
- 什么都不做 - 以不愉快的方式实现 运行时间未定义的行为,只支持正确使用
- 检查编译时对齐 (
alignof
) 并在错误时发出警告 检查编译时对齐(不正确,则在编译时失败,因为实际对齐可能比静态类型可见的要大alignof
),如果错误检查编译时对齐 (不正确,因为实际对齐可能比可见的更大静态类型alignof
) 并在 运行 时失败,如果错误- 检查编译时对齐 (
alignof
),如果错误则回退到基于锁 - 检查 运行 时间对齐(指针位),如果错误则在 运行 时间失败
- 检查运行时间对齐(指针位),如果错误则回退到基于锁
TL:DR:永远不要默默地退回到锁定,没有人想要那样,因为它违背了 std::atomic
的主要目的。 将非无锁视为一种可移植性回退,而不是一种可行的操作模式。
作为 UB 使得编译器 合法 可以简单地假设而不检查它是否对齐。能够假设没有任何 运行 时间检查是 one of the major benefits of the concept of UB。这是大多数人在 运行 优化构建时想要/期望的,而不是使用可能回退到使用互斥锁的条件分支使代码膨胀。
这里是否(以及如何)定义任何行为的选择完全取决于实现,作为 实现质量 和权衡性能与性能的问题。调试。我想您知道这一点,并且实际上是在询问用户希望编译器为这些 QoI 选择选择什么,这很好。
正如您链接的 P0019 提案所说,这一切都归结为 QOI 问题:
- Reference-ability Constraints
An object referenced by an atomic reference must satisfy possibly architecture-specific constraints. For example, the object might need to be properly aligned in memory or might not be allowed to reside in GPU register memory. We do not enumerate all potential constraints or specify behavior when these constraints are violated. It is a quality-of-implementation issue to generate appropriate information when constraints are violated.
"generate appropriate information" 措辞暗示他们希望实现在检测到违规时发出警告/错误,而不是回退到锁定。
尽管可以退回到锁定 的实现可能 愚蠢地将 required_alignment
设置为正确性的最小值 (1),而不是锁定自由的最小值。当然没有人想要那样,但这是 QoI 问题而不是标准合规性问题。
我希望(或至少希望)实现如下所示:
编译时发出警告 如果
atomic_ref
用于alignof
小于required_alignment
的任何对象。您可能知道某个T *p
恰好是 8 字节对齐的,即使alignof(T)
只是 1 或 4,所以这不应该是一个错误。采用某种本地方式消除警告是件好事。 (替代方案:承诺使用类似 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,其正确性取决于对齐的对象。 (还有没有 UBSan 的 gcc
-O0
,它允许一致的调试但不添加额外的检查。)没有人希望在编译时或 运行 时静默回退到互斥锁 。这种操作模式基本上只是存在,因此 ISO C++ 可以要求该功能在任何地方都得到支持,而不会使其无法在某些目标上实现。
与关键部分的手动细粒度锁定相比,锁定的回退通常是非常次优的,关键部分同时在为其设计的数据结构上执行一些相关的原子操作。人们使用
atomic<T>
(以及即将推出的atomic_ref<T>
)来提高性能,而大部分性能都被锁定破坏了。特别是读取端的可扩展性。
脚注 1:IIRC,alignof()
仅针对类型而非对象指定,但在 GNU C++ 中它也适用于对象。我将其用作 shorthand 是为了编译器的内部知识,即某个对象使用 alignas()
过度对齐。