I’d like to use reinterpreted arrays to write into and read from an array. Here is a little script that is a bit oversimplified but not too far from my real use-case.
using BenchmarkTools
function cheb!(A, x)
A[1] = 1
A[2] = x
for n = 3:length(A)
A[n] = 2 * x * A[n-1] - A[n-2]
end
end
# Standard Array
A = zeros(100)
# Reinterpreted Array
B = reinterpret(Float64, zeros(UInt8, 100 * sizeof(Float64)))
# simple benchmark
x = rand()
print(" Array: "); @btime cheb!($A, $x)
print("ReinterpretArray: "); @btime cheb!($B, $x)
I always assumed that the abstraction here would be free, but apparently not. It is ok, not great on Julia 1.7 but terrible on Julia 1.8:
function unsafe_arraycast(::Type{D}, ary::Vector{S}) where {S, D}
l = sizeof(S)*length(ary)÷sizeof(D)
res = ccall(:jl_reshape_array, Vector{D}, (Any, Any, Any), Vector{D}, ary, (l,))
return res
end
And maybe my most important Question - is there a reason for me to not use unsafe_wrap or UnsafeArrays as long as I always keep around the original reference? E.g. like this:
struct MyVector{T} <: Vector{T}
_A::Vector{UInt8}
A::Vector{T}
end
I think as long you keep the reference to the original Vector you are safe to use both, the GC will not free the memory exactly because you keep the original reference.
I think no, as the reference to the memory is the same. You can check it with
julia> function unsafe_arraycast(::Type{D}, ary::Vector{S}) where {S, D}
l = sizeof(S)*length(ary)÷sizeof(D)
res = ccall(:jl_reshape_array, Vector{D}, (Any, Any, Any), Vector{D}, ary, (l,))
return res
end
unsafe_arraycast (generic function with 1 method)
julia> A = zeros(UInt8, 10 * sizeof(Float64));
julia> B = unsafe_arraycast(Float64, A);
julia> pointer(A)
Ptr{UInt8} @0x00007f744d5eba28
julia> pointer(B)
Ptr{Float64} @0x00007f744d5eba28