Scala:Hash忽略初始大小(数十亿条目的快速哈希表)

时间:2012-10-31 20:55:41

标签: scala hash hashmap

我试图找出Scala的哈希函数在大型哈希表中的扩展程度(数十亿条目,例如存储特定DNA出现的频率)。

然而,有趣的是,HashMap和OpenHashMap似乎都忽略了指定初始大小的参数(2.9.2。和2.10.0,最新版本)。

我认为这是因为在第一次800.000之后添加新元素变得慢得多。

我试过增加要插入的字符串中的熵(下面代码中只有字符ACGT),没有效果。

有关此特定问题的任何建议吗?我也很高兴听到您对使用Scala的内置类型是否适合具有数十亿条目的哈希表的看法。

import scala.collection.mutable.{ HashMap, OpenHashMap }    
import scala.util.Random

object HelloWorld {
    def main(args: Array[String]) {


        val h = new collection.mutable.HashMap[String, Int] {
            override def initialSize = 8388608
        }

        // val h = new scala.collection.mutable.OpenHashMap[Int,Int](8388608); 



        for (i <- 0 until 10000000) {
            val kMer = genkMer()

            if(! h.contains(kMer))
            {
                h(kMer) = 0;
            }
            h(kMer) = h(kMer) + 1;

            if(i % 100000 == 0)
            {
                println(h.size);
            }
        }

        println("Exit. Hashmap size:\n");
        println(h.size);

    }

    def genkMer() : String =
    {
        val nucs = "A" :: "C" :: "G" :: "T" :: Nil

        var s:String = "";
        val r = new scala.util.Random
        val nums = for(i <- 1 to 55 toList) yield r.nextInt(4) 
        for (i <- 0 until 55) {
            s = s + nucs(nums(i))
        }
        s
    }
}

3 个答案:

答案 0 :(得分:3)

我不会使用Java数据结构来管理数十亿条目的地图。原因:

  • Java HashMap中的最大存储桶是2 ^ 30(~1B),所以
    • 使用默认加载因子,当地图在750 M条目后尝试调整大小时,您将失败
    • 您需要使用加载因子&gt;例如,理论上会有5个项目(例如,5个)
    • 使用高负载因子,您将获得大量的哈希冲突,读写性能将开始严重降低
    • 一旦你实际超过Integer.MAX_INTEGER值,我不知道存在什么问题 - 地图上的.size()将无法返回实际数,例如
  • 我非常担心在Java中运行一个256 GB的堆 - 如果你曾经使用过完整的GC,它将锁定世界很长一段时间来检查旧版本中的数十亿个对象

如果是我,我会看一个堆外解决方案:某种数据库。如果您只是存储(哈希码,计数),那么可能有效的许多键值存储之一。最大的障碍是找到一个可以支持数十亿条记录的记录(最多可以支持2 ^ 32)。

如果你能接受一些错误,概率方法可能值得一看。我在这里不是专家,但列出here的内容听起来很相关。

答案 1 :(得分:2)

这些是错误的数据结构。你会很快达到一个ram限制(除非你有100 + gb,即便如此,你仍然会很快达到限制)。

我不知道scala是否存在合适的数据结构,尽管有人可能会使用Java做一些事情。

答案 2 :(得分:2)

首先,你不能覆盖initialSize,我认为scala让你,因为它在HashTable中是私有的包:

private[collection] final def initialSize: Int = 16

其次,如果要设置初始大小,则必须为其提供所需初始大小的HashTable。因此,如果没有从16开始构建这个地图真的没有好办法,但它确实以2的幂增长,所以每次调整大小都会变得更好。

第三,scala集合相对较慢,我建议使用java / guava / etc集合。

最后,数十亿条目对于大多数硬件来说有点多,你可能会耗尽内存。你很可能需要使用内存映射文件,这是一个很好的例子(尽管没有散列):

https://github.com/peter-lawrey/Java-Chronicle

更新1 这里有一个很好的替代java集合:

https://github.com/boundary/high-scale-lib

更新2 我运行你的代码,它确实减慢了大约800,000个条目,但后来我提高了Java堆大小,它运行良好。尝试使用类似于jvm的东西:

-Xmx2G

或者,如果你想使用你记忆的最后一点:

-Xmx256G