Skip to content

Commit

Permalink
make allocated macro more reliable
Browse files Browse the repository at this point in the history
Changes testset to avoid compiler heuristics (copyast) that disables inference.
And changes the allocated macro to rely less on inference to elid allocations for the machinary itself.
  • Loading branch information
vtjnash committed Oct 30, 2019
1 parent 350548c commit b9546c8
Show file tree
Hide file tree
Showing 9 changed files with 37 additions and 43 deletions.
34 changes: 11 additions & 23 deletions base/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,6 @@ end
# total time spend in garbage collection, in nanoseconds
gc_time_ns() = ccall(:jl_gc_total_hrtime, UInt64, ())

# total number of bytes allocated so far
gc_bytes() = ccall(:jl_gc_total_bytes, Int64, ())

# print elapsed time, return expression value
const _mem_units = ["byte", "KiB", "MiB", "GiB", "TiB", "PiB"]
const _cnt_units = ["", " k", " M", " G", " T", " P"]
Expand Down Expand Up @@ -223,21 +220,14 @@ macro elapsed(ex)
end
end

# measure bytes allocated without *most* contamination from compilation
# Note: This reports a different value from the @time macros, because
# it wraps the call in a function, however, this means that things
# like: @allocated y = foo()
# will not work correctly, because it will set y in the context of
# the local function made by the macro, not the current function
# total number of bytes allocated so far
gc_bytes(b::Ref{Int64}) = ccall(:jl_gc_get_total_bytes, Cvoid, (Ptr{Int64},), b)

"""
@allocated
A macro to evaluate an expression, discarding the resulting value, instead returning the
total number of bytes allocated during evaluation of the expression. Note: the expression is
evaluated inside a local function, instead of the current context, in order to eliminate the
effects of compilation, however, there still may be some allocations due to JIT compilation.
This also makes the results inconsistent with the `@time` macros, which do not try to adjust
for the effects of compilation.
total number of bytes allocated during evaluation of the expression.
See also [`@time`](@ref), [`@timev`](@ref), [`@timed`](@ref),
and [`@elapsed`](@ref).
Expand All @@ -249,15 +239,13 @@ julia> @allocated rand(10^6)
"""
macro allocated(ex)
quote
let
local f
function f()
b0 = gc_bytes()
$(esc(ex))
gc_bytes() - b0
end
f()
end
while false; end # compiler heuristic: compile this block (alter this if the heuristic changes)
local b0 = Ref{Int64}(0)
local b1 = Ref{Int64}(0)
gc_bytes(b0)
$(esc(ex))
gc_bytes(b1)
b1[] - b0[]
end
end

Expand Down
17 changes: 13 additions & 4 deletions src/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2625,38 +2625,47 @@ JL_DLLEXPORT int jl_gc_enable(int on)
}
return prev;
}

JL_DLLEXPORT int jl_gc_is_enabled(void)
{
jl_ptls_t ptls = jl_get_ptls_states();
return !ptls->disable_gc;
}

JL_DLLEXPORT int64_t jl_gc_total_bytes(void)
JL_DLLEXPORT void jl_gc_get_total_bytes(int64_t *bytes)
{
jl_gc_num_t num = gc_num;
combine_thread_gc_counts(&num);
// Sync this logic with `base/util.jl:GC_Diff`
return (num.total_allocd + num.deferred_alloc + num.allocd);
*bytes = (num.total_allocd + num.deferred_alloc + num.allocd);
}

JL_DLLEXPORT uint64_t jl_gc_total_hrtime(void)
{
return gc_num.total_time;
}

JL_DLLEXPORT jl_gc_num_t jl_gc_num(void)
{
jl_gc_num_t num = gc_num;
combine_thread_gc_counts(&num);
return num;
}

// TODO: these were supposed to be thread local
JL_DLLEXPORT int64_t jl_gc_diff_total_bytes(void)
{
int64_t oldtb = last_gc_total_bytes;
int64_t newtb = jl_gc_total_bytes();
int64_t newtb;
jl_gc_get_total_bytes(&newtb);
last_gc_total_bytes = newtb;
return newtb - oldtb;
}
void jl_gc_sync_total_bytes(void) {last_gc_total_bytes = jl_gc_total_bytes();}

void jl_gc_sync_total_bytes(void)
{
jl_gc_get_total_bytes(&last_gc_total_bytes);
}

