@@ -472,41 +472,93 @@ function gc_bytes()
472472 b[]
473473end
474474
475- function allocated (f, args:: Vararg{Any,N} ) where {N}
475+ @constprop :none function allocated (f, args:: Vararg{Any,N} ) where {N}
476476 b0 = Ref {Int64} (0 )
477477 b1 = Ref {Int64} (0 )
478478 Base. gc_bytes (b0)
479- f (args... )
479+ @noinline f (args... )
480480 Base. gc_bytes (b1)
481481 return b1[] - b0[]
482482end
483483only (methods (allocated)). called = 0xff
484484
485- function allocations (f, args:: Vararg{Any,N} ) where {N}
485+ @constprop :none function allocations (f, args:: Vararg{Any,N} ) where {N}
486486 stats = Base. gc_num ()
487- f (args... )
487+ @noinline f (args... )
488488 diff = Base. GC_Diff (Base. gc_num (), stats)
489489 return Base. gc_alloc_count (diff)
490490end
491491only (methods (allocations)). called = 0xff
492492
493493function is_simply_call (@nospecialize ex)
494+ is_simple_atom (a) = a isa QuoteNode || a isa Symbol || is_self_quoting (a)
494495 Meta. isexpr (ex, :call ) || return false
495496 for a in ex. args
496- a isa QuoteNode && continue
497- a isa Symbol && continue
498- Base. is_self_quoting (a) && continue
497+ is_simple_atom (a) && continue
498+ Meta. isexpr (a, :... , 1 ) && is_simple_atom (a. args[1 ]) && continue
499499 return false
500500 end
501501 return true
502502end
503503
504+ function _gen_allocation_measurer (ex, fname:: Symbol )
505+ if isexpr (ex, :call )
506+ if ! is_simply_call (ex)
507+ ex = :((() -> $ ex)())
508+ end
509+ pushfirst! (ex. args, GlobalRef (Base, fname))
510+ return quote
511+ Experimental. @force_compile
512+ $ (esc (ex))
513+ end
514+ elseif fname === :allocated
515+ # v1.11-compatible implementation
516+ return quote
517+ Experimental. @force_compile
518+ local b0 = Ref {Int64} (0 )
519+ local b1 = Ref {Int64} (0 )
520+ gc_bytes (b0)
521+ $ (esc (ex))
522+ gc_bytes (b1)
523+ b1[] - b0[]
524+ end
525+ else
526+ @assert fname === :allocations
527+ return quote
528+ Experimental. @force_compile
529+ # Note this value is unused, but without it `allocated` and `allocations`
530+ # are sufficiently different that the compiler can remove allocations here
531+ # that it cannot remove there, giving inconsistent numbers.
532+ local b1 = Ref {Int64} (0 )
533+ local stats = Base. gc_num ()
534+ $ (esc (ex))
535+ local diff = Base. GC_Diff (Base. gc_num (), stats)
536+ gc_bytes (b1)
537+ Base. gc_alloc_count (diff)
538+ end
539+ end
540+ end
541+
504542"""
505543 @allocated
506544
507545A macro to evaluate an expression, discarding the resulting value, instead returning the
508546total number of bytes allocated during evaluation of the expression.
509547
548+ If the expression is a function call, an effort is made to measure only allocations from
549+ the argument expressions and during the function, excluding any overhead from calling it
550+ and not performing constant propagation with the provided argument values. If you want to
551+ include those effects, i.e. measuring the call site as well, use the syntax
552+ `@allocated (()->f(1))()`.
553+
554+ It is recommended to measure function calls with only simple argument expressions, e.g.
555+ `x = []; @allocated f(x)` instead of `@allocated f([])` to clarify that only `f` is
556+ being measured.
557+
558+ For more complex expressions, the code is simply run in place and therefore may see
559+ allocations due to the surrounding context. For example it is possible for
560+ `@allocated f(1)` and `@allocated x = f(1)` to give different results.
561+
510562See also [`@allocations`](@ref), [`@time`](@ref), [`@timev`](@ref), [`@timed`](@ref),
511563and [`@elapsed`](@ref).
512564
@@ -516,11 +568,7 @@ julia> @allocated rand(10^6)
516568```
517569"""
518570macro allocated (ex)
519- if ! is_simply_call (ex)
520- ex = :((() -> $ ex)())
521- end
522- pushfirst! (ex. args, GlobalRef (Base, :allocated ))
523- return esc (ex)
571+ _gen_allocation_measurer (ex, :allocated )
524572end
525573
526574"""
@@ -541,11 +589,7 @@ julia> @allocations rand(10^6)
541589 This macro was added in Julia 1.9.
542590"""
543591macro allocations (ex)
544- if ! is_simply_call (ex)
545- ex = :((() -> $ ex)())
546- end
547- pushfirst! (ex. args, GlobalRef (Base, :allocations ))
548- return esc (ex)
592+ _gen_allocation_measurer (ex, :allocations )
549593end
550594
551595
0 commit comments