Skip to content

Commit

Permalink
test for suboptimal type inference and unnecessary allocation (#37)
Browse files Browse the repository at this point in the history
Also otherwise somewhat tidied up the test suite and made it more
comprehensive in a few places.

Fixes #27
  • Loading branch information
nsajko authored Apr 24, 2024
1 parent 0540043 commit 75d8f75
Showing 1 changed file with 122 additions and 79 deletions.
201 changes: 122 additions & 79 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,47 @@ import Aqua

const checked_dims = FixedSizeArrays.checked_dims

function allocated(f::F, args::Vararg{Any,N}) where {F,N}
@allocated f(args...)
# helpers for testing for allocation or suboptimal inference

allocated(f::F, args::Tuple) where {F} = @allocated f(args...)
allocated(::Type{T}, args::Tuple) where {T} = @allocated T(args...)
test_noalloc(f::F, args::Tuple) where {F} = @test iszero(allocated(f, args))
test_noalloc(::Type{T}, args::Tuple) where {T} = @test iszero(allocated(T, args))
test_inferred(f::F, ::Type{R}, args::Tuple) where {F,R} = @test (@inferred f(args...)) isa R
test_inferred(::Type{T}, ::Type{R}, args::Tuple) where {T,R} = @test (@inferred T(args...)) isa R
test_inferred(::Type{T}, args::Tuple) where {T } = test_inferred(T, T, args)
function test_inferred_noalloc(f::F, ::Type{R}, args::Tuple) where {F,R}
test_noalloc(f, args)
test_inferred(f, R, args)
end
function test_inferred_noalloc(::Type{T}, ::Type{R}, args::Tuple) where {T,R}
test_noalloc(T, args)
test_inferred(T, R, args)
end
function test_inferred_noalloc(::Type{T}, args::Tuple) where {T }
test_noalloc(T, args)
test_inferred(T, args)
end

@testset "FixedSizeArrays" begin
@testset "meta" begin
@test isempty(detect_ambiguities(Main)) # test for ambiguities in this file
end

@testset "Aqua.jl" begin
Aqua.test_all(FixedSizeArrays)
end

@testset "Constructors" begin
@test FixedSizeArray{Float64,0}(undef) isa FixedSizeArray{Float64,0}
@test FixedSizeArray{Float64,0}(undef, ()) isa FixedSizeArray{Float64,0}
for dim_count 0:4
siz = ntuple(Returns(2), dim_count)
T = FixedSizeArray{Float64}
R = FixedSizeArray{Float64,dim_count}
for args ((undef, siz), (undef, siz...))
test_inferred( R, args)
test_inferred(T, R, args)
end
end
for offset (-1, 0, 2, 3)
ax = offset:(offset + 1)
oa = OffsetArray([10, 20], ax)
Expand Down Expand Up @@ -57,41 +86,40 @@ end
if 20 < n
@test_throws ArgumentError checked_dims(t)
else
@test factorial(n) == prod(t) == @inferred checked_dims(t)
@test iszero(allocated(checked_dims, t))
@test factorial(n) == prod(t) == checked_dims(t)
test_inferred_noalloc(checked_dims, Int, (t,))
end
end
end

@testset "FixedSizeVector" begin
v = FixedSizeVector{Float64}(undef, 3)
@test length(v) == 3
v .= 1:3
test_inferred_noalloc(((a, b) -> a .= b), FixedSizeVector{Float64}, (v, 1:3))
@test v == 1:3
@test typeof(v) == typeof(similar(v)) == FixedSizeVector{Float64}
@test similar(v, Int) isa FixedSizeVector{Int}
@test eltype(similar(v, Int)) == Int
@test copy(v) isa FixedSizeVector{Float64}
@test zero(v) isa FixedSizeVector{Float64}
@test similar(FixedSizeVector{Int}, (2,)) isa FixedSizeVector{Int}
@test similar(FixedSizeArray{Int}, (2,)) isa FixedSizeVector{Int}
@test FixedSizeArray{Int}(undef, 2) isa FixedSizeVector{Int}
test_inferred(similar, FixedSizeVector{Float64}, (v,))
test_inferred(similar, FixedSizeVector{Int}, (v, Int))
test_inferred(copy, FixedSizeVector{Float64}, (v,))
test_inferred(zero, FixedSizeVector{Float64}, (v,))
test_inferred(similar, FixedSizeVector{Int}, (FixedSizeVector{Int}, (2,)))
test_inferred(similar, FixedSizeVector{Int}, (FixedSizeArray{Int}, (2,)))
example_abstract_vectors = (7:9, [7, 8, 9], OffsetArray([7, 8, 9], 1:3))
test_convert = function(T, a)
test_inferred(convert, FixedSizeVector{Int}, (T, a))
c = convert(T, a)
test_inferred(convert, FixedSizeVector{Int}, (T, c))
@test c == a
@test c === convert(T, c)
end
for T (FixedSizeArray, FixedSizeVector)
for a example_abstract_vectors
@test convert(T, a) isa FixedSizeVector{Int}
@test convert(T, a) == a
@test convert(T, convert(T, a)) isa FixedSizeVector{Int}
@test convert(T, convert(T, a)) == a
test_convert(T, a)
end
end
for T (FixedSizeArray{Int}, FixedSizeVector{Int})
for S (Int, Float64)
for a map((c -> map(S, c)), example_abstract_vectors)
@test convert(T, a) isa FixedSizeVector{Int}
@test convert(T, a) == a
@test convert(T, convert(T, a)) isa FixedSizeVector{Int}
@test convert(T, convert(T, a)) == a
test_convert(T, a)
end
end
end
Expand All @@ -101,35 +129,34 @@ end
m = FixedSizeMatrix{Float64}(undef, 3, 3)
m_ref = reshape(1:9, size(m))
@test length(m) == 9
m .= m_ref
test_inferred_noalloc(((a, b) -> a .= b), FixedSizeMatrix{Float64}, (m, m_ref))
@test m == m_ref
@test typeof(m) == typeof(similar(m)) == FixedSizeMatrix{Float64}
@test similar(m, Int) isa FixedSizeMatrix{Int}
@test eltype(similar(m, Int)) == Int
@test copy(m) isa FixedSizeMatrix{Float64}
@test zero(m) isa FixedSizeMatrix{Float64}
@test similar(FixedSizeMatrix{Int}, (2, 3)) isa FixedSizeMatrix{Int}
@test similar(FixedSizeArray{Int}, (2, 3)) isa FixedSizeMatrix{Int}
@test FixedSizeArray{Int}(undef, 2, 3) isa FixedSizeMatrix{Int}
test_inferred(similar, FixedSizeMatrix{Float64}, (m,))
test_inferred(similar, FixedSizeMatrix{Int}, (m, Int))
test_inferred(copy, FixedSizeMatrix{Float64}, (m,))
test_inferred(zero, FixedSizeMatrix{Float64}, (m,))
test_inferred(similar, FixedSizeMatrix{Int}, (FixedSizeMatrix{Int}, (2, 3)))
test_inferred(similar, FixedSizeMatrix{Int}, (FixedSizeArray{Int}, (2, 3)))
example_abstract_matrices = (
reshape(1:9, (3, 3)),
OffsetArray(reshape(1:9, (3, 3)), 1:3, 1:3),
)
test_convert = function(T, a)
test_inferred(convert, FixedSizeMatrix{Int}, (T, a))
c = convert(T, a)
test_inferred(convert, FixedSizeMatrix{Int}, (T, c))
@test c == a
@test c === convert(T, c)
end
for T (FixedSizeArray, FixedSizeMatrix)
for a example_abstract_matrices
@test convert(T, a) isa FixedSizeMatrix{Int}
@test convert(T, a) == a
@test convert(T, convert(T, a)) isa FixedSizeMatrix{Int}
@test convert(T, convert(T, a)) == a
test_convert(T, a)
end
end
for T (FixedSizeArray{Int}, FixedSizeMatrix{Int})
for S (Int, Float64)
for a map((c -> map(S, c)), example_abstract_matrices)
@test convert(T, a) isa FixedSizeMatrix{Int}
@test convert(T, a) == a
@test convert(T, convert(T, a)) isa FixedSizeMatrix{Int}
@test convert(T, convert(T, a)) == a
test_convert(T, a)
end
end
end
Expand All @@ -140,36 +167,51 @@ end
size = ntuple(Returns(3), dim_count)
a = FixedSizeArray{Int, dim_count}(undef, size)
for v (3, 3.1, nothing)
mapped = map(Returns(v), a)
@test all(==(v), mapped)
@test mapped isa FixedSizeArray{typeof(v), dim_count}
args = (Returns(v), a)
@test all(==(v), map(args...))
test_inferred(map, FixedSizeArray{typeof(v), dim_count}, args)
end
end
end

@testset "broadcasting" begin
v3 = FixedSizeArray{Int}(undef, 3)
@test v3 isa FixedSizeVector{Int}
@test (@inferred (v3 + v3)) isa FixedSizeVector{Int}
@test (@inferred (v3 .+ v3)) isa FixedSizeVector{Int}
@test (@inferred (v3 .* v3)) isa FixedSizeVector{Int}
@test (@inferred (v3 .+ 3)) isa FixedSizeVector{Int}
@test (@inferred (v3 .* 3)) isa FixedSizeVector{Int}
@test (@inferred (v3 .+ .3)) isa FixedSizeVector{Float64}
@test (@inferred (v3 .* .3)) isa FixedSizeVector{Float64}
TVI = FixedSizeVector{Int}
TVF = FixedSizeVector{Float64}
v3 = TVI(1:3)
vf3 = TVF(1:3)
bp = (x, y) -> x .+ y
bm = (x, y) -> x .* y
test_inferred(+, TVI, (v3, v3))
test_inferred(bp, TVI, (v3, v3))
test_inferred(bm, TVI, (v3, v3))
test_inferred(+, TVF, (v3, vf3))
test_inferred(bp, TVF, (v3, vf3))
test_inferred(bm, TVF, (v3, vf3))
test_inferred(bp, TVI, (v3, 3))
test_inferred(bm, TVI, (v3, 3))
test_inferred(bp, TVF, (v3, .3))
test_inferred(bm, TVF, (v3, .3))
@testset "matrices" begin
m33 = FixedSizeArray{Int}(undef, 3, 3)
m13 = FixedSizeArray{Int}(undef, 1, 3)
m31 = FixedSizeArray{Int}(undef, 3, 1)
@test m33 isa FixedSizeMatrix{Int}
@test m13 isa FixedSizeMatrix{Int}
@test m31 isa FixedSizeMatrix{Int}
@test (@inferred (m33 .+ .3 )) isa FixedSizeMatrix{Float64}
@test (@inferred (m33 .+ 3 )) isa FixedSizeMatrix{Int}
@test (@inferred (m33 .+ v3 )) isa FixedSizeMatrix{Int}
@test (@inferred (m33 .+ m13)) isa FixedSizeMatrix{Int}
@test (@inferred (m33 .+ m31)) isa FixedSizeMatrix{Int}
@test (@inferred (m33 .+ m33)) isa FixedSizeMatrix{Int}
TMI = FixedSizeMatrix{Int}
TMF = FixedSizeMatrix{Float64}
m33 = TMI(undef, 3, 3)
m13 = TMI(undef, 1, 3)
m31 = TMI(undef, 3, 1)
test_inferred(+, TMI, (m33, m33))
test_inferred(bp, TMI, (m33, m33))
test_inferred(bm, TMI, (m33, m33))
test_inferred(bp, TMI, (m33, m13))
test_inferred(bm, TMI, (m33, m13))
test_inferred(bp, TMI, (m33, m31))
test_inferred(bm, TMI, (m33, m31))
test_inferred(bp, TMI, (m33, v3 ))
test_inferred(bm, TMI, (m33, v3 ))
test_inferred(bp, TMF, (m33, vf3))
test_inferred(bm, TMF, (m33, vf3))
test_inferred(bp, TMI, (m33, 3))
test_inferred(bm, TMI, (m33, 3))
test_inferred(bp, TMF, (m33, .3))
test_inferred(bm, TMF, (m33, .3))
end
end

Expand All @@ -181,6 +223,7 @@ end
for i_style (IndexLinear(), IndexCartesian())
for i eachindex(i_style, a)
@test isassigned(a, i) == isbitstype(elem_type)
test_inferred_noalloc(isassigned, Bool, (a, i))
end
end
end
Expand All @@ -193,6 +236,7 @@ end
end
for ij Iterators.product(1:3, 1:3)
@test isassigned(a, ij...) == (ij assigned_inds)
test_inferred_noalloc(isassigned, Bool, (a, ij...))
end
end
end
Expand All @@ -207,10 +251,11 @@ end
s = S{E}(undef, 5)
s .= 1:5
d = D{Float64}(undef, 5)
@test copyto!(d, s) isa D{Float64}
test_inferred_noalloc(copyto!, D{Float64}, (d, s))
@test copyto!(d, s) == 1:5
@test copyto!(d, 1, s, 1, length(s)) isa D{Float64}
@test copyto!(d, 1, s, 1, length(s)) == 1:5
args = (d, 1, s, 1, length(s))
test_inferred_noalloc(copyto!, D{Float64}, args)
@test copyto!(args...) == 1:5
@test_throws ArgumentError copyto!(d, 1, s, 1, -1)
end
end
Expand All @@ -220,20 +265,20 @@ end
@testset "$T" for T in (Float16, Float32, Float64)
v = randn(T, 8)
v_fixed = FixedSizeVector(v)
v_sum = @inferred v_fixed + v_fixed
@test v_sum isa FixedSizeVector{T}
test_inferred(+, FixedSizeVector{T}, (v_fixed, v_fixed))
v_sum = v_fixed + v_fixed
@test v_sum v + v
v_mul = @inferred v_fixed * v_fixed'
@test v_mul isa FixedSizeMatrix{T}
test_inferred(((x, y) -> x * y'), FixedSizeMatrix{T}, (v_fixed, v_fixed))
v_mul = v_fixed * v_fixed'
@test v_mul v * v'

M = randn(T, 8, 8)
M_fixed = FixedSizeMatrix(M)
M_sum = @inferred M_fixed + M_fixed
@test M_sum isa FixedSizeMatrix{T}
test_inferred(+, FixedSizeMatrix{T}, (M_fixed, M_fixed))
M_sum = M_fixed + M_fixed
@test M_sum M + M
M_mul = @inferred M_fixed * M_fixed
@test M_mul isa FixedSizeMatrix{T}
test_inferred(*, FixedSizeMatrix{T}, (M_fixed, M_fixed))
M_mul = M_fixed * M_fixed
@test M_mul M * M
end
end
Expand All @@ -244,10 +289,8 @@ end
siz = ntuple(Returns(2), dim_count)
arr = FixedSizeArray{elem_type, dim_count}(undef, siz)
@test pointer(arr) === pointer(arr, 1) === pointer(arr, 2) - sizeof(elem_type)
@test (@inferred pointer(arr)) isa Ptr{elem_type}
@test (@inferred pointer(arr, 1)) isa Ptr{elem_type}
@test iszero(allocated(pointer, arr))
@test iszero(allocated(pointer, arr, 1))
test_inferred_noalloc(pointer, Ptr{elem_type}, (arr,))
test_inferred_noalloc(pointer, Ptr{elem_type}, (arr, 1))
end
end
end
Expand Down

0 comments on commit 75d8f75

Please sign in to comment.