Skip to content

Commit 9d18432

Browse files
authored
Use OncePerTask instead of a handmade thread local cache (#123)
1 parent a2e24df commit 9d18432

File tree

3 files changed

+40
-17
lines changed

3 files changed

+40
-17
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "FixedPointDecimals"
22
uuid = "fb4d412d-6eee-574d-9565-ede6634db7b0"
33
authors = ["Fengyang Wang <[email protected]>", "Curtis Vogt <[email protected]>"]
4-
version = "0.6.3"
4+
version = "0.6.4"
55

66
[deps]
77
BitIntegers = "c3b6d118-76ef-56ca-8cc7-ebb389d030a1"

src/FixedPointDecimals.jl

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,41 @@ const FD = FixedDecimal
116116

117117
include("parse.jl")
118118

119-
function __init__()
120-
nt = isdefined(Base.Threads, :maxthreadid) ? Threads.maxthreadid() : Threads.nthreads()
121-
# Buffers used in parsing when dealing with BigInts, see _divpow10! in parse.jl
122-
resize!(empty!(_BIGINT_10s), nt)
123-
resize!(empty!(_BIGINT_Rs), nt)
124-
return
119+
const _BIGINT1 = BigInt(1)
120+
const _BIGINT2 = BigInt(2)
121+
const _BIGINT10 = BigInt(10)
122+
# Adapted from Parsers.jl, see https://github.com/JuliaData/Parsers.jl/pull/195
123+
@static if isdefined(Base, :OncePerTask)
124+
const _get_bigint10s = OncePerTask{BigInt}(() -> BigInt(; nbits=256))
125+
const _get_bigintRs = OncePerTask{BigInt}(() -> BigInt(; nbits=256))
126+
else
127+
# N.B This code is not thread safe in the presence of thread migration
128+
const _BIGINT_10s = BigInt[] # buffer for "remainders" in _divpow10!, accessed via `access_threaded`
129+
const _BIGINT_Rs = BigInt[] # buffer for "remainders" in _divpow10!, accessed via `access_threaded`
130+
131+
_get_bigint10s() = access_threaded(() -> (@static VERSION > v"1.5" ? BigInt(; nbits=256) : BigInt()), _BIGINT_10s)
132+
_get_bigintRs() = access_threaded(() -> (@static VERSION > v"1.5" ? BigInt(; nbits=256) : BigInt()), _BIGINT_Rs)
133+
134+
function access_threaded(f, v::Vector)
135+
tid = Threads.threadid()
136+
0 < tid <= length(v) || _length_assert()
137+
if @inbounds isassigned(v, tid)
138+
@inbounds x = v[tid]
139+
else
140+
x = f()
141+
@inbounds v[tid] = x
142+
end
143+
return x
144+
end
145+
@noinline _length_assert() = @assert false "0 < tid <= v"
146+
147+
function __init__()
148+
nt = isdefined(Base.Threads, :maxthreadid) ? Threads.maxthreadid() : Threads.nthreads()
149+
# Buffers used in parsing when dealing with BigInts, see _divpow10! in parse.jl
150+
resize!(empty!(_BIGINT_10s), nt)
151+
resize!(empty!(_BIGINT_Rs), nt)
152+
return
153+
end
125154
end
126155

127156
# Custom widemul implementation to avoid the cost of widening to BigInt.

src/parse.jl

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,6 @@ const OPTIONS_ROUND_THROWS = Parsers.Options(rounding=nothing)
3131
# TODO: a lookup table per type would be faster
3232
@inline _shift(n::T, decpos) where {T} = T(10)^decpos * n
3333

34-
const _BIGINT1 = BigInt(1)
35-
const _BIGINT2 = BigInt(2)
36-
const _BIGINT10 = BigInt(10)
37-
const _BIGINT_10s = BigInt[] # buffer for "remainders" in _divpow10!, accessed via `Parsers.access_threaded`
38-
const _BIGINT_Rs = BigInt[] # buffer for "remainders" in _divpow10!, accessed via `Parsers.access_threaded`
39-
4034
for T in (Base.BitSigned_types..., Base.BitUnsigned_types...)
4135
let bytes = Tuple(codeunits(string(typemax(T))))
4236
# The number of digits an integer of type T can hold
@@ -74,8 +68,8 @@ function _divpow10!(x::T, code, pow, ::RoundingMode{:Throw}) where {T}
7468
end
7569
function _divpow10!(x::BigInt, code, pow, ::RoundingMode{:Nearest})
7670
# adapted from https://github.com/JuliaLang/julia/blob/112554e1a533cebad4cb0daa27df59636405c075/base/div.jl#L217
77-
@inbounds r = Parsers.access_threaded(() -> (@static VERSION > v"1.5" ? BigInt(; nbits=256) : BigInt()), _BIGINT_Rs) # we must not yield here!
78-
@inbounds y = Parsers.access_threaded(() -> (@static VERSION > v"1.5" ? BigInt(; nbits=256) : BigInt()), _BIGINT_10s) # we must not yield here!
71+
r = _get_bigintRs() # we must not yield here!
72+
y = _get_bigint10s() # we must not yield here!
7973
Base.GMP.MPZ.set!(y, _BIGINT10) # y = 10
8074
Base.GMP.MPZ.pow_ui!(y, pow) # y = y^pow
8175
Base.GMP.MPZ.tdiv_qr!(x, r, x, y) # x, r = divrem(x, y)
@@ -87,15 +81,15 @@ function _divpow10!(x::BigInt, code, pow, ::RoundingMode{:Nearest})
8781
return x, code
8882
end
8983
function _divpow10!(x::BigInt, code, pow, ::RoundingMode{:ToZero})
90-
@inbounds y = Parsers.access_threaded(() -> (@static VERSION > v"1.5" ? BigInt(; nbits=256) : BigInt()), _BIGINT_10s) # we must not yield here!
84+
y = _get_bigint10s() # we must not yield here!
9185
Base.GMP.MPZ.set!(y, _BIGINT10) # y = 10
9286
Base.GMP.MPZ.pow_ui!(y, pow) # y = y^pow
9387
Base.GMP.MPZ.tdiv_q!(x, y) # x = div(x, y)
9488
return x, code
9589
end
9690

9791
function _divpow10!(x::BigInt, code, pow, ::RoundingMode{:Throw})
98-
@inbounds y = Parsers.access_threaded(() -> (@static VERSION > v"1.5" ? BigInt(; nbits=256) : BigInt()), _BIGINT_10s) # we must not yield here!
92+
y = _get_bigint10s() # we must not yield here!
9993
Base.GMP.MPZ.set!(y, _BIGINT10) # y = 10
10094
Base.GMP.MPZ.pow_ui!(y, pow) # y = y^pow
10195
Base.GMP.MPZ.tdiv_qr!(x, y, x, y) # x, y = divrem(x, y)

0 commit comments

Comments
 (0)