Skip to content

Commit 2644f79

Browse files
committed
mpfr: speed improvements
Unlike GMP / BigInt, the MPFR library exposes an interface for external allocation: http://www.mpfr.org/mpfr-current/mpfr.html#Custom-Interface Using that allows us to use our fast memory-pool gc and avoid adding finalizers and use the slow malloc/free functions. (and in some cases, some of it might even end up on the stack!) Also switch to allocating lgamma_sign on the stack, for better performance (and thread-safety) Also fixes Serialization to preserve BigFloat precision.
1 parent 01a0964 commit 2644f79

File tree

4 files changed

+58
-28
lines changed

4 files changed

+58
-28
lines changed

base/mpfr.jl

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import
1717
cosh, sinh, tanh, sech, csch, coth, acosh, asinh, atanh,
1818
cbrt, typemax, typemin, unsafe_trunc, realmin, realmax, rounding,
1919
setrounding, maxintfloat, widen, significand, frexp, tryparse, iszero,
20-
isone, big, RefValue
20+
isone, big, _string_n
2121

2222
import .Base.Rounding: rounding_raw, setrounding_raw
2323

@@ -39,8 +39,8 @@ function __init__()
3939
nothing
4040
end
4141

42-
const ROUNDING_MODE = RefValue{Cint}(0)
43-
const DEFAULT_PRECISION = RefValue(256)
42+
const ROUNDING_MODE = Ref{Cint}(0) # actually an Enum, defined by `to_mpfr`
43+
const DEFAULT_PRECISION = Ref{Int}(256)
4444

4545
# Basic type and initialization definitions
4646

@@ -54,21 +54,44 @@ mutable struct BigFloat <: AbstractFloat
5454
sign::Cint
5555
exp::Clong
5656
d::Ptr{Limb}
57+
# _d::Buffer{Limb} # Julia gc handle for memory @ d
58+
_d::String # Julia gc handle for memory @ d (optimized)
59+
60+
# Not recommended for general use:
61+
# used internally by, e.g. deepcopy
62+
global function _BigFloat(prec::Clong, sign::Cint, exp::Clong, d::String)
63+
# ccall-based version, inlined below
64+
#z = new(zero(Clong), zero(Cint), zero(Clong), C_NULL, d)
65+
#ccall((:mpfr_custom_init,:libmpfr), Cvoid, (Ptr{Limb}, Clong), d, prec) # currently seems to be a no-op in mpfr
66+
#NAN_KIND = Cint(0)
67+
#ccall((:mpfr_custom_init_set,:libmpfr), Cvoid, (Ref{BigFloat}, Cint, Clong, Ptr{Limb}), z, NAN_KIND, prec, d)
68+
#return z
69+
return new(prec, sign, exp, pointer(d), d)
70+
end
5771

5872
function BigFloat()
59-
prec = precision(BigFloat)
60-
z = new(zero(Clong), zero(Cint), zero(Clong), C_NULL)
61-
ccall((:mpfr_init2,:libmpfr), Cvoid, (Ref{BigFloat}, Clong), z, prec)
62-
finalizer(cglobal((:mpfr_clear, :libmpfr)), z)
63-
return z
73+
prec::Clong = precision(BigFloat)
74+
nb = ccall((:mpfr_custom_get_size,:libmpfr), Csize_t, (Clong,), prec)
75+
nb = (nb + Core.sizeof(Limb) - 1) ÷ Core.sizeof(Limb) # align to number of Limb allocations required for this
76+
#d = Vector{Limb}(undef, nb)
77+
d = _string_n(nb * Core.sizeof(Limb))
78+
EXP_NAN = Clong(1) - Clong(typemax(Culong) >> 1)
79+
return _BigFloat(prec, one(Cint), EXP_NAN, d) # +NAN
6480
end
81+
end
6582

66-
# Not recommended for general use:
67-
function BigFloat(prec::Clong, sign::Cint, exp::Clong, d::Ptr{Cvoid})
68-
new(prec, sign, exp, d)
83+
# overload the definition of unsafe_convert to ensure that `x.d` is assigned
84+
# it may have been dropped in the event that the BigFloat was serialized
85+
Base.unsafe_convert(::Type{Ref{BigFloat}}, x::Ptr{BigFloat}) = x
86+
@inline function Base.unsafe_convert(::Type{Ref{BigFloat}}, x::Ref{BigFloat})
87+
x = x[]
88+
if x.d == C_NULL
89+
x.d = pointer(x._d)
6990
end
91+
return convert(Ptr{BigFloat}, Base.pointer_from_objref(x))
7092
end
7193

