When I print a cpp_int
from Boost, it seems that the entire object is copied.
#include <iostream>
#include <boost/multiprecision/cpp_int.hpp>
using std::cout;
void* operator new(size_t size) {
void *memory = malloc(size);
cout << "New: " << memory << " " << size << "\n";
return memory;
}
int main() {
auto u = new boost::multiprecision::cpp_int("987654321");
cout << "------\n";
cout << *u << "\n";
}
New: 0x23d4e70 32
------
New: 0x23d52b0 31
987654321
The confusing thing is that the overload for printing is ostream& operator<<(ostream&, const T&)
, but passing *u
into a function such as template <typename T> void cr(const T&) {}
does not show any new memory allocation. I also tried u->str()
but this also causes a 2nd memory allocation.
I also tried overloading the cout for the cpp_int
:
std::ostream& operator <<(std::ostream& stream, const boost::multiprecision::cpp_int& mpi) {
return stream << mpi.str();
}
but the result was the same. However, I am also surprised that this compiled since I had expected there to already be an overload. My assumption is that I may need to modify something more back-end.
How can I avoid this? I don't want to be copying and then deleting 30+ bytes every time I want to print a cpp_int
.
If not, switching data type it not out of the question, as long as the interface is similar for minimal refactoring.
The way you mismatched malloc/new is invoking UB (as ubsan+asan will readily tell you).
So let's focus on the claim:
When we ask clang to follow the overload for
operator<<
it leads us here:As you can see the number is taken by const-reference, so no copy is performed. There will be allocations for the buffers (in the
str()
implementation). I don't think the Multiprecision library is going to boast a highly optimized implementation of IO operations.Memory Profiling
To see exactly what allocations are done where, I ran a debug build through Massif:
At the peak, the top allocations are:
Sadly somehow I cannot make the threshold < 1% (this might be a documented limit).
What we can see is that though the 31B allocation happens somewhere, it is dwarfed by the file output buffer (1024B).
If we replace the output statement by just
you can still witness the 31B allocation, which does NOT match the size of the cpp_int type. Indeed, iff we were to copy THAT:
THEN we see a 32B allocation instead:
It is pretty clear that the cpp_int is not being copied, as makes sense.