JuliaSIMD / JuliaSIMD/StrideArraysCore.jl

Removing allocations for repeated use of object_and_preserve

Open
#4 6 comments 0 reactions 0 assignees View on GitHub
Dominant language
Julia
Stars
19
Forks
10
PR merge metrics
No merged PRs in 30d

Description

When using `CheapThreads.jl`, I get allocations when new `Reference`s are created in `object_and_preserve`. This makes sense, but I'd really like to do away with this is possible as my aim is to use `CheapThreads.jl` within `DifferentialEquations.jl` and I don't want the allocs to build up. In my case the arg that is being referenced is a `struct`, but you also get allocations for other `!isbits` types, e.g.:

```Julia
julia> f1(arg) = @time StrideArraysCore.object_and_preserve(arg)
julia> f1(1)
0.000000 seconds
(1, nothing)

julia> f1("string")
0.000000 seconds (1 allocation: 16 bytes)
(StrideArraysCore.Reference{String}("string"), StrideArraysCore.Reference{String}("string"))

julia> struct TestStruct
a::Vector{Int64}
end

julia> ts = TestStruct([1])
TestStruct([1])

julia> f1(ts)
0.000000 seconds (1 allocation: 16 bytes)
(StrideArraysCore.Reference{TestStruct}(TestStruct([1])), StrideArraysCore.Reference{TestStruct}(TestStruct([1])))
```

I can see two possible solutions:

#### 1. Allow users to create their own `Reference`s

This just needs `object_and_preserve` to be extended like

```Julia
@inline object_and_preserve(r::Reference) = (r, r)
```

When added, it works like so:

```Julia
julia> ref = StrideArraysCore.Reference("string")
StrideArraysCore.Reference{String}("string")

julia> f1(ref)
0.000000 seconds
(StrideArraysCore.Reference{String}("string"), StrideArraysCore.Reference{String}("string"))

julia> ref = StrideArraysCore.Reference(ts)
StrideArraysCore.Reference{TestStruct}(TestStruct([1]))

julia> f1(ref)
0.000000 seconds
(StrideArraysCore.Reference{TestStruct}(TestStruct([1])), StrideArraysCore.Reference{TestStruct}(TestStruct([1])))
```

This seems to work fine with `batch` from `CheapThreads.jl`.

#### 2. Encourage users to extend `object_and_preserve`, `store!` and `load` for their `struct`s

For example:

```Julia
@inline function ThreadingUtilities.load(p::Ptr{UInt}, ::Type{TestMutableStruct}, i)
i, ref = ThreadingUtilities.load(p, Ptr{TestMutableStruct}, i)
i, Base.unsafe_pointer_to_objref(ref)::TestMutableStruct
end
@inline function ThreadingUtilities.store!(p::Ptr{UInt}, r::TestMutableStruct, i)
ThreadingUtilities.store!(p + i, reinterpret(UInt, pointer_from_objref(r)))
i + sizeof(UInt)
end
@inline StrideArraysCore.object_and_preserve(r::TestMutableStruct) = (r, r)
```

This seems to be fine with `CheapThreads`, but I don't know if there are implications of doing this.

```Julia
julia> arg_test(args, start, stop) = nothing

julia> mutable struct TestMutableStruct
a::String
end

julia> tms = TestMutableStruct("a")
TestMutableStruct("a")

julia> u = zeros(100)
...
julia> @time batch(arg_test, (10, 8), u, tms)
0.000003 seconds
```

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!

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.