94+
7295
"""
7396
BigFloat(x)
7497
@@ -739,6 +762,7 @@ function setprecision(::Type{BigFloat}, precision::Int)
739762
throw(DomainError(precision, "`precision` cannot be less than 2."))
740763
end
741764
DEFAULT_PRECISION[] = precision
765+
return precision
742766
end
743767

744768
setprecision(precision::Int) = setprecision(BigFloat, precision)
@@ -957,11 +981,11 @@ set_emin!(x) = ccall((:mpfr_set_emin, :libmpfr), Cvoid, (Clong,), x)
957981

958982
function Base.deepcopy_internal(x::BigFloat, stackdict::IdDict)
959983
haskey(stackdict, x) && return stackdict[x]
960-
prec = precision(x)
961-
y = BigFloat(zero(Clong), zero(Cint), zero(Clong), C_NULL)
962-
ccall((:mpfr_init2,:libmpfr), Cvoid, (Ref{BigFloat}, Clong), y, prec)
963-
finalizer(cglobal((:mpfr_clear, :libmpfr)), y)
964-
ccall((:mpfr_set, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), y, x, ROUNDING_MODE[])
984+
# d = copy(x._d)
985+
d = x._d
986+
d′ = GC.@preserve d unsafe_string(pointer(d), sizeof(d)) # creates a definitely-new String
987+
y = _BigFloat(x.prec, x.sign, x.exp, d′)
988+
#ccall((:mpfr_custom_move,:libmpfr), Cvoid, (Ref{BigFloat}, Ptr{Limb}), y, d) # unnecessary
965989
stackdict[x] = y
966990
return y
967991
end

stdlib/Serialization/src/Serialization.jl

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -313,11 +313,6 @@ function serialize(s::AbstractSerializer, n::BigInt)
313313
serialize(s, string(n, base = 62))
314314
end
315315

316-
function serialize(s::AbstractSerializer, n::BigFloat)
317-
serialize_type(s, BigFloat)
318-
serialize(s, string(n))
319-
end
320-
321316
function serialize(s::AbstractSerializer, ex::Expr)
322317
serialize_cycle(s, ex) && return
323318
l = length(ex.args)
@@ -1184,8 +1179,6 @@ function deserialize(s::AbstractSerializer, T::Type{Dict{K,V}}) where {K,V}
11841179
return t
11851180
end
11861181

1187-
deserialize(s::AbstractSerializer, ::Type{BigFloat}) = parse(BigFloat, deserialize(s))
1188-
11891182
deserialize(s::AbstractSerializer, ::Type{BigInt}) = parse(BigInt, deserialize(s), base = 62)
11901183

11911184
function deserialize(s::AbstractSerializer, t::Type{Regex})

test/mpfr.jl

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -853,11 +853,17 @@ end
853853
@test_throws ArgumentError parse(BigFloat, "1\0")
854854

855855
@testset "serialization (issue #12386)" begin
856-
b = IOBuffer()
857-
x = 2.1 * big(pi)
858-
serialize(b, x)
859-
seekstart(b)
860-
@test deserialize(b) == x
856+
b = PipeBuffer()
857+
let x = setprecision(53) do
858+
return 2.1 * big(pi)
859+
end
860+
serialize(b, x)
861+
@test deserialize(b) == x
862+
end
863+
let x = BigFloat(Inf, 46)
864+
serialize(b, x)
865+
@test deserialize(b) == x == BigFloat(Inf, 2)
866+
end
861867
end
862868
@test isnan(sqrt(BigFloat(NaN)))
863869

test/precompile.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ try
142142
143143
g() = override(1.0)
144144
Test.@test g() === 2.0 # compile this
145+
146+
const abigfloat_f() = big"12.34"
147+
const abigfloat_x = big"43.21"
145148
end
146149
""")
147150
@test_throws ErrorException Core.kwfunc(Base.nothing) # make sure `nothing` didn't have a kwfunc (which would invalidate the attempted test)
@@ -164,6 +167,10 @@ try
164167
@test Foo.override(1.0e0) == Float64('a')
165168
@test Foo.override(1.0f0) == 'b'
166169
@test Foo.override(UInt(1)) == 2
170+
171+
# Issue #15722
172+
@test Foo.abigfloat_f()::BigFloat == big"12.34"
173+
@test (Foo.abigfloat_x::BigFloat + 21) == big"64.21"
167174
end
168175

169176
cachedir = joinpath(dir, "compiled", "v$(VERSION.major).$(VERSION.minor)")

0 commit comments

Comments
 (0)