Skip to content

Commit 8c43170

Browse files
authored
specialize copyto! for performance (#23)
The fallback generic implementation of `copyto!` uses `setindex!` in an elementwise manner. Instead base the implementation on `unsafe_copyto!`. This improves the performance of `copyto!`, thus it also improves the performance of `copy!`, `copy` and of broadcasted operations. This change only applies when copying between two `FixedSizeArray`s, or between a `Memory` and a `FixedSizeArray`. Fixes #19
1 parent dd77e40 commit 8c43170

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

src/FixedSizeArrays.jl

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,12 @@ function Base.similar(
5151
similar(FixedSizeArray{E}, axes(bc))
5252
end
5353

54-
# one-based indexing check
54+
# helper functions
55+
56+
memory_of(m::Memory) = m
57+
memory_of(f::FixedSizeArray) = f.mem
58+
59+
first_linear_index(a) = first(eachindex(IndexLinear(), a))
5560

5661
axes_are_one_based(axes) = all(isone first, axes)
5762

@@ -77,4 +82,36 @@ FixedSizeArray(a::AbstractArray{T,N}) where {T,N} = FixedSizeArray{T,N}
7782
Base.convert(::Type{T}, a::T) where {T<:FixedSizeArray} = a
7883
Base.convert(::Type{T}, a::AbstractArray) where {T<:FixedSizeArray} = T(a)::T
7984

85+
# `copyto!` between `FixedSizeArray` and `Memory`
86+
87+
Base.@propagate_inbounds function copyto5!(dst, doff, src, soff, n)
88+
if !iszero(n)
89+
(n < false) &&
90+
throw(ArgumentError("the number of elements to copy must be nonnegative"))
91+
@boundscheck checkbounds(dst, doff:doff+n-1)
92+
@boundscheck checkbounds(src, soff:soff+n-1)
93+
@inbounds let d, s
94+
d = GenericMemoryRef(memory_of(dst), doff)
95+
s = GenericMemoryRef(memory_of(src), soff)
96+
unsafe_copyto!(d, s, n)
97+
end
98+
end
99+
dst
100+
end
101+
102+
Base.@propagate_inbounds function copyto2!(dst::T, src) where {T}
103+
fd = first_linear_index(dst)
104+
fs = first_linear_index(src)
105+
len = length(src)
106+
copyto5!(dst, fd, src, fs, len)::T
107+
end
108+
109+
Base.@propagate_inbounds Base.copyto!(dst::FixedSizeArray, doff::Integer, src::FixedSizeArray, soff::Integer, n::Integer) = copyto5!(dst, doff, src, soff, n)
110+
Base.@propagate_inbounds Base.copyto!(dst::FixedSizeArray, doff::Integer, src::Memory , soff::Integer, n::Integer) = copyto5!(dst, doff, src, soff, n)
111+
Base.@propagate_inbounds Base.copyto!(dst::Memory , doff::Integer, src::FixedSizeArray, soff::Integer, n::Integer) = copyto5!(dst, doff, src, soff, n)
112+
113+
Base.@propagate_inbounds Base.copyto!(dst::FixedSizeArray, src::FixedSizeArray) = copyto2!(dst, src)
114+
Base.@propagate_inbounds Base.copyto!(dst::FixedSizeArray, src::Memory ) = copyto2!(dst, src)
115+
Base.@propagate_inbounds Base.copyto!(dst::Memory , src::FixedSizeArray) = copyto2!(dst, src)
116+
80117
end # module FixedSizeArrays

test/runtests.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,22 @@ import Aqua
131131
end
132132
end
133133
end
134+
135+
@testset "`copyto!`" begin
136+
for (D, S) (
137+
(Memory, FixedSizeVector),
138+
(FixedSizeVector, Memory),
139+
(FixedSizeVector, FixedSizeVector),
140+
)
141+
for E (Float64, Int)
142+
s = S{E}(undef, 5)
143+
s .= 1:5
144+
d = D{Float64}(undef, 5)
145+
@test copyto!(d, s) isa D{Float64}
146+
@test copyto!(d, s) == 1:5
147+
@test copyto!(d, 1, s, 1, length(s)) isa D{Float64}
148+
@test copyto!(d, 1, s, 1, length(s)) == 1:5
149+
end
150+
end
151+
end
134152
end

0 commit comments

Comments
 (0)