238238 end
239239end
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
248248end
249249
250250function 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... )
256256end
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
267333end
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 ))
313400end
0 commit comments