C 浮子上的原子操作

C 浮子上的原子操作,c,operating-system,kernel,atomic,atomicity,C,Operating System,Kernel,Atomic,Atomicity,大多数语言为原子int操作(添加、比较和交换等)提供函数 为什么不选择浮动类型?啊,原来的问题被标记为Go,有点不同,所以我还是要回答这个问题,抱歉,如果这不是编辑问题的完整答案:) 使用Go,您可以在不安全的黑暗面上进行一次小行程,以原子方式交换任何指针值: 摘录: var uptr unsafe.Pointer var f1, f2 float64 f1 = 12.34 f2 = 56.78 // Original values fmt.Println(f1, f2) uptr = un

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


为什么不选择浮动类型?

啊,原来的问题被标记为Go,有点不同,所以我还是要回答这个问题,抱歉,如果这不是编辑问题的完整答案:)

使用Go,您可以在不安全的黑暗面上进行一次小行程,以原子方式交换任何指针值:

摘录:

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)

交换调用(以及其他原子操作,如)映射到保证原子性的CPU体系结构指令(有关更多详细信息,请参阅)。至于为什么没有对浮点的汇编支持,我不知道。

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

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

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

其次,如果我们想要
float
,我们当然也想要
double
?但是,
double
经常会遇到比机器字还大的问题,这就排除了许多体系结构上甚至负载和存储的原子性

第三,当涉及原子时,CPU架构师倾向于实现系统设计师和操作系统人员的要求,而操作系统人员通常并不喜欢浮点运算——愚蠢的额外寄存器需要保存,减缓了上下文切换。。。硬件中的更多说明/功能会降低功耗和复杂性,如果客户不需要它们


简而言之,没有足够的用例,没有硬件支持,也没有语言支持。当然,在某些体系结构上你可以,而且我想GPU compute可能对主要是浮点硬件的同步有更多的需求,所以谁知道它是否会保持这种状态呢?

在Go的背景下改进之前的答案,我们可以在不直接使用不安全软件包的情况下使用浮点数64与uint64s之间进行转换

给定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
        }
    }
}
CAS(比较和交换)是最重要的原子操作,因为其他操作(
添加
等)可以用它来模拟

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


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

OpenCL提供原子单精度浮点运算。它支持C的一个稍小的子集。这意味着它是可移植的,AFAIK。这个答案是不正确的。您可以尝试``var x AtomicFloat64 x.Add(1)x.Add(1)x.Add(1)x.Value()->-0.25当然不是3```如果您想要更好的方法,请检查以下代码:您完全正确!这段代码有一个bug。感谢注意,我将更新答案。无需执行
a:=atomic.LoadUint64
,因为它可能在下一个
if
语句之前更改。看见