Skip to content

Commit a47b58f

Browse files
authored
"improve" allocations macro with more functions (#58057)
Change the implementation of the allocations to always add a function call (closure) wrapper so that the inner code can be optimized, even if the outer scope is `@latestworld`. The implementation creates a closure if necessary (the code is complex) or just makes a call if the expression is simple (just a call on symbols). Many packages in the wild are observed to already be doing this themselves.
1 parent bdab032 commit a47b58f

File tree

3 files changed

+47
-22
lines changed

3 files changed

+47
-22
lines changed

base/timing.jl

+38-14
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,35 @@ function gc_bytes()
472472
b[]
473473
end
474474

475+
function allocated(f, args::Vararg{Any,N}) where {N}
476+
b0 = Ref{Int64}(0)
477+
b1 = Ref{Int64}(0)
478+
Base.gc_bytes(b0)
479+
f(args...)
480+
Base.gc_bytes(b1)
481+
return b1[] - b0[]
482+
end
483+
only(methods(allocated)).called = 0xff
484+
485+
function allocations(f, args::Vararg{Any,N}) where {N}
486+
stats = Base.gc_num()
487+
f(args...)
488+
diff = Base.GC_Diff(Base.gc_num(), stats)
489+
return Base.gc_alloc_count(diff)
490+
end
491+
only(methods(allocations)).called = 0xff
492+
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+
Base.is_self_quoting(a) && continue
499+
return false
500+
end
501+
return true
502+
end
503+
475504
"""
476505
@allocated
477506
@@ -487,15 +516,11 @@ julia> @allocated rand(10^6)
487516
```
488517
"""
489518
macro allocated(ex)
490-
quote
491-
Experimental.@force_compile
492-
local b0 = Ref{Int64}(0)
493-
local b1 = Ref{Int64}(0)
494-
gc_bytes(b0)
495-
$(esc(ex))
496-
gc_bytes(b1)
497-
b1[] - b0[]
519+
if !is_simply_call(ex)
520+
ex = :((() -> $ex)())
498521
end
522+
pushfirst!(ex.args, GlobalRef(Base, :allocated))
523+
return esc(ex)
499524
end
500525

501526
"""
@@ -516,15 +541,14 @@ julia> @allocations rand(10^6)
516541
This macro was added in Julia 1.9.
517542
"""
518543
macro allocations(ex)
519-
quote
520-
Experimental.@force_compile
521-
local stats = Base.gc_num()
522-
$(esc(ex))
523-
local diff = Base.GC_Diff(Base.gc_num(), stats)
524-
Base.gc_alloc_count(diff)
544+
if !is_simply_call(ex)
545+
ex = :((() -> $ex)())
525546
end
547+
pushfirst!(ex.args, GlobalRef(Base, :allocations))
548+
return esc(ex)
526549
end
527550

551+
528552
"""
529553
@lock_conflicts
530554

test/boundscheck_exec.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ if bc_opt == bc_default
350350
m1 === m2
351351
end
352352
no_alias_prove(1)
353-
@test_broken (@allocated no_alias_prove(5)) == 0
353+
@test (@allocated no_alias_prove(5)) == 0
354354
end
355355

356356
end

test/math.jl

+8-7
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ has_fma = Dict(
4646
@test clamp(100, Int8) === Int8(100)
4747
@test clamp(200, Int8) === typemax(Int8)
4848

49-
begin
50-
x = [0.0, 1.0, 2.0, 3.0, 4.0]
49+
let x = [0.0, 1.0, 2.0, 3.0, 4.0]
5150
clamp!(x, 1, 3)
5251
@test x == [1.0, 1.0, 2.0, 3.0, 3.0]
5352
end
@@ -59,12 +58,14 @@ has_fma = Dict(
5958
@test clamp(typemax(UInt16), Int16) === Int16(32767)
6059

6160
# clamp should not allocate a BigInt for typemax(Int16)
62-
x = big(2) ^ 100
63-
@test (@allocated clamp(x, Int16)) == 0
61+
let x = big(2) ^ 100
62+
@test (@allocated clamp(x, Int16)) == 0
63+
end
6464

65-
x = clamp(2.0, BigInt)
66-
@test x isa BigInt
67-
@test x == big(2)
65+
let x = clamp(2.0, BigInt)
66+
@test x isa BigInt
67+
@test x == big(2)
68+
end
6869
end
6970
end
7071

0 commit comments

Comments
 (0)