Skip to content

Commit

Permalink
Use Empty in collector(ElType) for thread-safety
Browse files Browse the repository at this point in the history
  • Loading branch information
tkf committed May 21, 2020
1 parent a3af794 commit 92e0d90
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 11 deletions.
8 changes: 3 additions & 5 deletions benchmark/bench_collectors.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module BenchCollectors

using BangBang: collector, finish!, push!!
using BangBang: Empty, collector, finish!, push!!
using BenchmarkTools: @benchmarkable, BenchmarkGroup
using SplittablesBase: amount

Expand Down Expand Up @@ -55,11 +55,9 @@ end
end
end

const vec0 = Union{}[]
collect_collector(src) = finish!(_foldl(push!!, collector(), src))

collect_collector(src) = finish!(_foldl(push!!, collector(vec0), src))

collect_vector(src) = _foldl(push!!, vec0, src)
collect_vector(src) = _foldl(push!!, Empty(Vector), src)

collect_collector_nonexpanding(src) =
finish!(_foldl(push!!, collector(resize!(Union{}[], amount(src)), Val(true)), src))
Expand Down
2 changes: 2 additions & 0 deletions src/NoBang/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ function _setindex(d0::AbstractDict, v, k)
return d
end

resize(xs::AbstractVector, n::Integer) = similar(xs, (n,))

setproperty(value, name, x) = setproperties(value, NamedTuple{(name,)}((x,)))

materialize(::Any, x) = Broadcast.materialize(x)
Expand Down
14 changes: 14 additions & 0 deletions src/NoBang/emptycontainers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ append(::Empty{T}, x) where T = T(x)
append(e::Empty, ::Empty) = e

_empty(x::Empty) = x
resize(::Empty{T}, n::Integer) where {T <: AbstractVector} = similar(T, (n,))

_union(::Empty{T}, x) where {T} = unique!!(T(x))
_union(e::Empty, ::Empty) = e
Expand All @@ -83,5 +84,18 @@ Base.get(::Empty, _, default) = default
Base.IteratorSize(::Type{<:Empty}) = Base.HasLength()
Base.IteratorEltype(::Type{<:Empty}) = Base.HasEltype()
Base.eltype(::Type{<:Empty}) = Union{}
Base.eltype(::Type{<:Empty{<:AbstractVector{T}}}) where {T} = T
Base.length(::Empty) = 0
Base.iterate(::Empty) = nothing

Base.firstindex(::Empty{<:Vector}) = 1
Base.lastindex(::Empty{<:Vector}) = 0
Base.similar(e::Empty) = similar(e, eltype(e), (0,))
Base.similar(e::Empty, dims::Int...) = similar(e, eltype(e), dims)
Base.similar(e::Empty, dims::Tuple{Vararg{Int}}) = similar(e, eltype(e), dims)
Base.similar(e::Empty, T::Type) = similar(e, T, (0,))
Base.similar(e::Empty, T::Type, dims::Int...) = similar(e, T, dims)
Base.similar(::Empty{<:Vector}, T::Type, dims::Tuple{Vararg{Int}}) =
similar(Vector{T}, dims)
Base.similar(::Empty{Vector{T}}, dims::Tuple{Vararg{Int}}) where {T} =
similar(Vector{T}, dims)
9 changes: 9 additions & 0 deletions src/base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,15 @@ possible(::typeof(_setindex!), ::C, ::V, ::K) where {C <: AbstractDict, V, K} =
promote_type(keytype(C), K) <: keytype(C) &&
promote_type(valtype(C), V) <: valtype(C)

"""
resize!!(vector::AbstractVector, n::Integer) -> vector′
"""
resize!!(xs::Union{AbstractVector,Empty{<:AbstractVector}}, n::Integer) =
implements(resize!, xs) ? resize!(xs, n) : NoBang.resize(xs, n)

pure(::typeof(resize!)) = NoBang.resize
_asbb(::typeof(resize!)) = resize!!

