我想对一个巨大的数组进行排序,比如10 {8个X
类型的条目,最多N
个不同的密钥,其中N
是~10 ^ 2。因为我不知道元素的范围或间距,所以不能选择计数排序。所以到目前为止我最好的猜测是使用哈希映射来计算这样的计数
std::unordered_map< X, unsigned > counts;
for (auto x : input)
counts[x]++;
这个工作正常,比3-way quicksort快4倍,但我是一个紧张的人,而且还不够快。
我想知道:我错过了什么吗?我可以更好地利用N
提前知道的事实吗?或者是否可以根据我的需要调整哈希映射?
编辑另一个前提条件是输入序列排序严格,键的频率大致相同。
答案 0 :(得分:2)
STL的实施在性能方面往往并不完美(请不要进行神圣的战争)。
如果您知道唯一元素( N )的数量有保证且合理的上限,那么您可以轻松实现自己的大小为 2 ^ s 的哈希表&gt ;&GT; 名词的。以下是我通常自己做的事情:
int size = 1;
while (size < 3 * N) size <<= 1;
//Note: at least 3X size factor, size = power of two
//count = -1 means empty entry
std::vector<std::pair<X, int>> table(size, make_pair(X(), -1));
auto GetHash = [size](X val) -> int { return std::hash<X>()(val) & (size-1); };
for (auto x : input) {
int cell = GetHash(x);
bool ok = false;
for (; table[cell].second >= 0; cell = (cell + 1) & (size-1)) {
if (table[cell].first == x) { //match found -> stop
ok = true;
break;
}
}
if (!ok) { //match not found -> add entry on free place
table[cell].first = x;
table[cell].second = 0;
}
table[cell].second++; //increment counter
}
在MSVC2013上,与代码相比,它将时间从0.62秒提高到0.52秒,因为 int 用作 X 类型。
此外,我们可以选择更快的哈希函数。但请注意,散列函数的选择在很大程度上取决于输入的属性。我们来看Knuth's multiplicative hash:
auto GetHash = [size](X val) -> int { return (val*2654435761) & (size-1); };
它进一步将时间缩短到0.34秒。
作为结论:你真的想重新实现标准数据结构以实现2倍的速度提升吗?
注意:另一台编译器/机器上的加速可能完全不同。如果你的 X 类型不是POD,你可能需要做一些黑客攻击。
答案 1 :(得分:2)
计数排序确实是最好的,但由于未知的范围或间距而不适用。
似乎可以使用fork-join轻松并行化,例如boost::thread
您还可以尝试更高效的手动哈希映射。 Unorded_map通常使用链接列表来对抗潜在的错误哈希函数。如果散列表不适合L1高速缓存,则链接列表的内存开销可能会损害性能。 Closed Hashing可能会使用更少的内存。一些优化提示:
X
的类型,所以也许更便宜的哈希函数可能会超过更多的冲突。答案 2 :(得分:0)
我希望将项目存储在已排序的向量中,因为大约100个密钥,意味着插入到向量中只会出现10 ^ 6个条目中的1个。查找将是向量中的处理器效率bsearch