use*_*444 16 c++ string libstdc++ libc++
考虑以下测试程序:
#include <iostream>
#include <string>
#include <vector>
int main()
{
std::cout << sizeof(std::string("hi")) << " ";
std::string a[10];
std::cout << sizeof(a) << " ";
std::vector<std::string> v(10);
std::cout << sizeof(v) + sizeof(std::string) * v.capacity() << "\n";
}
Run Code Online (Sandbox Code Playgroud)
输出libstdc++和libc++分别是:
8 80 104
24 240 264
Run Code Online (Sandbox Code Playgroud)
如您所见,libc++对于一个简单的程序,需要3倍的内存.实现有何不同会导致这种内存差异?我需要关注,我该如何解决它?
How*_*ant 53
这是一个简短的程序,可以帮助您探索以下两种内存使用情况std::string:堆栈和堆.
#include <string>
#include <new>
#include <cstdio>
#include <cstdlib>
std::size_t allocated = 0;
void* operator new (size_t sz)
{
void* p = std::malloc(sz);
allocated += sz;
return p;
}
void operator delete(void* p) noexcept
{
return std::free(p);
}
int
main()
{
allocated = 0;
std::string s("hi");
std::printf("stack space = %zu, heap space = %zu, capacity = %zu\n",
sizeof(s), allocated, s.capacity());
}
Run Code Online (Sandbox Code Playgroud)
使用http://melpon.org/wandbox/可以很容易地获得不同编译器/库组合的输出,例如:
gcc 4.9.1:
stack space = 8, heap space = 27, capacity = 2
Run Code Online (Sandbox Code Playgroud)
gcc 5.0.0:
stack space = 32, heap space = 0, capacity = 15
Run Code Online (Sandbox Code Playgroud)
铛/的libc ++:
stack space = 24, heap space = 0, capacity = 22
Run Code Online (Sandbox Code Playgroud)
VS-2015:
stack space = 32, heap space = 0, capacity = 15
Run Code Online (Sandbox Code Playgroud)
(最后一行来自http://webcompiler.cloudapp.net)
上面的输出还显示capacity,它是char在必须从堆中分配新的更大缓冲区之前字符串可以容纳多少s的度量.对于gcc-5.0,libc ++和VS-2015实现,这是对短字符串缓冲区的度量.也就是说,在堆栈上分配的大小缓冲区用于保存短字符串,从而避免了更昂贵的堆分配.
似乎libc ++实现具有最短(堆栈使用)的短字符串实现,但是包含最大的短字符串缓冲区.如果计算总内存使用量(堆栈+堆),则libc ++在所有这4个实现中的这个2字符字符串的总内存使用量最小.
应该注意的是,所有这些测量都是在64位平台上进行的.在32位上,libc ++堆栈的使用量将降至12,小的字符串缓冲区将降至10.我不知道32位平台上其他实现的行为,但您可以使用上面的代码找出.
Jon*_*ely 10
你不应该担心,标准库实现者知道他们在做什么.
使用GCC subversion trunk libstdc ++中的最新代码给出了这些数字:
32 320 344
Run Code Online (Sandbox Code Playgroud)
这是因为几个星期前我将默认std::string实现切换为使用小字符串优化(空格为15个字符)而不是您测试的写时复制实现.
简介:它看起来只是libstdc++使用一个char*.实际上,它分配了更多的内存.
所以,你不应该担心Clang的libc++实现是内存效率低下的.
从libstdc ++的文档(详细说明):
A string looks like this:
[_Rep]
_M_length
[basic_string<char_type>] _M_capacity
_M_dataplus _M_refcount
_M_p ----------------> unnamed array of char_type
Run Code Online (Sandbox Code Playgroud)
_M_p指向字符串中的第一个字符,并将其转换为指向_Rep的指针,并减去1以获取指向标题的指针.
这种方法具有巨大的优势,即字符串对象只需要一次分配.所有的丑陋都局限在一对内联函数中,每个内联函数都编译为一个add指令:_Rep :: _ M_data()和string :: _ M_rep(); 获取原始字节块并具有足够空间的分配函数,并在前面构造一个_Rep对象.
您希望_M_data指向字符数组而不是_Rep的原因是调试器可以看到字符串内容.(可能我们应该添加一个非内联成员来获取调试器使用的_Rep,这样用户就可以检查实际的字符串长度.)
所以,它看起来只是一个char*但在内存使用方面有误导性.
以前libstdc++基本上使用这种布局:
struct _Rep_base
{
size_type _M_length;
size_type _M_capacity;
_Atomic_word _M_refcount;
};
Run Code Online (Sandbox Code Playgroud)
这更接近于结果libc++.
libc++使用"短字符串优化".确切的布局取决于是否_LIBCPP_ABI_ALTERNATE_STRING_LAYOUT定义.如果已定义,则如果字符串很短,则数据指针将是字对齐的.有关详细信息,请参阅源代码.
短字符串优化避免了堆分配,因此libstdc++如果您只考虑在堆栈上分配的部分,它看起来也比实现更昂贵.sizeof(std::string)仅显示堆栈使用情况而非整体内存使用情况(堆栈+堆).
我还没有检查源代码中的实际实现,但我记得在研究 C++ 字符串库时检查过这一点。24 字节字符串实现是典型的。如果字符串的长度小于或等于 16 字节,则不会从堆中进行 malloc,而是将字符串复制到大小为 16 字节的内部缓冲区中。否则,它会分配并存储内存地址等。这种较小的缓冲实际上有助于提高运行时性能。
对于某些编译器,可以选择关闭内部缓冲区。
| 归档时间: |
|
| 查看次数: |
9124 次 |
| 最近记录: |