"""
setproperties!!(value, patch::NamedTuple)
setproperties!!(value; patch...)
Expand Down
17 changes: 11 additions & 6 deletions src/collectors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ julia> finish!(append!!(collector(Vector{Float64}(undef, 10), Val(true)), [1, 2,
collector
collector(data::AbstractArray, ::Val{false} = Val(false)) = SafeCollector(data)
collector(data::AbstractArray, ::Val{true}) = UnsafeCollector(data)
collector(ElType::Type = Union{}) = collector(ElType[])
collector(ElType::Type = Union{}) = SafeCollector(Empty(Vector{ElType}), 1)

abstract type AbstractCollector end

Expand Down Expand Up @@ -79,10 +79,12 @@ _next_length(data, src) = max(4, length(data) + length(src), length(data) * 2)
i = c.i
if possible(_append!, data, src)
if c isa UnsafeCollector
data′ = data
elseif i + length(src) - 1 > lastindex(data)
resize!(data, _next_length(data, src))
data′ = resize!!(data, _next_length(data, src))
else
data′ = data
end
data′ = data
else
T = promote_type(eltype(data), eltype(src))
if i + length(src) - 1 > lastindex(data)
Expand All @@ -109,10 +111,13 @@ end
::Base.IteratorEltype,
)
if c isa UnsafeCollector
data′′ = c.data
elseif c.i + length(src) - 1 > lastindex(c.data)
resize!(c.data, _next_length(c.data, src))
data′′ = resize!!(c.data, _next_length(c.data, src))
else
data′′ = c.data
end
data, i = foldl(src, init = (c.data, c.i)) do (data, i), v
data, i = foldl(src, init = (data′′, c.i)) do (data, i), v
Base.@_inline_meta
T = promote_type(eltype(data), eltype(v))
if T <: eltype(data)
Expand Down Expand Up @@ -145,7 +150,7 @@ Extract the `data` collected in the collector `c`.
See [`collector`](@ref).
"""
finish!(c::AbstractCollector) = resize!(c.data, c.i - firstindex(c.data))
finish!(c::AbstractCollector) = resize!!(c.data, c.i - firstindex(c.data))
# Final length is `(c.i - 1) - (firstindex(c.data) - 1)` where the
# first `- 1` is because `c.i` is the index for the next element and
# the second `- 1` is for turning the index to offset.
6 changes: 6 additions & 0 deletions src/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ implements(::Mutator, ::Type{<:ImmutableContainer}) = false
implements(::Mutator, ::Type{<:MaybeMutableContainer}) = true
implements(::Mutator, ::Type{<:AbstractString}) = false

implements(::typeof(resize!), ::Type{<:AbstractVector}) = true
implements(
::typeof(resize!),
::Type{<:Union{SubArray{<:Any,1},Base.ReshapedArray{<:Any,1}}},
) = false

Base.@pure ismutablestruct(T::DataType) = T.mutable
implements(::typeof(setproperty!), T::DataType) = ismutablestruct(T)
implements(::typeof(setproperty!), ::Type{<:NamedTuple}) = false
Expand Down
26 changes: 26 additions & 0 deletions test/test_empty_proxy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,36 @@ module TestEmptyProxy

include("preamble.jl")

==ₜₗ(_, _) = false
==ₜₗ(x::T, y::T) where {T} = length(x) == length(y)

@testset begin
@test collect(Empty(Vector)) == Union{}[]
@test collect(Empty(Dict)) == Union{}[]
@test collect(Empty(Set)) == Union{}[]
end

@testset "similar" begin
@test similar(Empty(Vector)) ==ₜ Union{}[]
@test similar(Empty(Vector{Union{}})) ==ₜ Union{}[]
@test similar(Empty(Vector{Int})) ==ₜ Int[]
@test similar(Empty(Vector), Int) ==ₜ Int[]
@test similar(Empty(Vector{Union{}}), Int) ==ₜ Int[]
@test similar(Empty(Vector{Int}), Symbol) ==ₜ Symbol[]

@test similar(Empty(Vector), 3) ==ₜₗ resize!(Union{}[], 3)
@test similar(Empty(Vector{Union{}}), 3) ==ₜₗ resize!(Union{}[], 3)
@test similar(Empty(Vector{Int}), 3) ==ₜₗ resize!(Int[], 3)
@test similar(Empty(Vector), Int, 3) ==ₜₗ resize!(Int[], 3)
@test similar(Empty(Vector{Union{}}), Int, 3) ==ₜₗ resize!(Int[], 3)
@test similar(Empty(Vector{Int}), Symbol, 3) ==ₜₗ resize!(Symbol[], 3)

@test similar(Empty(Vector), (3,)) ==ₜₗ resize!(Union{}[], 3)
@test similar(Empty(Vector{Union{}}), (3,)) ==ₜₗ resize!(Union{}[], 3)
@test similar(Empty(Vector{Int}), (3,)) ==ₜₗ resize!(Int[], 3)
@test similar(Empty(Vector), Int, (3,)) ==ₜₗ resize!(Int[], 3)
@test similar(Empty(Vector{Union{}}), Int, (3,)) ==ₜₗ resize!(Int[], 3)
@test similar(Empty(Vector{Int}), Symbol, (3,)) ==ₜₗ resize!(Symbol[], 3)
end

end # module

0 comments on commit 92e0d90

Please sign in to comment.