每次我尝试打印时,boost::multiprecision::cpp_int 都会被复制然后删除
boost::multiprecision::cpp_int is being copied then deleted every time I try and print it
当我从 Boost 打印 cpp_int
时,似乎复制了整个对象。
#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
令人困惑的是,用于打印的重载是 ostream& operator<<(ostream&, const T&)
,但是将 *u
传递给 template <typename T> void cr(const T&) {}
等函数不会显示任何新的内存分配。我也试过 u->str()
但这也会导致第二次内存分配。
我还尝试为 cpp_int
:
重载 cout
std::ostream& operator <<(std::ostream& stream, const boost::multiprecision::cpp_int& mpi) {
return stream << mpi.str();
}
但结果是一样的。但是,我也很惊讶这个编译,因为我预计已经过载了。我的假设是我可能需要修改更多后端内容。
我怎样才能避免这种情况?我不想每次打印 cpp_int
.
时都复制然后删除 30 多个字节
如果不是,切换数据类型不是不可能的,只要接口相似以进行最小重构。
你不匹配的方式 malloc/new 是调用 UB(正如 ubsan+asan 会很容易告诉你的那样)。
==32752==ERROR: AddressSanitizer: alloc-dealloc-mismatch (malloc vs operator delete
#0 0x7fb58c15c407 in operator delete(void*, unsigned long) (/usr/lib/x86_64-lin
#1 0x564b19759014 in __gnu_cxx::new_allocator<char>::deallocate(char*, unsigned
#2 0x564b1974b8cb in std::allocator<char>::deallocate(char*, unsigned long) /us
#3 0x564b1974b8cb in std::allocator_traits<std::allocator<char> >::deallocate(s
#4 0x564b197478f4 in std::__cxx11::basic_string<char, std::char_traits<char>, s
#5 0x564b19744f74 in std::__cxx11::basic_string<char, std::char_traits<char>, s
#6 0x564b19741053 in std::__cxx11::basic_string<char, std::char_traits<char>, s
#7 0x564b19744993 in std::ostream& boost::multiprecision::operator<< <boost::mu
#8 0x564b1973da5a in main /home/sehe/Projects/Whosebug/test.cpp:14
#9 0x7fb58ab85bf6 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21bf6
#10 0x564b1973d669 in _start (/home/sehe/Projects/Whosebug/sotest+0x4a669)
所以让我们把重点放在声明上:
#include <boost/multiprecision/cpp_int.hpp>
#include <iostream>
using Int = boost::multiprecision::cpp_int;
int main() {
Int u("987654321");
std::cout << u << "\n";
}
当我们要求 clang 跟随 operator<<
的重载时,它引导我们到这里:
template <class Backend, expression_template_option ExpressionTemplates>
inline std::ostream& operator<<(std::ostream& os, const number<Backend, ExpressionTemplates>& r)
{
std::streamsize d = os.precision();
std::string s = r.str(d, os.flags());
std::streamsize ss = os.width();
if (ss > static_cast<std::streamsize>(s.size()))
{
char fill = os.fill();
if ((os.flags() & std::ios_base::left) == std::ios_base::left)
s.append(static_cast<std::string::size_type>(ss - s.size()), fill);
else
s.insert(static_cast<std::string::size_type>(0), static_cast<std::string::size_type>(ss - s.size()), fill);
}
return os << s;
}
正如你所看到的,这个数字是由const-reference获取的,所以没有执行复制。将分配缓冲区(在 str()
实现中)。我认为 Multiprecision 库不会吹嘘高度优化的 IO 操作实现。
内存分析
为了准确了解在何处进行了哪些分配,我 运行 通过 Massif 进行了调试构建:
在高峰期,最高分配是:
99.97% (73,759B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->98.54% (72,704B) 0x50DDFA4: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28
| ->98.54% (72,704B) 0x40108F1: _dl_init (dl-init.c:72)
| ->98.54% (72,704B) 0x40010C8: ??? (in /lib/x86_64-linux-gnu/ld-2.27.so)
|
->01.39% (1,024B) 0x58C526A: _IO_file_doallocate (filedoalloc.c:101)
| ->01.39% (1,024B) 0x58D5447: _IO_doallocbuf (genops.c:365)
| ->01.39% (1,024B) 0x58D4566: _IO_file_overflow@@GLIBC_2.2.5 (fileops.c:759)
| ->01.39% (1,024B) 0x58D2ABB: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1266)
| ->01.39% (1,024B) 0x58C6A55: fwrite (iofwrite.c:39)
| ->01.39% (1,024B) 0x516675A: std::basic_ostream<char, std::char_traits<ch
| ->01.39% (1,024B) 0x10C85C: std::ostream& boost::multiprecision::operat
| ->01.39% (1,024B) 0x10B6C6: main (test.cpp:8)
|
->00.04% (31B) in 1 place, below massif's threshold (1.00%)
遗憾的是我无法使阈值 < 1%(这可能是记录的限制)。
我们可以看到的是,虽然 31B 分配发生在某处,但它与文件输出缓冲区 (1024B) 相比相形见绌。
如果我们将输出语句替换为
return u.str().length();
您仍然可以看到 31B 分配,它与 cpp_int 类型的大小不匹配。事实上,如果我们要复制那个:
return std::make_unique<Int>(u)? 0 : 1;
然后我们看到的是 32B 分配:
->00.04% (32B) in 1 place, below massif's threshold (1.00%)
很明显 cpp_int 没有被复制,这是有道理的。
当我从 Boost 打印 cpp_int
时,似乎复制了整个对象。
#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
令人困惑的是,用于打印的重载是 ostream& operator<<(ostream&, const T&)
,但是将 *u
传递给 template <typename T> void cr(const T&) {}
等函数不会显示任何新的内存分配。我也试过 u->str()
但这也会导致第二次内存分配。
我还尝试为 cpp_int
:
std::ostream& operator <<(std::ostream& stream, const boost::multiprecision::cpp_int& mpi) {
return stream << mpi.str();
}
但结果是一样的。但是,我也很惊讶这个编译,因为我预计已经过载了。我的假设是我可能需要修改更多后端内容。
我怎样才能避免这种情况?我不想每次打印 cpp_int
.
如果不是,切换数据类型不是不可能的,只要接口相似以进行最小重构。
你不匹配的方式 malloc/new 是调用 UB(正如 ubsan+asan 会很容易告诉你的那样)。
==32752==ERROR: AddressSanitizer: alloc-dealloc-mismatch (malloc vs operator delete
#0 0x7fb58c15c407 in operator delete(void*, unsigned long) (/usr/lib/x86_64-lin
#1 0x564b19759014 in __gnu_cxx::new_allocator<char>::deallocate(char*, unsigned
#2 0x564b1974b8cb in std::allocator<char>::deallocate(char*, unsigned long) /us
#3 0x564b1974b8cb in std::allocator_traits<std::allocator<char> >::deallocate(s
#4 0x564b197478f4 in std::__cxx11::basic_string<char, std::char_traits<char>, s
#5 0x564b19744f74 in std::__cxx11::basic_string<char, std::char_traits<char>, s
#6 0x564b19741053 in std::__cxx11::basic_string<char, std::char_traits<char>, s
#7 0x564b19744993 in std::ostream& boost::multiprecision::operator<< <boost::mu
#8 0x564b1973da5a in main /home/sehe/Projects/Whosebug/test.cpp:14
#9 0x7fb58ab85bf6 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21bf6
#10 0x564b1973d669 in _start (/home/sehe/Projects/Whosebug/sotest+0x4a669)
所以让我们把重点放在声明上:
#include <boost/multiprecision/cpp_int.hpp>
#include <iostream>
using Int = boost::multiprecision::cpp_int;
int main() {
Int u("987654321");
std::cout << u << "\n";
}
当我们要求 clang 跟随 operator<<
的重载时,它引导我们到这里:
template <class Backend, expression_template_option ExpressionTemplates>
inline std::ostream& operator<<(std::ostream& os, const number<Backend, ExpressionTemplates>& r)
{
std::streamsize d = os.precision();
std::string s = r.str(d, os.flags());
std::streamsize ss = os.width();
if (ss > static_cast<std::streamsize>(s.size()))
{
char fill = os.fill();
if ((os.flags() & std::ios_base::left) == std::ios_base::left)
s.append(static_cast<std::string::size_type>(ss - s.size()), fill);
else
s.insert(static_cast<std::string::size_type>(0), static_cast<std::string::size_type>(ss - s.size()), fill);
}
return os << s;
}
正如你所看到的,这个数字是由const-reference获取的,所以没有执行复制。将分配缓冲区(在 str()
实现中)。我认为 Multiprecision 库不会吹嘘高度优化的 IO 操作实现。
内存分析
为了准确了解在何处进行了哪些分配,我 运行 通过 Massif 进行了调试构建:
在高峰期,最高分配是:
99.97% (73,759B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->98.54% (72,704B) 0x50DDFA4: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28
| ->98.54% (72,704B) 0x40108F1: _dl_init (dl-init.c:72)
| ->98.54% (72,704B) 0x40010C8: ??? (in /lib/x86_64-linux-gnu/ld-2.27.so)
|
->01.39% (1,024B) 0x58C526A: _IO_file_doallocate (filedoalloc.c:101)
| ->01.39% (1,024B) 0x58D5447: _IO_doallocbuf (genops.c:365)
| ->01.39% (1,024B) 0x58D4566: _IO_file_overflow@@GLIBC_2.2.5 (fileops.c:759)
| ->01.39% (1,024B) 0x58D2ABB: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1266)
| ->01.39% (1,024B) 0x58C6A55: fwrite (iofwrite.c:39)
| ->01.39% (1,024B) 0x516675A: std::basic_ostream<char, std::char_traits<ch
| ->01.39% (1,024B) 0x10C85C: std::ostream& boost::multiprecision::operat
| ->01.39% (1,024B) 0x10B6C6: main (test.cpp:8)
|
->00.04% (31B) in 1 place, below massif's threshold (1.00%)
遗憾的是我无法使阈值 < 1%(这可能是记录的限制)。
我们可以看到的是,虽然 31B 分配发生在某处,但它与文件输出缓冲区 (1024B) 相比相形见绌。
如果我们将输出语句替换为
return u.str().length();
您仍然可以看到 31B 分配,它与 cpp_int 类型的大小不匹配。事实上,如果我们要复制那个:
return std::make_unique<Int>(u)? 0 : 1;
然后我们看到的是 32B 分配:
->00.04% (32B) in 1 place, below massif's threshold (1.00%)
很明显 cpp_int 没有被复制,这是有道理的。