【发布时间】:2014-01-25 16:32:24
【问题描述】:
大多数语言都提供用于原子int 操作(添加、比较和交换等)的函数。
为什么不用于浮点类型?
【问题讨论】:
标签: c operating-system kernel atomic
大多数语言都提供用于原子int 操作(添加、比较和交换等)的函数。
为什么不用于浮点类型?
【问题讨论】:
标签: c operating-system kernel atomic
让我们从操作系统/硬件设计的角度来思考浮点原子......
原子之所以存在是因为它们是同步所必需的。大多数同步涉及什么?句柄、标志、互斥锁、自旋锁——只要每个用户一致且用户之间不同,其实际价值就毫无意义。即使对于值更有意义的信号量之类的东西——它仍然是关于计数而不是测量,所以无论我们认为它代表什么,32 位都值得 32 位。
其次,技术问题。几乎我们可以编程的任何东西都进行整数运算。不是那么浮点 - 当 C 库模拟 FP 操作时,这些原子将难以实现和不可能实现。即使在硬件中,FP 操作通常也会比整数慢,谁想要慢锁呢? FPU 本身的设计甚至可能使其难以实现原子操作 - 例如如果它挂在协处理器接口上,而没有直接访问内存总线。
第二个半,如果我们想要float,我们肯定也想要double 吗?但是double 经常存在比机器字大的问题,在许多架构上排除了偶数加载和存储的原子性。
第三,当涉及到诸如原子之类的东西时,CPU 架构师倾向于实现系统设计人员和操作系统人员所要求的,而操作系统人员通常并不完全喜欢浮点 - 要保存的愚蠢的额外寄存器,减慢上下文切换...更多关于硬件成本和复杂性的说明/功能,如果客户不想要它们...
所以,简而言之,没有足够的用例,所以没有硬件支持,所以没有语言支持。当然,在某些架构上你可以 roll your own atomics,我想 GPU 计算可能对主要是浮点硬件的同步有更多需求,所以谁知道它是否会保持这种状态?
【讨论】:
为了在 Go 上下文中改进之前的答案,我们可以使用 https://golang.org/pkg/math/#Float64bits 和 https://golang.org/pkg/math/#Float64frombits 将 float64s 与 uint64s 相互转换,而无需直接使用 unsafe 包。
给定一个 uint64,然后我们可以使用所有可用的原子原语。
type AtomicFloat64 uint64
func (f *AtomicFloat64) Value() float64 {
return math.Float64frombits(atomic.LoadUint64((*uint64)(f)))
}
func (f *AtomicFloat64) Add(n float64) float64 {
for {
a := atomic.LoadUint64((*uint64)(f))
b := math.Float64bits(math.Float64frombits(a) + n)
if atomic.CompareAndSwapUint64((*uint64)(f), a, b) {
return
}
}
}
【讨论】:
a := atomic.LoadUint64,因为它可能在下一个if 语句之前改变。见golang.org/src/sync/atomic/64bit_arm.go?s=508:615#L27
啊,原来的问题被标记为 Go 并且略有不同,所以这就是我要回答的内容,如果这不是已编辑问题的完整答案,抱歉:)
使用 Go,您可以原子地交换任何指针值,但在不安全的阴暗面稍作旅行:
http://play.golang.org/p/aFbzUMhfEB
摘录:
var uptr unsafe.Pointer
var f1, f2 float64
f1 = 12.34
f2 = 56.78
// Original values
fmt.Println(f1, f2)
uptr = unsafe.Pointer(&f1)
atomic.SwapPointer(&uptr, unsafe.Pointer(&f2))
f1 = *(*float64)(unsafe.Pointer(uptr))
// f1 now holds the value of f2, f2 is untouched
fmt.Println(f1, f2)
交换调用(以及其他原子操作,如 CAS)映射到保证这种原子性的 CPU 架构指令(有关详细信息,请参阅 https://stackoverflow.com/a/1762179/1094941)。至于为什么没有对浮动的汇编支持,我不知道。
【讨论】:
CAS(比较和交换)是最重要的原子操作,因为其他(add、and、or 等)可以用它来模拟。
我认为浮点数没有 CAS 功能的一个重要原因是,相等性不适用于 IEEE754 浮点数,其方式与整数类型相同。例如,当旧的预期值或实际值变成NaN 时,您将不知道使用 CAS 交换是否成功。请记住,将 NaN 与包括 NaN 在内的任何其他值进行比较将始终返回 false。
关于原子的按位运算和算术运算,它们对浮点数的用处远不如对整数的用处。
【讨论】: