Recyclers.jl
Recyclers
— ModuleRecyclers
Recyclers.jl is a set of tools for implementing memory reuse patterns in multi-tasking Julia programs.
julia> using Recyclers
julia> recycler = Recyclers.CentralizedRecycler(() -> zeros(3));
julia> xs = Recyclers.get!(recycler) # get a cached object or create a new one
3-element Vector{Float64}:
0.0
0.0
0.0
julia> Recyclers.recycle!(recycler, xs) # returns `true` if recycled
true
Constructors
Recyclers.AbstractRecycler
— TypeAbstractRecycler{T}
A high-level object recycling interface. It pairs an AbstractCache{T}
with an object factory
function.
A concrete subtype Recycler{T}
of AbstractRecycler{T}
(e.g., ShardedRecycler
) have the following constructor methods:
Recycler{T}(factory)
Recycler(factory)
where factory
is a zero-argument function that returns an object of type T
and objects
is an iterable with elements of type T
. The constructor of each Recycler
subtype may accept additional keyword arguments.
factory
function contracts. The program author calling Recycler
constructor must verify that invocation of factory
function on arbitrary worker thread does not introduce any concurrency problems including data races. When used with the method Recycler{T}(factory)
, the factory
function must return an object of type T
(where T
does not have to be concrete; i.e., T = Any
always works). A run-time error is thrown if factory
is called and return an object that is not of type T
. When used with the method Recycler(factory)
, the factory
function must be type-stable in the sense that objects o1 = factory()
and o2 = factory()
obtained in arbitrary calling contexts must satisfy typeof(o1) === typeof(o2)
. With any constructor methods, changes in the return type of factory
function after construction of the recycler may result in run-time error.
Supported methods:
Recyclers.ShardedRecycler
— TypeRecyclers.ShardedRecycler{T}(factory; [limit])
Recyclers.ShardedRecycler(factory; [limit])
Create a sharded object recycler. See AbstractRecycler
for more information on the constructor and supported methods.
The factory
function must not introduce any concurrency problems. See see AbstractRecycler
for more information.
ShardedRecycler
uses a stack of size bounded by limit
for each worker thread. The keyword argument limit
must be a positive integer less than typemax(UInt)
or Inf
.
Recyclers.CentralizedRecycler
— TypeRecyclers.CentralizedRecycler{T}(factory)
Recyclers.CentralizedRecycler(factory)
Create a centralized object recycler. See AbstractRecycler
for more information on the constructor and supported methods.
The factory
function must not introduce any concurrency problems. See see AbstractRecycler
for more information.
CentralizedRecycler
uses a lock-free unbounded stack. It supports thread-safe empty!(recycler)
method.
Recyclers.@global
— MacroRecyclers.@global recycler_name = make_recycler
Declare a global constant named recycler_name
(a symbol) which holds a recycler created by make_recycler
(an expression).
The recycler is re-initialized (using Recyclers.unsafe_init!
) while importing the package in which recycler_name
is defined.
If it can be ensured that no tasks are requiring to access recycler_name
during exit, it may be a good idea to call Recyclers.unsafe_destroy!
or Recyclers.unsafe_empty!
via atexit
.
Example:
Recyclers.@global INTS_RECYCLER = Recyclers.ShardedRecycler(() -> Vector{Int}(undef, 100))
atexit() do
Recyclers.unsafe_destroy!(INTS_RECYCLER)
end
Recyclers.AbstractCache
— TypeAbstractCache{T}
A low-level object recycling interface.
Supported methods:
Recyclers.get!(factory, cache)
Recyclers.maybeget!(cache)
Recyclers.recycle!(cache, object)
Recyclers.recycling!(body, factory, cache)
Recyclers.unsafe_empty!(cache)
See also: AbstractRecycler
Recycling objects
Recyclers.get!
— FunctionRecyclers.get!(recycler::AbstractRecycler{T}) -> object::T
Recyclers.get!(factory, cache::AbstractCache{T}) -> object::T
Obtain cached object
of type T
or create a new one using factory()
.
The factory
function must not introduce any concurrency problems. See see AbstractRecycler
for more information.
Recyclers.maybeget!
— FunctionRecyclers.maybeget!(recycler::AbstractRecycler{T}) -> Some{T}(object) or nothing
Recyclers.maybeget!(cache::AbstractCache{T}) -> Some{T}(object) or nothing
Obtain cached object
wrapped in a Some{T}
or nothing
.
Recyclers.recycle!
— FunctionRecyclers.recycle!(recycler::AbstractRecycler{T}), object::T) -> isrecycled::Bool
Recyclers.recycle!(cache::AbstractCache{T}), object::T) -> isrecycled::Bool
Try to recycle a object
and return true
if and only if the object
is recycled.
Recyclers.recycling!
— FunctionRecyclers.recycle!(body, recycler::AbstractRecycler{T})) -> result
Recyclers.recycle!(body, factory, cache::AbstractCache{T})) -> result
Call a single-argument function body
with a object
, try to recycle the object
, and then return the result
returned from body
.
The object
is not recycled if body
throws since the invariance of object
may not hold any more.
Managing recyclers
Recyclers.unsafe_empty!
— FunctionRecyclers.unsafe_empty!(recycler::AbstractRecycler) -> recycler
Recyclers.unsafe_empty!(cache::AbstractCache) -> cache
Empty a cache
or underlying cache of a recycler
to help garbage collecting pooled objects. It is unsafe in the sense that there is no protection against accesses from other tasks; i.e., the programmer calling this function is responsible for ensuring that no concurrent tasks are accessing recycler
.
Recyclers.unsafe_init!
— FunctionRecyclers.unsafe_init!(recycler) -> recycler
Re-initialize recycler
. It is unsafe in the sense that there is no protection against accesses from other tasks; i.e., the programmer calling this function is responsible for ensuring that no concurrent tasks are accessing recycler
.
Recyclers.unsafe_destroy!
— FunctionRecyclers.unsafe_destroy!(recycler::AbstractRecycler) -> recycler
Recyclers.unsafe_destroy!(cache::AbstractCache) -> cache
Destroy a cache
or underlying cache of a recycler
, typically to minimize serialized global cache. The cache
and recycler
cannot be used unless Recyclers.unsafe_init!
is called first. It is unsafe in the sense that there is no protection against accesses from other tasks; i.e., the programmer calling this function is responsible for ensuring that no concurrent tasks are accessing recycler
.