【问题标题】:std::string implementation in GCC and its memory overhead for short stringsGCC 中的 std::string 实现及其短字符串的内存开销
【发布时间】:2011-06-30 19:54:15
【问题描述】:

我目前正在为低内存平台开发一个应用程序,该应用程序需要一个包含许多短字符串的 std::set(>100,000 个字符串,每个字符串包含 4-16 个字符)。我最近将此集合从 std::string 转换为 const char * 以节省内存,我想知道我是否真的避免了每个字符串的所有开销。

我尝试使用以下方法:

std::string sizeTest = "testString";
std::cout << sizeof(sizeTest) << " bytes";

但它只是给了我一个 4 个字节的输出,表明该字符串包含一个指针。我很清楚字符串在内部将它们的数据存储在 char * 中,但我认为字符串类会产生额外的开销。

std::string 的 GCC 实现是否比 sizeof(std::string) 所显示的开销更大?更重要的是,它在这种规模的数据集上是否重要?

这是我平台上相关类型的大小(它是 32 位,每个字节有 8 位):

字符:1 个字节
无效 *:4 个字节
字符 *:4 个字节
std::string: 4 字节

【问题讨论】:

  • 你在什么平台上得到这个结果?我问是因为我见过一个字节被认为是 16 位的平台(Texas Instruments DSP)。
  • sizeof(char*) 在您的平台上的结果是什么?
  • 我猜std::string 包含一个指向动态分配的字符串实现对象的指针。请记住,GCC 的字符串是 COW(写入时复制)。我尝试查看 gcc 的 basic_string.h 以获得一些见解,但尝试阅读神秘代码时感到头疼。
  • @Emile Cormier:字节中的位数由 CHAR_BITS 定义
  • 字符串是不可变的吗?如果是这样,那么您可以节省空间。但是,如果它们需要被操纵,那么由于您引入的额外复杂性,您不太可能保存任何东西。

标签: c++ string compiler-construction memory-management


【解决方案1】:

如果保证“>100,000 个字符串,每个字符串包含 4-16 个字符”,则不要使用 std::string。相反,编写您自己的 ShortString 类。有趣的是“sizeof(std::string) == 4”,这怎么可能? sizeof(char) 和 sizeof(void *) 是什么?

【讨论】:

  • 总是可能。它可以只保存一个指向某种内部字符串结构的指针,该结构保存所有常用数据,如长度和指向字符串数据的指针。
  • sizeof(char) 始终为 1。
  • @jalf 如果它只是持有一个指向内部结构的指针,那么当字符串长于 sizeof(void *)-1 时,实际内存成本会由 sizeof(void *) 添加。我可以想象当大小很小时,会针对 std::string 进行一些基于指针编码的大小优化。
  • @albert:我不明白你的意思。但是要获得sizeof(std::string) == 4,您只需要在一个 32 位系统上(其中指针为 4 字节宽),然后存储一个指针,并且在字符串类中没有其他成员。该指针可以仅引用包含长度/容量的辅助数据结构和指向实际字符串缓冲区的指针
【解决方案2】:

我猜你是在一个 32 位、每字节 8 位的平台上。我还要猜测,至少在您使用的 gcc 版本上,他们正在使用 std::string 的引用计数实现。您看到的 4 字节大小是指向包含引用计数和字符串数据(以及任何分配器状态,如果适用)的结构的指针。

在 gcc 的这种设计中,唯一的“短”字符串的大小 == 0,在这种情况下,它可以与所有其他空字符串共享一个表示。否则你会得到一个引用的 COW 字符串。

为了自己调查这个问题,编写一个分配器来跟踪它分配和释放多少内存以及多少次。使用此分配器来调查您感兴趣的容器的实现。

【讨论】:

    【解决方案3】:

    嗯,至少在 GCC 4.4.5 中,这是我在这方面得心应手的 机器,std::stringstd::basic_string&lt;char&gt; 的类型定义,并且 basic_string 定义在 /usr/include/c++/4.4.5/bits/basic_string.h。有很多 该文件中的间接性,但归结为非空 std::strings 存储指向其中之一的指针:

      struct _Rep_base
      {
    size_type       _M_length;
    size_type       _M_capacity;
    _Atomic_word        _M_refcount;
      };
    

    在内存中之后是实际的字符串数据。所以std::string 是 每个字符串至少有三个词的开销,加上 capacity 高于 `length 的任何开销(可能 不是,取决于你如何构造你的字符串——你可以检查 询问capacity() 方法)。

    内存分配器也会产生开销 大量小额分配;我不知道 GCC 用于 C++,但是 假设它类似于它用于 C 的 dlmalloc 分配器,那 每个分配可能至少两个字,加上一些对齐空间 大小为至少 8 个字节的倍数。

    【讨论】:

    • 许多实现使用小缓冲区优化不为少于 3-7 个字符的字符串进行分配,GCC 不这样做吗?
    • 我刚刚检查了微软的实现,它允许最多 15 个chars(加上 NULL)的字符串没有分配。
    • gcc 使用不合格的牛字符串。当他们可以进行破坏性的 abi 更改时,他们会切换到 sso...Herb 经常提到它...
    【解决方案4】:

    我已经对 std::string 开销进行了一些比较。一般来说,它大约是 48 字节!看看我博客上的文章: http://jovislab.com/blog/?p=76

    【讨论】:

    • 48 字节?哪个std::string? Microsoft 的字符串和 GCC 的字符串工作方式非常不同,而 GCC 用 C++11 改变了 lot。另外,您是如何跟踪内存使用情况的?因为大多数人都做错了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-18
    • 1970-01-01
    • 2016-01-12
    • 2012-06-02
    • 2018-01-29
    • 1970-01-01
    • 2011-01-14
    相关资源
    最近更新 更多