我从证券交易所收到“订单更新”。每个订单ID在1到100 000 000之间,因此我可以使用1亿个数组来存储1亿个订单,当收到更新时,我可以通过索引arrray[orderId]
非常快速地从数组中查找订单。我将花费几千兆字节的内存,但这没关系。
或者我可以使用hashmap,因为在任何时候“活动”命令的数量都是有限的(到,非常大致,100 000),查找也会非常快,但可能比数组慢一点
问题是 - hashmap实际上会慢吗?是否合理地创造了1亿个阵列?
我需要延迟而没有别的,我完全不关心内存,我应该选择什么?
答案 0 :(得分:17)
每当考虑性能问题时,一个实验值得一千个专家意见。测试一下!
那就是说,我会在黑暗中狂奔:如果你可以说服你的操作系统让你的数字万亿字节阵列驻留在物理内存中(这不一定容易) - 考虑一下{ {1}}和mlock
系统调用),您将获得相对更好的性能。您注意到(如果存在)任何此类性能增益可能是因为绕过散列函数的成本,并且避免了与您的散列图实现使用的任何冲突解决和内存分配策略相关的开销。
还值得注意的是,许多哈希表实现对于某些操作具有非恒定的复杂性(例如,在最坏的情况下,单独的链接可能降级为munlock
。鉴于您正在尝试优化延迟,具有非常积极的信号到OS内存管理器(例如,O(n)
和madvise
)的阵列可能会导致最接近恒定延迟的查找可以很容易地使用微处理器。
答案 1 :(得分:8)
虽然客观地回答这个问题的唯一方式是性能测试,但我会争论使用Hashtable Map。 (缓存和内存访问可能充满惊喜;我没有专业知识来推测哪一个会更快,何时更快。还要考虑其他代码可能会使本地化性能差异被边缘化。)
“最初选择”哈希的第一个原因是基于观察到有100M个不同的密钥但仅 0.1M活动记录。这意味着如果使用数组,则索引利用率仅为0.1% - 这是非常稀疏的数组。
如果数据以的形式存储在数组中,那么它需要相对较小或者数组大小会膨胀。如果数据不存储在数组中(例如,数组是指针),那么部分地减轻了数组中数据的局部性的参数。无论哪种方式,简单数组方法都需要大量未使用的空间。
由于所有键都已经是整数,分布(哈希)函数并且可以有效地实现 - 不需要创建复杂类型/序列的哈希,因此“成本”这个函数应该接近零。
所以,我简单的提议哈希:
虽然我已经针对给定的案例提出了“优化”的专用哈希表规则,但我会从一个普通的Map实现开始(无论是哈希表还是树)并测试它...如果标准实现工作得很好,为什么不呢?用吗?
现在,在预期和极端负荷下测试不同的候选人 - 并挑选胜利者。
答案 2 :(得分:2)
这似乎取决于ID的聚类。
如果活动ID已经适当地进行了群集,那么在没有散列的情况下,操作系统和/或L2缓存可以很好地保留好数据并保持低延迟。
如果它们是完全随机的,那么只要活动事务的数量超过可用缓存行数或者这些事务的大小超过缓存的大小,就会受到影响(目前尚不清楚哪个是可能在你的情况下首先发生。)
然而,如果活动ID有一些不幸的模式导致高争用率(例如,它是一个不同属性的位包,并且频繁变化的属性击中硬件受到伤害的地方),然后你可能会受益于使用索引的1:1哈希来回到随机情况,即使这通常被认为是一个非常糟糕的情况。
对于压实的哈希来说;注意到有些人担心哈希冲突的最坏情况回退行为,你可能只是在连续内存中实现了一个完整大小的表的缓存,因为它有一个合理约束的最坏情况。只需在地图中保留最繁忙的条目,然后回到碰撞的完整表格。如果它更活跃,则将另一个条目移动到地图中(如果您可以找到合适的算法来确定它)。
即便如此,还不清楚必要的哈希表大小是否足以将工作集减少为可缓存。你的订单有多大?
答案 3 :(得分:0)
散列映射与数组的开销几乎为零。
,毫无疑问,我会在100,000,000个阵列上打赌100,000个记录的散列图。还要记住,虽然你“不关心内存”,但这也意味着你最好有内存备份它 - 一个100,000,000个整数的数组将占用400mb,即使它们都是空的。您冒着换出数据的风险。如果您的数据被换出,您将获得几个数量级的性能。
答案 4 :(得分:0)
你应该像其他人所说的那样测试和分析。我在黑暗中随机刺伤:高负荷因子哈希表将是这里的方式。一个巨大的阵列将花费您TLB未命中,然后每次访问最后一级缓存未命中。这很贵。根据您提到的工作集大小,哈希表可能只会花费一些算术和L1未命中。
再次,在代表性示例中测试两种替代方案。我们都只是在黑暗中刺伤。