Why does each BigFloat allocate a big String in addition to the MPFR big float object?

I’m doing allocation profiling on some code that uses BigFloats at precision 2048, and I noticed that each time a BigFloat is allocated, half of the allocation seems to go to the MPFR object, and the other half to a String.

The relevant code in base/mpfr.jl calls this String a “gc handle”. I don’t know why this is necessary, but surely these String objects should take up much less than half space allocated for the Bigfloat?

The initial part of the BigFloat definition:

The BigFloat constructor that creates the String object:

Now that I think about the code, I guess the intent behind it is for the String object to hold the memory of the MPFR object, so there shouldn’t be any additional allocation? So I guess this is a bug in the allocation profiler?

Here’s a screenshot of the Julia allocation profiler in the Code editor:

Yes, there is no additional allocation (your interpretation of the String object is correct).

Why do you think there is a Bug in the allocation profiler?

1 Like

Because two equal allocations are noted, both in the flamegraph and in the annotated source, instead of just one.

The BigFloat object is allocated and the String is allocated. Why should there be one?

Before we exit the BigFloat constructor, there are already two allocations, as shown in both the flamegraph and the annotated source.

EDIT: I’m talking about this, specifically:

Within the constructor, there are two equal allocations shown, and then the total is their sum. I guess this causes the profiler to double-count the BigFloat size?

Yes, because the BigFloat object is instantiated/allocated in the call to _BigFloat.

If you look at the allocation profile, you will find there are two kinds of allocations; the string and the BigFloat (although the profiler can’t figure out the type here and calls it UnknownType). As you can see, they are of different size. Play with precision and observe the string allocation change size. It isn’t doubly allocated though.

Profile.Allocs.@profile for i in 1:10000000 BigFloat(;precision=2048) end
result = Profile.Allocs.fetch()
result = sort(result.allocs, by=x->x.size)
result[[begin,end]]

2-element Vector{Profile.Allocs.Alloc}:
 Profile.Allocs.Alloc(Profile.Allocs.UnknownType, Base.StackTraces.StackFrame[ijl_gc_pool_alloc at libjulia-internal.1.8.dylib:?, ...], 48)
 Profile.Allocs.Alloc(String, Base.StackTraces.StackFrame[ijl_alloc_string at libjulia-internal.1.8.dylib:?, ...], 256)
1 Like

Ah, I thought the profiler was reporting allocation size, but I guess it’s only considering the number of allocations. The Code editor Julia plugin, I mean.