STL容器和大量数据

时间:2014-07-28 15:55:52

标签: c++ c++11 vector stl unordered-map

我有大量的数据被读入内存 - 暂时,但对于系统来说是必需的。

我一直在检查std::vector以及std::unordered_map的效果 对于std::vector,我使用了struct类型:

struct information{
    std::string name;
    unsigned int offset;
}

对于std::unordered_map,我使用std::string作为密钥,unsigned int offset作为值。

如果,让我们说,其中2 000 000被加载到内存中,我尝试了以下内容并得到了这些结果:

std::vector:

在随机字符串上,如果在向量上调用了保留字,则永远不会超过32个字符。

std::vector<information> vec;
vec.reserve(2500000);

插入

vec.push_back({dataName, offset});

非常快。但是,尝试查找数据非常缓慢。发现是这样实现的:

auto it = std::find_if(vec.begin(), vec.end(), [&name](information &info) -> bool {return info.name == name); });

有意义的是,它是一个大型向量,并且在名称比较中找到了正确的struct。但是表现极差。使用的内存很好 - 我假设内存增长的一部分是由于std::string大小调整大小。

关于矢量实现的问题是:有没有办法增加查找时间?我知道可以对矢量进行排序以增加查找时间,但是在排序矢量时会浪费时间。特别是在这种尺寸的矢量上。

std::unordered_map

插入

std::unordered_map<std::string, unsigned int> unordMap;
unordMap.reserve(2500000);
unordMap.emplace(name, offset);

需要很长时间。在预先留出空间以试图缩短插入时间时,会发生以下情况:

当不调用reserve时,插入结束时的内存更多,没有保留内存仍然比矢量实现更多。储备并没有真正改善插入时间。

当然,查询速度非常快。我关于std::unordered_map的问题是可以改善插入时间和内存使用情况吗?

如果这些都不能完成,那么我的下一个问题可能会非常明显。有没有办法在这两个数据结构之间得到结果?大量数据的最佳效果是什么?

4 个答案:

答案 0 :(得分:4)

struct information{
  std::string name;
  unsigned int offset;
  information(information const&)=default;
  information(information&&)=default;
  information(std::string n, unsigned o):name(std::move(n)),offset(o),hash(std::hash<std::string>()(name)) {};
  information():information("",0) {};
  bool operator<( information const& o ) const {
    return tie() < o.tie();
  }
  std::tuple<std::size_t, std::string const&> tie() const { return std::tie(hash, name); }
private:
  std::size_t hash;
};

std::vector使用上述结构。

添加完所有数据后,std::sort

要找到与name匹配的内容,请执行以下操作:

struct information_searcher {
  struct helper {
    std::tuple<std::size_t, std::string const&> data;
    helper( std::string const& o ):data(std::hash<std::string>()(o), o) {};
    helper( helper const& o ) = default;
    helper( information const& o ):data(o.tie()) {}
    bool operator<( helper const& o ) const { return data < o.data; }
  };
  bool operator()( helper lhs, helper rhs ) const { return lhs < rhs; }
};

information* get_info_by_name( std::string const& name ) {
  auto range = std::equal_range( vec.begin(), vec.end(), information_searcher::helper(name), information_searcher{} );
  if (range.first == range.second) {
    return nullptr;
  } else {
    return &*range.first;
  }
}

这是一个几乎零开销的查找。

我们在这里做的是对字符串进行哈希处理(以便进行快速比较),如果我们发生冲突,请回到std::string比较。

information_searcher是一个允许我们搜索数据的类,而不必创建information(这需要浪费的分配)。

get_info_by_name返回一个指针 - nullptr如果找不到,指针指向第一个带有该名称的元素。

更改information.name是不礼貌的,并使hash字段不正确。

这可能会使用比原始std::vector版本更多的内存。

一般情况下,如果您的作品包含“将一堆内容添加到表格中”。然后做一堆查找&#39;,最好的办法是建立一个std::vector,快速排序,然后使用equal_range进行查找。 mapunordered_map针对大量混合插入/删除/等进行了优化。

答案 1 :(得分:1)

大向量的问题是当您不知道所需对象的索引时的查找时间。正如你所说的,改进它的一种方法是保持有序向量并对其进行二进制搜索。这样,查找时间将不是线性复杂性,而是对数复杂度,这对于非常大的容器节省了相当多的时间。这是std::map中使用的查找(有序的查找)。您可以使用std::vector上的std::lower_boundstd::equal_range进行类似的二进制搜索。

大型无序映射的问题完全不同:这种容器使用散列函数和模数计算,以便根据元素在标准数组中放置元素。因此,当std::unordered_map中有n个元素时,您不太可能只需要一个n元素长数组,因为某些索引不会被填充。您将至少使用hash-and-modulo生成的最大索引。提高内存使用率和插入时间的一种方法是编写自己的哈希函数。但根据您使用的字符串类型,可能会很难。

答案 2 :(得分:1)

vector通常实现为“动态数组”,应该是最节省内存的。 有了良好的预约策略,它可以插入O(1)=快。搜索是O(n)=非常糟糕。

你可以通过排序来帮助矢量(如果你第一次加载然后搜索比我认为最好 - std :: sort + std :: binary_search)。
您也可以使用std :: lower_bound实现insert-sort之类的操作。 insert = O(log n)= good,search = O(log n)= good

map(ordered)实际上可以做同样的工作,但也可以使用tree =较少的内存效率,访问和排序的向量一样好(但可能更少的重新分配,但在你的情况下,排序的向量仍然是最好的)

unordered_map通常使用哈希表来实现=一些内存开销但快速操作(插入不能像未排序的向量那样快,但应该仍然非常快)。散列的问题在于它可以快速甚至最快,但也可能是最差的解决方案(在极端条件下)。上面的结构(排序的矢量和地图/树是稳定的,总是表现相同 - 对数复杂度)。

答案 3 :(得分:0)

嗯,这里的最佳解决方案是创建std :: map,它在插入和查找方面都具有对数的复杂性。虽然,我没有看到你为什么不使用std :: vector的原因。使用快速排序甚至可以对2M元素进行排序时速度非常快,特别是如果你只进行一次。 std :: binary_search非常快。如果您需要在插入之间进行大量查找,请仔细考虑。