Skip to content

Commit eba7627

Browse files
JeffBezansonxlxs4
andcommitted
fix double-counting and non-deterministic results in summarysize
fixes #53061 Co-authored-by: Orestis Ousoultzoglou <[email protected]>
1 parent 196dec6 commit eba7627

File tree

4 files changed

+87
-12
lines changed

4 files changed

+87
-12
lines changed

base/reflection.jl

+12-3
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,17 @@ function datatype_nfields(dt::DataType)
529529
return unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).nfields
530530
end
531531

532+
"""
533+
Base.datatype_npointers(dt::DataType) -> Int
534+
535+
Return the number of pointers in the layout of a datatype.
536+
"""
537+
function datatype_npointers(dt::DataType)
538+
@_foldable_meta
539+
dt.layout == C_NULL && throw(UndefRefError())
540+
return unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).npointers
541+
end
542+
532543
"""
533544
Base.datatype_pointerfree(dt::DataType) -> Bool
534545
@@ -537,9 +548,7 @@ Can be called on any `isconcretetype`.
537548
"""
538549
function datatype_pointerfree(dt::DataType)
539550
@_foldable_meta
540-
dt.layout == C_NULL && throw(UndefRefError())
541-
npointers = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).npointers
542-
return npointers == 0
551+
return datatype_npointers(dt) == 0
543552
end
544553

545554
"""

base/summarysize.jl

+25-9
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

+33
Original file line numberDiff line numberDiff line change
@@ -2253,6 +2253,39 @@ JL_DLLEXPORT size_t jl_get_field_offset(jl_datatype_t *ty, int field)
22532253
return jl_field_offset(ty, field - 1);
22542254
}
22552255

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

test/misc.jl

+17
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,23 @@ end
573573
# issue #44780
574574
@test summarysize(BigInt(2)^1000) > summarysize(BigInt(2))
575575

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

578595
# empty arrays

0 commit comments

Comments
 (0)