static void jl_gc_premark(jl_ptls_t ptls2)
{
Expand Down
3 changes: 0 additions & 3 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -749,9 +749,6 @@ extern void JL_GC_POP() JL_NOTSAFEPOINT;

JL_DLLEXPORT int jl_gc_enable(int on);
JL_DLLEXPORT int jl_gc_is_enabled(void);
JL_DLLEXPORT int64_t jl_gc_total_bytes(void);
JL_DLLEXPORT uint64_t jl_gc_total_hrtime(void);
JL_DLLEXPORT int64_t jl_gc_diff_total_bytes(void);

typedef enum {
JL_GC_AUTO = 0, // use heuristics to determine the collection type
Expand Down
1 change: 1 addition & 0 deletions src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ JL_DLLEXPORT void *jl_gc_counted_malloc(size_t sz);

JL_DLLEXPORT void JL_NORETURN jl_throw_out_of_memory_error(void);

JL_DLLEXPORT int64_t jl_gc_diff_total_bytes(void);
void jl_gc_sync_total_bytes(void);
void jl_gc_track_malloced_array(jl_ptls_t ptls, jl_array_t *a) JL_NOTSAFEPOINT;
void jl_gc_count_allocd(size_t sz) JL_NOTSAFEPOINT;
Expand Down
4 changes: 2 additions & 2 deletions stdlib/Test/src/Test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1109,7 +1109,7 @@ function testset_beginend(args, tests, source)
err isa InterruptException && rethrow()
# something in the test block threw an error. Count that as an
# error in this test set
record(ts, Error(:nontest_error, :(), err, Base.catch_stack(), $(QuoteNode(source))))
record(ts, Error(:nontest_error, Expr(:tuple), err, Base.catch_stack(), $(QuoteNode(source))))
finally
copy!(RNG, oldrng)
end
Expand Down Expand Up @@ -1182,7 +1182,7 @@ function testset_forloop(args, testloop, source)
err isa InterruptException && rethrow()
# Something in the test block threw an error. Count that as an
# error in this test set
record(ts, Error(:nontest_error, :(), err, Base.catch_stack(), $(QuoteNode(source))))
record(ts, Error(:nontest_error, Expr(:tuple), err, Base.catch_stack(), $(QuoteNode(source))))
end
end
quote
Expand Down
8 changes: 5 additions & 3 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5019,13 +5019,15 @@ end
# when calculating total allocation size.
@noinline function f17255(n)
GC.enable(false)
b0 = Base.gc_bytes()
b0 = Ref{Int64}(0)
b1 = Ref{Int64}(0)
Base.gc_bytes(b0)
local a
for i in 1:n
a, t, allocd = @timed [Ref(1) for i in 1:1000]
@test allocd > 0
b1 = Base.gc_bytes()
if b1 < b0
Base.gc_bytes(b1)
if b1[] < b0[]
return false, a
end
end
Expand Down
8 changes: 4 additions & 4 deletions test/offsetarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -533,15 +533,15 @@ end
B = OffsetArray(reshape(1:24, 4, 3, 2), -5, 6, -7)
for R in (fill(0, -4:-1), fill(0, -4:-1, 7:7), fill(0, -4:-1, 7:7, -6:-6))
@test @inferred(maximum!(R, B)) == reshape(maximum(B, dims=(2,3)), axes(R)) == reshape(21:24, axes(R))
@test @allocated(maximum!(R, B)) <= 400
@test @allocated(maximum!(R, B)) <= 800
@test @inferred(minimum!(R, B)) == reshape(minimum(B, dims=(2,3)), axes(R)) == reshape(1:4, axes(R))
@test @allocated(minimum!(R, B)) <= 400
@test @allocated(minimum!(R, B)) <= 800
end
for R in (fill(0, -4:-4, 7:9), fill(0, -4:-4, 7:9, -6:-6))
@test @inferred(maximum!(R, B)) == reshape(maximum(B, dims=(1,3)), axes(R)) == reshape(16:4:24, axes(R))
@test @allocated(maximum!(R, B)) <= 400
@test @allocated(maximum!(R, B)) <= 800
@test @inferred(minimum!(R, B)) == reshape(minimum(B, dims=(1,3)), axes(R)) == reshape(1:4:9, axes(R))
@test @allocated(minimum!(R, B)) <= 400
@test @allocated(minimum!(R, B)) <= 800
end
@test_throws DimensionMismatch maximum!(fill(0, -4:-1, 7:7, -6:-6, 1:1), B)
@test_throws DimensionMismatch minimum!(fill(0, -4:-1, 7:7, -6:-6, 1:1), B)
Expand Down
2 changes: 0 additions & 2 deletions test/ranges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1490,8 +1490,6 @@ end
end

@testset "allocation of TwicePrecision call" begin
0:286.493442:360
0:286:360
@test @allocated(0:286.493442:360) == 0
@test @allocated(0:286:360) == 0
end
Expand Down
3 changes: 1 addition & 2 deletions test/syntax.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1845,8 +1845,7 @@ end
@testset "closure conversion in testsets" begin
p = (2, 3, 4)
@test p == (2, 3, 4)
identity(p)
allocs = @allocated identity(p)
allocs = (() -> @allocated identity(p))()
@test allocs == 0
end

Expand Down

0 comments on commit b9546c8

Please sign in to comment.