Skip to content

Commit a869b6f

Browse files
authored
Merge pull request #61 from JuliaObjects/tests
Add allocation and inference tests
2 parents 2044dd5 + 779b210 commit a869b6f

File tree

2 files changed

+108
-19
lines changed

2 files changed

+108
-19
lines changed

.github/workflows/CI.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ jobs:
1010
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
1111
runs-on: ${{ matrix.os }}
1212
strategy:
13-
fail-fast: true
13+
fail-fast: false
1414
matrix:
1515
version:
1616
- '1.0'
1717
- '1.3'
1818
- '1.5'
19+
- '1.6'
20+
- '1'
1921
- 'nightly'
2022
os:
2123
- ubuntu-latest

test/runtests.jl

Lines changed: 105 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ end
238238
end
239239
end
240240

241-
function funny_numbers(n)::Tuple
241+
function funny_numbers(::Type{Tuple}, n)::Tuple
242242
types = [
243243
Int128, Int16, Int32, Int64, Int8,
244244
UInt128, UInt16, UInt32, UInt64, UInt8,
@@ -248,47 +248,124 @@ function funny_numbers(n)::Tuple
248248
end
249249

250250
function funny_numbers(::Type{NamedTuple}, n)::NamedTuple
251-
t = funny_numbers(n)
251+
t = funny_numbers(Tuple,n)
252252
pairs = map(1:n) do i
253253
Symbol("a$i") => t[i]
254254
end
255255
(;pairs...)
256256
end
257257

258-
for n in [0,1,20,40]
258+
abstract type S end
259+
Sn_from_n = Dict{Int,Type}()
260+
for n in [0,1,10,20,40]
259261
Sn = Symbol("S$n")
260262
types = [Symbol("A$i") for i in 1:n]
261263
fields = [Symbol("a$i") for i in 1:n]
262264
typed_fields = [:($ai::$Ai) for (ai,Ai) in zip(fields, types)]
263-
@eval struct $(Sn){$(types...)}
265+
@eval struct $(Sn){$(types...)} <: S
264266
$(typed_fields...)
265267
end
266-
@eval funny_numbers(::Type{$Sn}) = ($Sn)(funny_numbers($n)...)
268+
@eval Sn_from_n[$n] = $Sn
269+
end
270+
function funny_numbers(::Type{S}, n)::S
271+
fields = funny_numbers(Tuple, n)
272+
Sn_from_n[n](fields...)
273+
end
274+
275+
reconstruct(obj, content) = constructorof(typeof(obj))(content...)
276+
277+
function write_output_to_ref!(f, out_ref::Ref, arg_ref::Ref)
278+
arg = arg_ref[]
279+
out_ref[] = f(arg)
280+
out_ref
281+
end
282+
function write_output_to_ref!(f, out_ref::Ref, arg_ref1::Ref, arg_ref2::Ref)
283+
arg1 = arg_ref1[]
284+
arg2 = arg_ref2[]
285+
out_ref[] = f(arg1,arg2)
286+
out_ref
287+
end
288+
function hot_loop_allocs(f::F, args...) where {F}
289+
# we want to test that f(args...) does not allocate
290+
# when used in hot loops
291+
# however a naive @allocated f(args...)
292+
# will not be representative of what happens in an inner loop
293+
# Instead it will sometimes box inputs/outputs
294+
# and report too many allocations
295+
# so we use Refs to minimize inputs and outputs
296+
out_ref = Ref(f(args...))
297+
arg_refs = map(Ref, args)
298+
write_output_to_ref!(f, out_ref, arg_refs...)
299+
out_ref = typeof(out_ref)() # erase out_ref so we can assert work was done later
300+
# Avoid splatting args... which also results in undesired allocs
301+
allocs = if length(arg_refs) == 1
302+
r1, = arg_refs
303+
@allocated write_output_to_ref!(f, out_ref, r1)
304+
elseif length(arg_refs) == 2
305+
r1,r2 = arg_refs
306+
@allocated write_output_to_ref!(f, out_ref, r1, r2)
307+
else
308+
error("TODO too many args")
309+
end
310+
@assert out_ref[] == f(args...)
311+
return allocs
312+
end
313+
314+
@testset "no allocs $T" for T in [Tuple, NamedTuple, S]
315+
@testset "n = $n" for n in [0,1,10,20]
316+
obj = funny_numbers(T, n)
317+
new_content = funny_numbers(Tuple, n)
318+
@test 0 == hot_loop_allocs(constructorof, typeof(obj))
319+
@test 0 == hot_loop_allocs(reconstruct, obj, new_content)
320+
@test 0 == hot_loop_allocs(getproperties, obj)
321+
patch_sizes = [0,1,n÷3,n÷2,n]
322+
patch_sizes = min.(patch_sizes, n)
323+
patch_sizes = unique(patch_sizes)
324+
for k in patch_sizes
325+
patch = if T === Tuple
326+
funny_numbers(Tuple, k)
327+
else
328+
funny_numbers(NamedTuple, k)
329+
end
330+
@test 0 == hot_loop_allocs(setproperties, obj, patch)
331+
end
332+
end
267333
end
268334

269335
@testset "inference" begin
270336
@testset "Tuple n=$n" for n in [0,1,2,3,4,5,10,20,30,40]
271-
t = funny_numbers(n)
337+
t = funny_numbers(Tuple,n)
272338
@test length(t) == n
273339
@test getproperties(t) === t
274340
@inferred getproperties(t)
341+
@inferred constructorof(typeof(t))
342+
content = funny_numbers(Tuple,n)
343+
@inferred reconstruct(t, content)
275344
for k in 0:n
276-
t2 = funny_numbers(k)
277-
@inferred setproperties(t, t2)
345+
t2 = funny_numbers(Tuple,k)
278346
@test setproperties(t, t2)[1:k] === t2
279347
@test setproperties(t, t2) isa Tuple
280348
@test length(setproperties(t, t2)) == n
281349
@test setproperties(t, t2)[k+1:n] === t[k+1:n]
350+
@inferred setproperties(t, t2)
282351
end
283352
end
284-
@inferred getproperties(funny_numbers(100))
285-
@inferred setproperties(funny_numbers(100), funny_numbers(90))
353+
@inferred getproperties(funny_numbers(Tuple,100))
354+
@inferred setproperties(funny_numbers(Tuple,100), funny_numbers(Tuple,90))
355+
286356
@testset "NamedTuple n=$n" for n in [0,1,2,3,4,5,10,20,30,40]
287357
nt = funny_numbers(NamedTuple, n)
288358
@test nt isa NamedTuple
289359
@test length(nt) == n
290360
@test getproperties(nt) === nt
291361
@inferred getproperties(nt)
362+
363+
@inferred constructorof(typeof(nt))
364+
if VERSION >= v"1.3"
365+
content = funny_numbers(NamedTuple,n)
366+
@inferred reconstruct(nt, content)
367+
end
368+
#no_allocs_test(nt, content)
292369
for k in 0:n
293370
nt2 = funny_numbers(NamedTuple, k)
294371
@inferred setproperties(nt, nt2)
@@ -301,13 +378,23 @@ end
301378
@inferred getproperties(funny_numbers(NamedTuple, 100))
302379
@inferred setproperties(funny_numbers(NamedTuple, 100), funny_numbers(NamedTuple, 90))
303380

381+
@inferred setproperties(funny_numbers(S,0), funny_numbers(NamedTuple, 0))
382+
@inferred setproperties(funny_numbers(S,1), funny_numbers(NamedTuple, 1))
383+
@inferred setproperties(funny_numbers(S,20), funny_numbers(NamedTuple, 18))
384+
@inferred setproperties(funny_numbers(S,40), funny_numbers(NamedTuple, 38))
385+
@inferred constructorof(S0)
386+
@inferred constructorof(S1)
387+
@inferred constructorof(S20)
388+
@inferred constructorof(S40)
389+
if VERSION >= v"1.3"
390+
@inferred reconstruct(funny_numbers(S,0) , funny_numbers(Tuple,0))
391+
@inferred reconstruct(funny_numbers(S,1) , funny_numbers(Tuple,1))
392+
@inferred reconstruct(funny_numbers(S,20), funny_numbers(Tuple,20))
393+
@inferred reconstruct(funny_numbers(S,40), funny_numbers(Tuple,40))
394+
end
304395

305-
@inferred setproperties(funny_numbers(S0), funny_numbers(NamedTuple, 0))
306-
@inferred setproperties(funny_numbers(S1), funny_numbers(NamedTuple, 1))
307-
@inferred setproperties(funny_numbers(S20), funny_numbers(NamedTuple, 18))
308-
@inferred setproperties(funny_numbers(S40), funny_numbers(NamedTuple, 38))
309-
@inferred getproperties(funny_numbers(S0))
310-
@inferred getproperties(funny_numbers(S1))
311-
@inferred getproperties(funny_numbers(S20))
312-
@inferred getproperties(funny_numbers(S40))
396+
@inferred getproperties(funny_numbers(S,0))
397+
@inferred getproperties(funny_numbers(S,1))
398+
@inferred getproperties(funny_numbers(S,20))
399+
@inferred getproperties(funny_numbers(S,40))
313400
end

0 commit comments

Comments
 (0)