a Double
is eight bytes long, and ought to be usable with swift-atomics. yet it doesn’t conform to AtomicValue
for some reason.
why?
a Double
is eight bytes long, and ought to be usable with swift-atomics. yet it doesn’t conform to AtomicValue
for some reason.
why?
Looks like it is planned:
Here is the relevant ticket, which alludes to but does not fully cover some of the issues with atomic doubles.
More broadly, atomic floating point is kind of a pain in the ass. Few CPUs define a full set of atomic floating point operations. This tends to force users to define their atomic floating points on top of atomic integers, storing the raw bits and then interpreting the raw bits as a floating point number to perform the operation. This has the following implications:
fmul
) that doesn't have an atomic equivalent, it becomes necessary to implement it using a compare-and-swap loop. The performance of this degrades fairly substantially under contention.While I can personally believe that there are some uses for atomic doubles, I've tended to be suspicious of them in most code. They tend to smell of premature optimization, where value semantics or locks end up behaving better. Their fast-path is not substantially better than an uncontended lock on most platforms (which is itself a single RMW) so you have to really consider what you're trying to achieve. Nonetheless, it makes sense for swift-atomics to offer what it can.
(Sidebar: arm64 prior to armv8.1 made all atomic operations an atomic RMW so there's no reason to be excessively suspicious of this pattern, but armv8.1 still felt the need to add single-operation atomics.)
neither of these limitations are relevant to me, because i am not performing any increments or comparisons with the Double
, i am merely trying to use the atomic to publish a floating point value from an actor.
the procedure looks a bit like this:
i do not understand why everyone’s default response to this kind of question is “just use locks instead”. in my view, actor
s have a limitation where they cannot expose any kind of mutable property without isolation.
public
actor NetworkStatistics
{
public private(set)
var ninetiethPercentileLatency:Double
}
updating the latency requires sequencing, because we don’t want to accidentally overwrite a newer statistic with an older one. but reading it really should not require any synchronization, because it is only a derived value.
A bit inconvenient, but will this simple wrapper work for you?
extension UnsafeAtomic where Value == UInt64 {
public var double: Double {
get { Double(bitPattern: load(ordering: .relaxed)) }
set { store(newValue.bitPattern, ordering: .releasing) }
}
}
var val = UnsafeAtomic<UInt64>.create(0)
val.double = 3.14
let x: Double = val.double
that’s what i ended up doing. it’s a shame swift-atomics can’t do this by default.
(but i didn’t expose a setter for the property, to prevent it from being passed inout
.)