有什么有效的方法可以对一组>一百万个字符串进行重复数据删除?

时间:2020-02-18 01:24:21

标签: c++ string duplicates

对于我的项目,我需要非常有效地对大量字符串进行重复数据删除。也就是说,给定一个可能包含重复项的字符串列表,我想生成该列表中所有字符串的列表,但不包含任何重复项。

这是简化的伪代码:

set = # empty set
deduped = []
for string in strings:
    if !set.contains(string):
        set.add(string)
        deduped.add(string)

这是简化的C ++(大致):

std::unordered_set <const char *>set;
for (auto &string : strings) {
  // do some non-trivial work here that is difficult to parallelize
  auto result = set.try_emplace(string);
}
// afterwards, iterate over set and dump strings into vector

但是,这还不足以满足我的需求(我已经对其进行了基准测试)。这里有一些使它更快的想法:

  • 使用其他C ++集实现(例如Abseil的实现)
  • 同时插入集合(但是,根据C ++实现中的注释,这很困难。此外,并行化会带来性能开销)
  • 由于字符串集在每次运行中变化很小,因此可能会缓存哈希函数是否不产生冲突。如果它不生成任何内容(同时考虑到更改),则可以在查找过程中通过字符串的哈希比较字符串,而不是通过实际的字符串相等性来比较(strcmp)。
  • 在运行中将重复数据删除的字符串存储在文件中(但是,尽管这看起来很简单,但是这里有很多复杂性)

我发现,所有这些解决方案都非常棘手,或者没有提供那么大的加速比。对快速重复数据删除有什么想法吗?理想情况下,不需要并行化或文件缓存的东西。

2 个答案:

答案 0 :(得分:1)

您可以尝试各种算法和数据结构来解决您的问题:

  1. 尝试使用前缀树(trie),后缀机器,哈希表。哈希表是查找重复项的最快方法之一。尝试其他哈希表。
  2. 使用各种数据属性来减少不必要的计算。例如,您只能处理长度相同的字符串子集。
  3. 尝试实现"divide and conquer"方法来并行化计算。例如,将字符串集除以等于硬件线程的子集数量。然后将这些子集合并为一个。由于在此过程中子集的大小会减小(如果重复项的数量足够大),因此合并这些子集应该不会太昂贵。

不幸的是,没有通用的方法可以解决此问题。在很大程度上,决定取决于正在处理的数据的性质。在我看来,清单上的第二项是最有前途的。始终尝试减少计算量以使用较小的数据集。

答案 1 :(得分:0)

您可以通过手动实现std::unordered_set的简化版本来显着并行化任务:

  1. 创建任意数量的存储桶(可能应该与线程池中的线程数量成比例或相等)。
  2. 使用线程池并行计算字符串的哈希值,并使用btw桶的哈希值拆分字符串。在其中添加字符串时,可能需要锁定各个存储桶,但操作应简短和/或可以使用无锁结构。
  3. 使用您的线程池分别处理每个存储桶-比较散列,如果它们相等,则比较散列字符串。

您可能需要尝试使用存储桶大小,并检查它会如何影响性能。从逻辑上讲,它的一侧不应太大,而另一侧也不能太小-以防止拥塞。

从您的描述开始,听起来您已将所有字符串加载到内存中,然后消除了重复项。您可以尝试直接将数据读取到std::unordered_set,而不是那么节省内存并提高速度。