- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
基于哈希表的容器是非常快的关联数组(例如 unordered_map
、 unordered_set
)。
它们的性能高度依赖于用于为每个条目创建索引的哈希函数。随着哈希表的增长,元素一次又一次地重新哈希。
指针是简单类型,基本上是唯一标识对象的 4/8 字节值。问题在于,由于多个 LSB 为零,因此将地址作为散列函数的结果使用效率不高。
例子:
struct MyVoidPointerHash {
size_t operator()(const void* val) const {
return (size_t)val;
}
};
struct MyVoidPointerHash2 {
size_t operator()(const void* val) const {
return ((size_t)val) >> 3; // 3 on 64 bit, 1 on 32 bit
}
};
int*
对于大小为 4KB 的类指针。结果非常有趣。
std::unordered_set
对于所有测试,数据大小为 16MB,在单个
new
中分配称呼。第一个算法运行两次以确保缓存尽可能热并且 CPU 全速运行。
return (size_t)(val);
return '(size_t)(val) >> 3;
uintptr_t ad = (uintptr_t)val;
return (size_t)((13 * ad) ^ (ad >> 15));
uintptr_t ad = (uintptr_t)val;
return (size_t)(ad ^ (ad >> 16));
template<typename Tval>
struct MyTemplatePointerHash1 {
size_t operator()(const Tval* val) const {
static const size_t shift = (size_t)log2(1 + sizeof(Tval));
return (size_t)(val) >> shift;
}
};
int*
:
4K_class*
:
最佳答案
从理论的角度来看,正确的答案是:“使用 std::hash
,它可能专门用于尽可能好,如果不适用,请使用良好的散列函数而不是快速的散列函数。散列函数的速度与其质量无关”。
实际的答案是:“使用 std::hash
,这很糟糕,但性能却出奇地好。”
TL;DR
在变得好奇之后,我在周末运行了大约 30 个小时的基准测试。除其他事项外,我试图获得平均情况与最坏情况,并试图通过故意针对插入的集合大小提供有关桶数的错误提示,将 std::unordered_map
强制为最坏情况行为。
我将较差的散列 ( std::hash<T*>
) 与众所周知的整体良好质量的通用散列 (djb2、sdbm) 以及它们的变体进行了比较,这些散列解释了非常短的输入长度,并与明确认为用于散列表的散列进行了比较(murmur2 和 murmur3),以及实际上比不进行散列更糟糕的 piss-poor 散列,因为它们丢弃了熵。
由于由于对齐,指针上的最低 2-3 位始终为零,因此我认为值得将简单的右移测试为“散列”,因此仅使用非零信息,以防散列表,例如仅使用最低的 N 位。事实证明,对于合理的类次(我也尝试过不合理的类次!),这实际上表现得很好。
发现
我的一些发现是众所周知的,并不令人惊讶,另一些则非常令人惊讶:
std::unordered_map
表现不好真的很难。即使故意对桶计数给出不好的提示,这将迫使多次重新散列,整体性能也不会差多少。只有以几乎荒谬的方式丢弃大部分熵的最糟糕的哈希函数才能显着影响性能超过 10-20%(例如 right_shift_12
,它实际上只导致 50,000 个输入的 12 个不同的哈希值!它在这种情况下,哈希映射的运行速度要慢 100 倍,这并不奇怪——我们基本上是在链表上进行随机访问查找。)。 std::hash<T*>
的特化对于 GCC(一个简单的 reinterpret_cast
)来说是彻头彻尾的可怜。有趣的是,执行相同操作的仿函数始终在插入时执行得更快,而在随机访问时执行得更慢。差异很小(在 8-10 秒的测试运行中为 12 毫秒),但这不是噪音,它始终存在——可能与指令重新排序或流水线有关。完全相同的代码(这也是一个无操作)在两种不同的场景中如何一致地表现不同,这是令人震惊的。 std::hash
。通常他们会进入前 3-4 名。 std::vector
(然后删除了对象,不需要它们)。
std::unordered_map
中,按照 vector 中存储的顺序,一次是原始顺序(“顺序”),一次是在 vector 上应用
std::random_shuffle
之后。
insert
列是创建
std::unordered_map
、分别插入 50,000 和 1,000,000 个元素以及销毁 map 的 50 次迭代的时间(以毫秒为单位)。
access
列是在 'vector' 中对伪随机元素进行 100,000,000 次查找,然后在
unordered_map
中查找该地址的时间(以毫秒为单位)。
vector
中的随机元素,至少对于大数据集(小数据集完全适合 L2)。
Benchmarking hash funcs...
std::hash 2576
reinterpret_cast 2561
djb2 13970
djb2_mod 13969
sdbm 10246
yet_another_lc 13966
murmur2 11373
murmur3 15129
simple_xorshift 7829
double_xorshift 13567
right_shift_2 5806
right_shift_3 5866
right_shift_4 5705
right_shift_5 5691
right_shift_8 5795
right_shift_12 5728
MyTemplatePointerHash1 5652
BasileStarynkevitch 4315
--------------------------------
sizeof(T) = 4
sizeof(T*) = 4
insertion order = sequential
dataset size = 50000 elements
name insert access
std::hash 421 6988
reinterpret_cast 408 7083
djb2 451 8875
djb2_mod 450 8815
sdbm 455 8673
yet_another_lc 443 8292
murmur2 478 9006
murmur3 490 9213
simple_xorshift 460 8591
double_xorshift 477 8839
right_shift_2 416 7144
right_shift_3 422 7145
right_shift_4 414 6811
right_shift_5 425 8006
right_shift_8 540 11787
right_shift_12 1501 49604
MyTemplatePointerHash1 410 7138
BasileStarynkevitch 445 8014
--------------------------------
sizeof(T) = 4
sizeof(T*) = 4
insertion order = random
dataset size = 50000 elements
name insert access
std::hash 443 7570
reinterpret_cast 436 7658
djb2 473 8791
djb2_mod 472 8766
sdbm 472 8817
yet_another_lc 458 8419
murmur2 479 9005
murmur3 491 9205
simple_xorshift 464 8591
double_xorshift 476 8821
right_shift_2 441 7724
right_shift_3 440 7716
right_shift_4 450 8061
right_shift_5 463 8653
right_shift_8 649 16320
right_shift_12 3052 114185
MyTemplatePointerHash1 438 7718
BasileStarynkevitch 453 8140
--------------------------------
sizeof(T) = 4
sizeof(T*) = 4
insertion order = sequential
dataset size = 1000000 elements
name insert access
std::hash 8945 32801
reinterpret_cast 8796 33251
djb2 11139 54855
djb2_mod 11041 54831
sdbm 11459 36849
yet_another_lc 14258 57350
murmur2 16300 39024
murmur3 16572 39221
simple_xorshift 14930 38509
double_xorshift 16192 38762
right_shift_2 8843 33325
right_shift_3 8791 32979
right_shift_4 8818 32510
right_shift_5 8775 30436
right_shift_8 10505 35960
right_shift_12 30481 91350
MyTemplatePointerHash1 8800 33287
BasileStarynkevitch 12885 37829
--------------------------------
sizeof(T) = 4
sizeof(T*) = 4
insertion order = random
dataset size = 1000000 elements
name insert access
std::hash 12183 33424
reinterpret_cast 12125 34000
djb2 22693 51255
djb2_mod 22722 51266
sdbm 15160 37221
yet_another_lc 24125 51850
murmur2 16273 39020
murmur3 16587 39270
simple_xorshift 16031 38628
double_xorshift 16233 38757
right_shift_2 11181 33896
right_shift_3 10785 33660
right_shift_4 10615 33204
right_shift_5 10357 38216
right_shift_8 15445 100348
right_shift_12 73773 1044919
MyTemplatePointerHash1 11091 33883
BasileStarynkevitch 15701 38092
--------------------------------
sizeof(T) = 64
sizeof(T*) = 4
insertion order = sequential
dataset size = 50000 elements
name insert access
std::hash 415 8243
reinterpret_cast 422 8321
djb2 445 8730
djb2_mod 449 8696
sdbm 460 9439
yet_another_lc 455 9003
murmur2 475 9109
murmur3 482 9313
simple_xorshift 463 8694
double_xorshift 465 8900
right_shift_2 416 8402
right_shift_3 418 8405
right_shift_4 423 8366
right_shift_5 421 8347
right_shift_8 453 9195
right_shift_12 666 18008
MyTemplatePointerHash1 433 8191
BasileStarynkevitch 466 8443
--------------------------------
sizeof(T) = 64
sizeof(T*) = 4
insertion order = random
dataset size = 50000 elements
name insert access
std::hash 450 8135
reinterpret_cast 457 8208
djb2 470 8736
djb2_mod 476 8698
sdbm 483 9420
yet_another_lc 476 8953
murmur2 481 9089
murmur3 486 9283
simple_xorshift 466 8663
double_xorshift 468 8865
right_shift_2 456 8301
right_shift_3 456 8302
right_shift_4 453 8337
right_shift_5 457 8340
right_shift_8 505 10379
right_shift_12 1099 34923
MyTemplatePointerHash1 464 8226
BasileStarynkevitch 466 8372
--------------------------------
sizeof(T) = 64
sizeof(T*) = 4
insertion order = sequential
dataset size = 1000000 elements
name insert access
std::hash 9548 35362
reinterpret_cast 9635 35869
djb2 10668 37339
djb2_mod 10763 37311
sdbm 11126 37145
yet_another_lc 11597 39944
murmur2 16296 39029
murmur3 16432 39280
simple_xorshift 16066 38645
double_xorshift 16108 38778
right_shift_2 8966 35953
right_shift_3 8916 35949
right_shift_4 8973 35504
right_shift_5 8941 34997
right_shift_8 9356 31233
right_shift_12 13831 45799
MyTemplatePointerHash1 8839 31798
BasileStarynkevitch 15349 38223
--------------------------------
sizeof(T) = 64
sizeof(T*) = 4
insertion order = random
dataset size = 1000000 elements
name insert access
std::hash 14756 36237
reinterpret_cast 14763 36918
djb2 15406 38771
djb2_mod 15551 38765
sdbm 14886 37078
yet_another_lc 15700 40290
murmur2 16309 39024
murmur3 16432 39381
simple_xorshift 16177 38625
double_xorshift 16073 38750
right_shift_2 14732 36961
right_shift_3 14170 36965
right_shift_4 13687 37295
right_shift_5 11978 35135
right_shift_8 11498 46930
right_shift_12 25845 268052
MyTemplatePointerHash1 10150 32046
BasileStarynkevitch 15981 38143
--------------------------------
sizeof(T) = 256
sizeof(T*) = 4
insertion order = sequential
dataset size = 50000 elements
name insert access
std::hash 432 7957
reinterpret_cast 429 8036
djb2 462 8970
djb2_mod 453 8884
sdbm 460 9110
yet_another_lc 466 9015
murmur2 495 9147
murmur3 494 9300
simple_xorshift 479 8792
double_xorshift 477 8948
right_shift_2 430 8120
right_shift_3 429 8132
right_shift_4 432 8196
right_shift_5 437 8324
right_shift_8 425 8050
right_shift_12 519 11291
MyTemplatePointerHash1 425 8069
BasileStarynkevitch 468 8496
--------------------------------
sizeof(T) = 256
sizeof(T*) = 4
insertion order = random
dataset size = 50000 elements
name insert access
std::hash 462 7956
reinterpret_cast 456 8046
djb2 490 9002
djb2_mod 483 8905
sdbm 482 9116
yet_another_lc 492 8982
murmur2 492 9120
murmur3 492 9276
simple_xorshift 477 8761
double_xorshift 477 8903
right_shift_2 458 8116
right_shift_3 459 8124
right_shift_4 462 8281
right_shift_5 463 8370
right_shift_8 458 8069
right_shift_12 662 16244
MyTemplatePointerHash1 459 8091
BasileStarynkevitch 472 8476
--------------------------------
sizeof(T) = 256
sizeof(T*) = 4
insertion order = sequential
dataset size = 1000000 elements
name insert access
std::hash 9756 34368
reinterpret_cast 9718 34897
djb2 10935 36894
djb2_mod 10820 36788
sdbm 11084 37857
yet_another_lc 11125 37996
murmur2 16522 39078
murmur3 16461 39314
simple_xorshift 15982 38722
double_xorshift 16151 38868
right_shift_2 9611 34997
right_shift_3 9571 35006
right_shift_4 9135 34750
right_shift_5 8978 32878
right_shift_8 8688 30276
right_shift_12 10591 35827
MyTemplatePointerHash1 8721 30265
BasileStarynkevitch 15524 38315
--------------------------------
sizeof(T) = 256
sizeof(T*) = 4
insertion order = random
dataset size = 1000000 elements
name insert access
std::hash 14169 36078
reinterpret_cast 14096 36637
djb2 15373 37492
djb2_mod 15279 37438
sdbm 15531 38247
yet_another_lc 15924 38779
murmur2 16524 39109
murmur3 16422 39280
simple_xorshift 16119 38735
double_xorshift 16136 38875
right_shift_2 14319 36692
right_shift_3 14311 36776
right_shift_4 13932 35682
right_shift_5 12736 34530
right_shift_8 9221 30663
right_shift_12 15506 98465
MyTemplatePointerHash1 9268 30697
BasileStarynkevitch 15952 38349
--------------------------------
sizeof(T) = 1024
sizeof(T*) = 4
insertion order = sequential
dataset size = 50000 elements
name insert access
std::hash 421 7863
reinterpret_cast 419 7953
djb2 457 8983
djb2_mod 455 8927
sdbm 445 8609
yet_another_lc 446 8892
murmur2 492 9090
murmur3 507 9294
simple_xorshift 467 8687
double_xorshift 472 8872
right_shift_2 432 8009
right_shift_3 432 8014
right_shift_4 435 7998
right_shift_5 442 8099
right_shift_8 432 7914
right_shift_12 462 8911
MyTemplatePointerHash1 426 7744
BasileStarynkevitch 467 8417
--------------------------------
sizeof(T) = 1024
sizeof(T*) = 4
insertion order = random
dataset size = 50000 elements
name insert access
std::hash 452 7948
reinterpret_cast 456 8018
djb2 489 9037
djb2_mod 490 8992
sdbm 477 8795
yet_another_lc 491 9179
murmur2 502 9078
murmur3 507 9273
simple_xorshift 473 8671
double_xorshift 480 8873
right_shift_2 470 8105
right_shift_3 470 8100
right_shift_4 476 8333
right_shift_5 468 8065
right_shift_8 469 8094
right_shift_12 524 10216
MyTemplatePointerHash1 451 7826
BasileStarynkevitch 472 8419
--------------------------------
sizeof(T) = 1024
sizeof(T*) = 4
insertion order = sequential
dataset size = 1000000 elements
name insert access
std::hash 10910 38432
reinterpret_cast 10892 38994
djb2 10499 38985
djb2_mod 10507 38983
sdbm 11318 37450
yet_another_lc 11740 38260
murmur2 16960 39544
murmur3 16816 39647
simple_xorshift 16096 39021
double_xorshift 16419 39183
right_shift_2 10219 38909
right_shift_3 10012 39036
right_shift_4 10642 40284
right_shift_5 10116 38678
right_shift_8 9083 32914
right_shift_12 9376 31586
MyTemplatePointerHash1 8777 30129
BasileStarynkevitch 16036 38913
--------------------------------
sizeof(T) = 1024
sizeof(T*) = 4
insertion order = random
dataset size = 1000000 elements
name insert access
std::hash 16304 38695
reinterpret_cast 16241 39641
djb2 16377 39533
djb2_mod 16428 39526
sdbm 15402 37811
yet_another_lc 16180 38730
murmur2 16679 39355
murmur3 16792 39586
simple_xorshift 16196 38965
double_xorshift 16371 39127
right_shift_2 16445 39263
right_shift_3 16598 39421
right_shift_4 16378 39839
right_shift_5 15517 38442
right_shift_8 11589 33547
right_shift_12 11992 49041
MyTemplatePointerHash1 9052 30222
BasileStarynkevitch 16163 38829
关于c++ - 指针的最快哈希函数是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20953390/
什么是更快的安卓? Color.rgb(184, 134, 011); 或 Color.parseColor("#234181"); 还是别的什么? 答案:最快的似乎是: int mycolor =
没错, 基本上我需要计算出从服务器到最终用户的最短路线。我有 2 台服务器 - 一台在英国,一台在美国。 我需要根据最终用户的位置确定从哪个服务器加载内容。 我最初想使用 fsock/curl/fgc
我正在阅读固定宽度整数类型 ( cpp reference) 并遇到int_fast8_t、int_fast16_t、int_fast32_t 和 int_least8_t 类型,int_least1
Closed. This question is opinion-based。它当前不接受答案。 想改善这个问题吗?更新问题,以便editing this post用事实和引用来回答。 6年前关闭。
我有大量目录,我想尽快读取所有文件。我的意思是,不是 DirectoryInfo.GetFiles 快,而是“get-clusters-from-disk-low-level”快。 当然,.NET 2
我尝试寻找最小的可被1到n整除的数字,现在我正在寻求有关进一步压缩/使我的解决方案更有效的方法的建议。如果也有O(1)解决方案,那将非常酷。 def get_smallest_number(n):
有很多不同的方法可以在驱动程序之间选择元素。我想知道哪一个最快且最适合 native 应用程序(iOS 和 Android)。 Appium Driver 类有: findElementByAcces
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,
让矩阵 A 说 A = magic(100);。我见过两种计算矩阵 A 的所有元素之和的方法。 sumOfA = sum(sum(A)); 或者 sumOfA = sum(A(:)); 其中一个比另一
我想为玩具车在没有障碍物的平面 (2d) 上规划一条路线。玩具车应该从点 (p1x,p1y) 移动到 (p2x,p2y)(又名狄利克雷边界条件)。此外,玩具车在起点的速度是(v1x,v1y),终点处要
假设有 n 个 3 维对象(多面体)。最快的方法是计算所有对象的交集O(n^2)? 现在,我正在使用一个基本上强制 T(n) 等于 n ^ 2 的库: for each object: // ther
关闭。这个问题需要 details or clarity 。它目前不接受答案。 想改进这个问题吗? 添加细节并通过 editing this post 澄清问题。 关闭 5 年前。 Improve
在 c: 上,我有数以万计的 *.foobar 文件。它们在各种各样的地方(即子目录)。这些文件的大小大约为 1 - 64 kb,并且是纯文本。 我有一个 class Foobar(string fi
我的基本问题是有多个线程做一些事情,其中一些需要比其他线程更多的时间(20 倍甚至更多),他们需要的时间只取决于起始值,但不能从起始值预测单独他们需要多少时间。为了减少更快线程的空闲时间,我想通过
好的,我有一个疑问: select distinct(a) from mytable where b in (0,3) 什么会更快,上面的还是 select distinct(a) from myta
问题简介: 我正在开发一个生态生理模型,我使用了一个名为 S 的引用类列表。存储模型需要输入/输出的每个对象(例如气象、生理参数等)。 此列表包含 5 个对象(请参见下面的示例): - 两个数据帧,S
我有一个正在工作的问题陈述,但我仍然想知道更高效、更快,更重要的是正确设计来处理下面提到的场景。 我有一个 POJO 类 class A { String s; Double d; } 我正在
关闭。这个问题需要更多focused .它目前不接受答案。 想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post . 关闭 3 年前。 Improve this q
关于 LLVM 如何优化代码,关于 SO 以及整个网络都有一些非常好的描述。但这些都无法回答我的具体问题。 在 Xcode 中,项目和目标设置中有各种代码优化选项。我理解在开发过程中不需要优化,但为什
我正在用 C# 开发一个服务器项目,在收到 TCP 消息后,它会被解析并存储在一个精确大小的 byte[] 中。 (不是固定长度的缓冲区,而是存储所有数据的绝对长度的字节[]。) 现在为了阅读这个 b
我是一名优秀的程序员,十分优秀!