-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Removing allocations for repeated use of object_and_preserve #4
Comments
Why not 1 & 2? I.e., let users decide what works best for their use case. |
This or something like it seems like a good solution. julia> x = Ref(("hi", "world", 9))
Base.RefValue{Tuple{String, String, Int64}}(("hi", "world", 9))
julia> p = Base.unsafe_convert(Ptr{typeof(x[])}, x)
Ptr{Tuple{String, String, Int64}} @0x00007f2a74688db0
julia> tsptr(x::Ptr{T}) where {T} = (Base.unsafe_pointer_to_objref(x)::Base.RefValue{T})[]
tsptr (generic function with 1 method)
julia> tsptr(p)
("hi", "world", 9)
julia> @benchmark tsptr($p)
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 1.765 ns (0.00% GC)
median time: 1.772 ns (0.00% GC)
mean time: 1.798 ns (0.00% GC)
maximum time: 12.976 ns (0.00% GC)
--------------
samples: 10000
evals/sample: 1000 I don't know if there is a way to avoid the type instability all together by using something other than unsafe_pointer_to_objref(x::Ptr) = ccall(:jl_value_ptr, Any, (Ptr{Cvoid},), x) e.g. tsptr2(x::Ptr{T}) where {T} = ccall(:jl_value_ptr, Ref{Base.RefValue{T}}, (Ptr{Cvoid},), x)[]
tsptr2(p) yields julia> tsptr2(p)
("hi", "world", 9)
julia> @code_warntype tsptr2(p)
MethodInstance for tsptr2(::Ptr{Tuple{String, String, Int64}})
from tsptr2(x::Ptr{T}) where T in Main at REPL[13]:1
Static Parameters
T = Tuple{String, String, Int64}
Arguments
#self#::Core.Const(tsptr2)
x::Ptr{Tuple{String, String, Int64}}
Body::Tuple{String, String, Int64}
1 ─ %1 = Core.apply_type(Main.Ptr, Main.Cvoid)::Core.Const(Ptr{Nothing})
│ %2 = Base.cconvert(%1, x)::Ptr{Tuple{String, String, Int64}}
│ %3 = Core.apply_type(Main.Ptr, Main.Cvoid)::Core.Const(Ptr{Nothing})
│ %4 = Base.unsafe_convert(%3, %2)::Ptr{Nothing}
│ %5 = $(Expr(:foreigncall, :(:jl_value_ptr), Ref{Base.RefValue{T}}, svec(Ptr{Nothing}), 0, :(:ccall), :(%4), :(%2)))::Base.RefValue{Tuple{String, String, Int64}}
│ %6 = Base.getindex(%5)::Tuple{String, String, Int64}
└── return %6 It loads a It's basically the same fast. |
Sorry for the delay in replying. Yeah enabling both sounds good. I'm intrigued to see how much of a difference removing the type instability makes to performance - will give it a go. |
I saw basically no difference (you can't get much faster than 1.8 ns), but would do it on principle anyway. Would be easier on the branch predictor. |
Yep also saw no difference for For julia> mutable struct TestMutableStruct
a::String
end
julia> tms = TestMutableStruct("a")
TestMutableStruct("a")
julia> ref = Ref(tms)
Base.RefValue{TestMutableStruct}(TestMutableStruct("a"))
julia> p = Base.unsafe_convert(Ptr{typeof(ref[])}, ref)
Ptr{TestMutableStruct} @0x000000012a49d140
julia> Base.unsafe_pointer_to_objref(p)
TestMutableStruct("a")
julia> tsptr(p)
ERROR: TypeError: in typeassert, expected Base.RefValue{TestMutableStruct}, got a value of type TestMutableStruct
Stacktrace:
[1] tsptr(::Ptr{TestMutableStruct}) at ./REPL[21]:1
[2] top-level scope at REPL[22]:1 Is this expected? Seems that |
julia> tsptr2(x::Ptr{T}) where {T} = ccall(:jl_value_ptr, Ref{TestMutableStruct}, (Ptr{Cvoid},), x)
tsptr2 (generic function with 1 method)
julia> tsptr2(p)
TestMutableStruct("a") You need to specify julia> x = Ref(("hi", "world", 9))
Base.RefValue{Tuple{String, String, Int64}}(("hi", "world", 9)) But now in your example, |
When using
CheapThreads.jl
, I get allocations when newReference
s are created inobject_and_preserve
. This makes sense, but I'd really like to do away with this is possible as my aim is to useCheapThreads.jl
withinDifferentialEquations.jl
and I don't want the allocs to build up. In my case the arg that is being referenced is astruct
, but you also get allocations for other!isbits
types, e.g.:I can see two possible solutions:
1. Allow users to create their own
Reference
sThis just needs
object_and_preserve
to be extended likeWhen added, it works like so:
This seems to work fine with
batch
fromCheapThreads.jl
.2. Encourage users to extend
object_and_preserve
,store!
andload
for theirstruct
sFor example:
This seems to be fine with
CheapThreads
, but I don't know if there are implications of doing this.The above extensions only work for mutable structs because of
pointer_from_objref
.I'm interested to know what people's thoughts are on the best way forward for this!
The text was updated successfully, but these errors were encountered: