Skip to content

Commit 2dec97f

Browse files
JeffBezansonxlxs4
authored and
KristofferC
committed
fix double-counting and non-deterministic results in summarysize (#54606)
fixes #53061 Co-authored-by: Orestis Ousoultzoglou <orousoultzoglou@gmail.com> (cherry picked from commit 68fe512)
1 parent e6dd695 commit 2dec97f

File tree

4 files changed

+87
-12
lines changed

4 files changed

+87
-12
lines changed

base/reflection.jl

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,17 @@ function datatype_nfields(dt::DataType)
510510
return unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).nfields
511511
end
512512

513+
"""
514+
Base.datatype_npointers(dt::DataType) -> Int
515+
516+
Return the number of pointers in the layout of a datatype.
517+
"""
518+
function datatype_npointers(dt::DataType)
519+
@_foldable_meta
520+
dt.layout == C_NULL && throw(UndefRefError())
521+
return unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).npointers
522+
end
523+
513524
"""
514525
Base.datatype_pointerfree(dt::DataType) -> Bool
515526
@@ -518,9 +529,7 @@ Can be called on any `isconcretetype`.
518529
"""
519530
function datatype_pointerfree(dt::DataType)
520531
@_foldable_meta
521-
dt.layout == C_NULL && throw(UndefRefError())
522-
npointers = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).npointers
523-
return npointers == 0
532+
return datatype_npointers(dt) == 0
524533
end
525534

526535
"""

base/summarysize.jl

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ struct SummarySize
88
chargeall::Any
99
end
1010

11+
nth_pointer_isdefined(obj, i::Int) = ccall(:jl_nth_pointer_isdefined, Cint, (Any, Csize_t), obj, i-1) != 0
12+
get_nth_pointer(obj, i::Int) = ccall(:jl_get_nth_pointer, Any, (Any, Csize_t), obj, i-1)
13+
1114
"""
1215
Base.summarysize(obj; exclude=Union{...}, chargeall=Union{...}) -> Int
1316
@@ -26,7 +29,7 @@ julia> Base.summarysize(1.0)
2629
8
2730
2831
julia> Base.summarysize(Ref(rand(100)))
29-
864
32+
848
3033
3134
julia> sizeof(Ref(rand(100)))
3235
8
@@ -50,15 +53,28 @@ function summarysize(obj;
5053
val = x[i]
5154
end
5255
elseif isa(x, GenericMemory)
53-
nf = length(x)
54-
if @inbounds @inline isassigned(x, i)
55-
val = x[i]
56+
T = eltype(x)
57+
if Base.allocatedinline(T)
58+
np = datatype_npointers(T)
59+
nf = length(x) * np
60+
idx = (i-1) ÷ np + 1
61+
if @inbounds @inline isassigned(x, idx)
62+
elt = x[idx]
63+
p = (i-1) % np + 1
64+
if nth_pointer_isdefined(elt, p)
65+
val = get_nth_pointer(elt, p)
66+
end
67+
end
68+
else
69+
nf = length(x)
70+
if @inbounds @inline isassigned(x, i)
71+
val = x[i]
72+
end
5673
end
5774
else
58-
nf = nfields(x)
59-
ft = typeof(x).types
60-
if !isbitstype(ft[i]) && isdefined(x, i)
61-
val = getfield(x, i)
75+
nf = datatype_npointers(typeof(x))
76+
if nth_pointer_isdefined(x, i)
77+
val = get_nth_pointer(x, i)
6278
end
6379
end
6480
if nf > i
@@ -82,7 +98,7 @@ end
8298
# and so is somewhat approximate.
8399
key = ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), obj)
84100
haskey(ss.seen, key) ? (return 0) : (ss.seen[key] = true)
85-
if nfields(obj) > 0
101+
if datatype_npointers(typeof(obj)) > 0
86102
push!(ss.frontier_x, obj)
87103
push!(ss.frontier_i, 1)
88104
end

src/datatype.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2246,6 +2246,39 @@ JL_DLLEXPORT size_t jl_get_field_offset(jl_datatype_t *ty, int field)
22462246
return jl_field_offset(ty, field - 1);
22472247
}
22482248

2249+
jl_value_t *get_nth_pointer(jl_value_t *v, size_t i)
2250+
{
2251+
jl_datatype_t *dt = (jl_datatype_t*)jl_typeof(v);
2252+
const jl_datatype_layout_t *ly = dt->layout;
2253+
uint32_t npointers = ly->npointers;
2254+
if (i >= npointers)
2255+
jl_bounds_error_int(v, i);
2256+
const uint8_t *ptrs8 = (const uint8_t *)jl_dt_layout_ptrs(ly);
2257+
const uint16_t *ptrs16 = (const uint16_t *)jl_dt_layout_ptrs(ly);
2258+
const uint32_t *ptrs32 = (const uint32_t*)jl_dt_layout_ptrs(ly);
2259+
uint32_t fld;
2260+
if (ly->flags.fielddesc_type == 0)
2261+
fld = ptrs8[i];
2262+
else if (ly->flags.fielddesc_type == 1)
2263+
fld = ptrs16[i];
2264+
else
2265+
fld = ptrs32[i];
2266+
return jl_atomic_load_relaxed((_Atomic(jl_value_t*)*)(&((jl_value_t**)v)[fld]));
2267+
}
2268+
2269+
JL_DLLEXPORT jl_value_t *jl_get_nth_pointer(jl_value_t *v, size_t i)
2270+
{
2271+
jl_value_t *ptrf = get_nth_pointer(v, i);
2272+
if (__unlikely(ptrf == NULL))
2273+
jl_throw(jl_undefref_exception);
2274+
return ptrf;
2275+
}
2276+
2277+
JL_DLLEXPORT int jl_nth_pointer_isdefined(jl_value_t *v, size_t i)
2278+
{
2279+
return get_nth_pointer(v, i) != NULL;
2280+
}
2281+
22492282
#ifdef __cplusplus
22502283
}
22512284
#endif

test/misc.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,23 @@ end
571571
# issue #44780
572572
@test summarysize(BigInt(2)^1000) > summarysize(BigInt(2))
573573

574+
# issue #53061
575+
mutable struct S53061
576+
x::Union{Float64, Tuple{Float64, Float64}}
577+
y::Union{Float64, Tuple{Float64, Float64}}
578+
end
579+
let s = S53061[S53061(rand(), (rand(),rand())) for _ in 1:10^4]
580+
@test allequal(summarysize(s) for i in 1:10)
581+
end
582+
struct Z53061
583+
x::S53061
584+
y::Int64
585+
end
586+
let z = Z53061[Z53061(S53061(rand(), (rand(),rand())), 0) for _ in 1:10^4]
587+
@test allequal(summarysize(z) for i in 1:10)
588+
@test abs(summarysize(z) - 640000)/640000 <= 0.01
589+
end
590+
574591
## test conversion from UTF-8 to UTF-16 (for Windows APIs)
575592

576593
# empty arrays

0 commit comments

Comments
 (0)