@@ -472,41 +472,47 @@ 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+ val = f (args... )
480480 Base. gc_bytes (b1)
481+ donotdelete (val)
481482 return b1[] - b0[]
482483end
483484only (methods (allocated)). called = 0xff
484485
485- function allocations (f, args:: Vararg{Any,N} ) where {N}
486+ @constprop :none function allocations (f, args:: Vararg{Any,N} ) where {N}
487+ # Note this value is unused, but without it `allocated` and `allocations`
488+ # are sufficiently different that the compiler can remove allocations here
489+ # that it cannot remove there, giving inconsistent numbers.
490+ b1 = Ref {Int64} (0 )
486491 stats = Base. gc_num ()
487- f (args... )
492+ val = f (args... )
488493 diff = Base. GC_Diff (Base. gc_num (), stats)
494+ gc_bytes (b1)
495+ donotdelete (val)
489496 return Base. gc_alloc_count (diff)
490497end
491498only (methods (allocations)). called = 0xff
492499
493- function is_simply_call (@nospecialize ex)
494- Meta. isexpr (ex, :call ) || return false
495- for a in ex. args
496- a isa QuoteNode && continue
497- a isa Symbol && continue
498- isa_ast_node (a) || continue
499- return false
500- end
501- return true
502- end
503-
504500"""
505501 @allocated
506502
507503A macro to evaluate an expression, discarding the resulting value, instead returning the
508504total number of bytes allocated during evaluation of the expression.
509505
506+ If the expression is a function call, an effort is made to measure only allocations
507+ during the function, excluding any overhead from calling it and not performing
508+ constant propagation with the provided argument values. This mode of use is recommended.
509+ If you want to include those effects, i.e. measuring the call site as well,
510+ use the syntax `@allocated (()->f(1))()`.
511+
512+ For more complex expressions, the code is simply run in place and therefore may see
513+ allocations due to the surrounding context. For example it is possible for
514+ `@allocated f(1)` and `@allocated x = f(1)` to give different results.
515+
510516See also [`@allocations`](@ref), [`@time`](@ref), [`@timev`](@ref), [`@timed`](@ref),
511517and [`@elapsed`](@ref).
512518
@@ -516,11 +522,46 @@ julia> @allocated rand(10^6)
516522```
517523"""
518524macro allocated (ex)
519- if ! is_simply_call (ex)
520- ex = :((() -> $ ex)())
525+ quote
526+ Experimental. @force_compile
527+ local b0 = Ref {Int64} (0 )
528+ local b1 = Ref {Int64} (0 )
529+ gc_bytes (b0)
530+ local val = $ (esc (ex))
531+ gc_bytes (b1)
532+ donotdelete (val)
533+ b1[] - b0[]
534+ end
535+ end
536+
537+ """
538+ @allocated inside f(...)
539+
540+ Return the number of bytes allocated during the execution of a given function,
541+ excluding any allocations in the argument expressions or due to the call itself.
542+
543+ ```julia-repl
544+ julia> @allocated identity([])
545+ 32
546+
547+ julia> @allocated inside identity([])
548+ 0
549+ ```
550+
551+ !!! compat "Julia 1.12"
552+ This macro was added in Julia 1.12.
553+ """
554+ macro allocated (how, ex)
555+ if how === :inside
556+ if isexpr (ex, :call )
557+ pushfirst! (ex. args, GlobalRef (Base, :allocated ))
558+ return esc (ex)
559+ else
560+ error (" `@allocated inside` requires a call expression" )
561+ end
562+ else
563+ error (" Unrecognized symbol argument to `@allocated`; must be `inside`" )
521564 end
522- pushfirst! (ex. args, GlobalRef (Base, :allocated ))
523- return esc (ex)
524565end
525566
526567"""
@@ -541,11 +582,46 @@ julia> @allocations rand(10^6)
541582 This macro was added in Julia 1.9.
542583"""
543584macro allocations (ex)
544- if ! is_simply_call (ex)
545- ex = :((() -> $ ex)())
585+ quote
586+ Experimental. @force_compile
587+ local b1 = Ref {Int64} (0 ) # See note above in `allocations`
588+ local stats = Base. gc_num ()
589+ local val = $ (esc (ex))
590+ local diff = Base. GC_Diff (Base. gc_num (), stats)
591+ gc_bytes (b1)
592+ donotdelete (val)
593+ Base. gc_alloc_count (diff)
594+ end
595+ end
596+
597+ """
598+ @allocations inside f(...)
599+
600+ Return the number of allocations during the execution of a given function,
601+ excluding any allocations in the argument expressions or due to the call itself.
602+
603+ ```julia-repl
604+ julia> @allocations identity([])
605+ 1
606+
607+ julia> @allocations inside identity([])
608+ 0
609+ ```
610+
611+ !!! compat "Julia 1.12"
612+ This macro was added in Julia 1.12.
613+ """
614+ macro allocations (how, ex)
615+ if how === :inside
616+ if isexpr (ex, :call )
617+ pushfirst! (ex. args, GlobalRef (Base, :allocations ))
618+ return esc (ex)
619+ else
620+ error (" `@allocations inside` requires a call expression" )
621+ end
622+ else
623+ error (" Unrecognized symbol argument to `@allocations`; must be `inside`" )
546624 end
547- pushfirst! (ex. args, GlobalRef (Base, :allocations ))
548- return esc (ex)
549625end
550626
551627
0 commit comments