繁体   English   中英

浮点数的原子操作

[英]Atomic operations on floats

大多数语言都提供原子int操作(添加、比较和交换等)的函数。

为什么不用于浮动类型?

让我们从操作系统/硬件设计的角度思考浮点原子......

原子的存在是因为它们是同步所必需的。 大多数同步涉及什么? 句柄、标志、互斥锁、自旋锁——这些东西的实际价值是没有意义的,只要它对每个用户都是一致的,并且在用户之间是不同的。 即使对于像信号量这样的值更有意义的东西 - 它仍然是关于计数而不是测量,所以 32 位值得 32 位,无论我们认为它代表什么。

其次,技术问题。 几乎任何我们可以编程的东西都可以进行整数运算。 不是那么浮点 - 当 C 库模拟 FP 操作时,这些原子将难以实现和不可能实现。 即使在硬件中,FP 操作通常会比整数慢,谁想要慢速锁? FPU 本身的设计甚至可能使实现原子操作变得困难——例如,如果它挂在协处理器接口上而没有对内存总线的任何直接访问。

第二个半,如果我们想要float ,我们当然也想要double吗? 但是double通常存在比机器字大的问题,排除了许多架构上偶数加载和存储的原子性。

第三,当谈到原子之类的事情时,CPU 架构师倾向于实现系统设计人员和操作系统人员的要求,而操作系统人员通常并不完全喜欢浮点 - 愚蠢的额外寄存器要保存,减慢上下文切换......硬件中的更多说明/功能成本功率和复杂性,如果客户不想要它们......

所以,简而言之,没有足够的用例,所以没有硬件支持,所以没有语言支持。 当然,在某些架构上,您可以推出自己的 atomic ,我想 GPU 计算可能对主要浮点硬件上的同步有更多需求,那么谁知道它是否会保持这种状态呢?

为了改进 Go 上下文中的先前答案,我们可以使用https://golang.org/pkg/math/#Float64bitshttps://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
        }
    }
}

啊,原来的问题被标记为 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 )。 至于为什么没有对浮动的装配支持,我不知道。

C ++ 20将获得float和double的原子专业化。 Nvidia致力于实现这一标准化。 根据此处的论文( http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0020r6.html ),应将其用于HPC应用程序。 但我无法想象这样的事情。

CAS (compare-and-swap) 是最重要的原子操作,因为其他操作( addandor等)可以用它来模拟。

我认为浮点数没有 CAS 功能的一个重要原因是,等式不能像处理整数类型那样处理 IEEE754 浮点数。 例如,当旧的预期值或实际值结果为NaN时,您将不知道与 CAS 交换是否成功。 请记住,将NaN与包括NaN在内的任何其他值进行比较将始终返回 false。

关于原子按位和算术运算,它们对浮点的用处比对整数的用处要小得多。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM