为什么unordered_set比它包含的数据使用更多的RAM?

时间:2016-05-12 04:16:27

标签: c++ c++11 unordered-set

我有一个相对较大的文件,我需要确保只包含唯一的行。该文件只有500MB。我知道有很多开销,但我看到了近5GB的RAM使用率。我本可以使用外部合并排序并保持少量RAM,但这似乎更快编码。

我正在使用VC ++ 14。

#include <string>
#include <vector>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <unordered_set>

using std::vector;
using std::string;
using std::unordered_set;

class uniqify {
    unordered_set<string> s;
public:
    auto exists(const string &filename) const -> bool {
        std::ifstream fin(filename);
        bool good = fin.good();
        return fin.close(), good;
    }

    void read(const string &filename) {
        std::ifstream input(filename);
        string line;
        while (std::getline(input, line))
            if (line.size())
                s.insert(line);
    }

    void write(const string &filename) const {
        std::ofstream fout(filename);
        for (auto line : s)
            fout << line << "\n";
        fout.close();
    }
};

int main(int argc, char **argv) {
    uniqify u;
    string file("file.txt");
    if(u.exists(file))
        u.read(file);
    u.write("output_file.txt");
    return 0;
}

是什么原因导致RAM飙升超过10倍?

2 个答案:

答案 0 :(得分:11)

unordered_set是基于节点的容器。上次我检查时,MSVC使用双向链表来存储元素,并使用迭代器向量到该链表中来描述存储桶。 max_load_factor()的默认unordered_set为1,因此至少有多个存储桶作为节点。它每桶存储大约一个list迭代器 - 它是一个指针。所以对于每个节点,你有两个指针&#39;双向链表中的开销值,加上至少一个来自桶的指针,总共三个指针。

然后std::string在顶部添加自己的开销。我相信MSVC的std::string是两个指针+16个字节SSO buffer。超过15个字符的字符串将使用动态分配,这会花费更多。

因此集合中的每个字符串至少需要5个指针+ 16个字节的SSO缓冲区,每个指针8个字节,每个字符串最少56个字节。有55M的琴弦​​,那里约有3GB。而且我们还没有计算超过15个字符的字符串,也没有计算每个节点的内存分配开销,这可以很容易地将其带到5GB。

答案 1 :(得分:1)

无论C ++编译器的供应商提供哪种实现,都会产生数据架构的开销。

如果您按照其他类似性质的this question进行讨论,您会发现大多数供应商可能会使用哈希表来实现无序集,并且哈希表需要重新调整大小并以有趣的方式增长如果您有大量动态添加的条目。您应该将表格预先分配到正确的大小,而不是依靠动态的重新调整大小。

但是,这只是猜测,因为我不知道您的系统中使用了什么实现。