3

我试图找出 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
    }
}
4

3 回答 3

3

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

  • Java HashMap 中的最大桶是 2^30 (~1B),所以
    • 使用默认加载因子,当地图在 750 M 条目后尝试调整大小时,您将失败
    • 您需要使用 > 1 的负载因子(例如,理论上 5 可以为您提供 50 亿个项目)
    • 在高负载因子的情况下,你会遇到很多哈希冲突,读写性能都会开始严重下降
    • 一旦你实际超过 Integer.MAX_INTEGER 值,我不知道存在什么陷阱——例如,地图上的 .size() 将无法返回实际计数
  • 我会非常担心在 Java 中运行 256 GB 堆——如果你遇到完整的 GC,它将锁定世界很长一段时间以检查旧代中的数十亿个对象

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

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

于 2012-11-01T15:46:33.533 回答
2

这些是错误的数据结构。你会很快达到内存限制(除非你有 100+gb,即使那样你仍然会很快达到限制)。

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

于 2012-10-31T22:25:31.210 回答
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
于 2012-11-01T02:09:24.380 回答