是否有可用于Java的HashMap的并行处理实现?它甚至可能吗?

时间:2009-09-04 23:10:01

标签: java multithreading collections parallel-processing

搜索神奇的ParallelHashMap类

更简洁的是,您可以使用多个线程来加速HashMap查找吗?是否有任何实现已经执行此操作?

在我的项目中,我们需要在内存中维护一个大的对象映射。我们从不>在创建地图后修改地图,因此地图严格是只读的。但是,此映射上的读取和查找性能对于应用程序的成功至关重要。安装应用程序的系统通常具有许多可用的硬件线程。然而,我们的查找只使用单个线程从HashMap中检索值。使用多个线程(可能在池中)的分而治之的方法是否有助于提高查找速度?

我的大部分谷歌搜索都没有结果 - 返回了很多关于并发问题而不是解决方案的结果。任何建议都会受到赞赏,但如果您知道开箱即用的解决方案,那就太棒了。

另外值得注意的是,所有键和值都是不可变的。散列码值是预先计算的,并在实例化时存储在对象本身中。

至于实施的细节,地图中有大约35,000个项目。键和值都是对象。键是自定义查找键,值是字符串。目前,我们每秒可处理大约5,000次查找(这包括一些其他逻辑的开销,但主要的瓶颈是地图实现本身)。但是,为了满足我们未来的性能需求,我希望每秒可以获得大约10,000次查找。按照大多数正常标准,我们目前的实施速度很快 - 只是我们需要它更快。

在我们的35,000个值的Map中,我们平均有一个哈希码冲突,所以我猜测哈希码的分布相当合理。

7 个答案:

答案 0 :(得分:7)

因此,您的哈希码是预先计算的,并且equals函数很快 - 在这种情况下,您的hashmap应该非常快。

您是否已分析过您的应用程序以证明hashmap确实是瓶颈?

如果你有多个应用程序线程,他们都应该能够同时从hashmap执行自己的获取 - 因为你没有修改地图,所以你不需要在外部同步获取。使用散列映射的应用程序是否能够充分利用所有硬件线程?

由于哈希表的内容是不可变的,因此可能值得查看perfect hashing - 使用完美的哈希函数,您不应该在哈希表中发生冲突或需要链接,这可能会提高性能。我不知道手头的java实现,但是在C / C ++中知道,有gperf

答案 1 :(得分:3)

听起来你应该介绍一下。你可能会有很高的碰撞率。您还可以尝试在HashMap中使用较低的loadFactor来降低碰撞概率。

另外,如果hashCodes是预先计算的,那么除了mod和一些equals()之外,get()没有太多工作要做。关键对象的equals()有多快?

答案 2 :(得分:2)

回答你的问题:是的,绝对的。因为你不是在写它。

你将不得不手工制作它,这将有点棘手。在尝试之前,您是否已尽可能优化?

在C ++中,查看其sparsehash包中的Google密集哈希映射类。

在Java中,如果您使用原始键进行映射,请使用Trove或Colt贴图。

也就是说,这是你的并行哈希映射的开始:如果你选择n个哈希函数并产生n个线程来搜索每个路径(探测/链接每个n个插入点)你将得到一个不错的加速。要小心,因为创建线程的成本很高,因此在构造时产生线程,然后阻止它们直到需要它们为止。

希望锁定的成本不会高于查找成本,但这取决于您的实验。

答案 3 :(得分:1)

HashMap documentation(我改变了重点):

  

请注意,此实现不是   同步。如果有多个线程   同时访问此地图,和at   至少有一个线程修改了   从结构上来说,一定是   外部同步。

由于您的HashMap永远不会被修改,因此您可以安全地从中读取多个线程。实现锁定不是必需的。 (对于线程共享对不可变数据的访问的任何情况都是如此;通常,实现线程安全的最简单方法是不共享任何可写内存)

为确保您的代码不会意外修改地图,我会在构建后立即用Collections.unmodifiableMap包裹地图。不要让任何对原始可修改地图的引用遗留下来。

答案 4 :(得分:1)

你在评论中提到了这一点:

  

我正在做5个引用的数字之间的等号检查

由此我推断你的哈希计算也在用这5个数字做一些计算。为了获得良好的HashMap性能,此计算的结果应随机分散在所有可能的int值上。来自HashMap documentation

  

此实现提供   恒定时间表现   基本操作(获取和放置),   假设散列函数分散   其中的要素恰到好处   桶中。

换句话说,无论元素数是多少,查找时间都应该保持不变,如果你有一个好的哈希函数 。存储三个数字的类的良好hashCode()函数示例(使用prime number来减少由于注释建议的XOR产生零的机会):

return this.a.hashCode() ^ (31 * (this.b.hashCode() ^ (31 * this.c.hashCode())));

错误 hashCode函数示例:

return (this.a + this.b + this.c);

答案 5 :(得分:0)

HashMaps具有不断的查找时间。不知道如何真正加快速度,因为尝试让多个线程执行散列函数只会导致它变慢。

答案 6 :(得分:0)

我认为您需要证据证明HashMap上的get()方法是您延迟的地方。我认为这不太可能。围绕你的get()方法循环使其重复1,000次,你的应用程序可能根本不会减速。然后你会知道延迟在其他地方。