【问题标题】:Atomic operations on floats浮点数的原子操作
【发布时间】:2014-01-25 16:32:24
【问题描述】:

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

为什么不用于浮点类型?

【问题讨论】:

    标签: c operating-system kernel atomic


    【解决方案1】:

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

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

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

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

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

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

    【讨论】:

      【解决方案2】:

      为了在 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
              }
          }
      }
      

      【讨论】:

      【解决方案3】:

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

      【讨论】:

      • OpenCL 提供原子单精度浮点运算。它支持稍小的 C 子集。khronos.org AFAIK 意味着它是可移植的。
      【解决方案4】:

      CAS(比较和交换)是最重要的原子操作,因为其他(addandor 等)可以用它来模拟。

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

      关于原子的按位运算和算术运算,它们对浮点数的用处远不如对整数的用处。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-05-07
        • 2021-02-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-23
        • 1970-01-01
        相关资源
        最近更新 更多