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

Scala:Hash忽略初始大小(数十亿条目的快速哈希表),scala,hash,hashmap,Scala,Hash,Hashmap,我试图找出Scala的散列函数在大型散列表中的伸缩性(具有数十亿个条目,例如存储特定DNA位出现的频率) 然而,有趣的是,HashMap和OpenHashMap似乎都忽略了指定初始大小的参数(2.9.2.和2.10.0,最新版本) 我认为这是因为在第一次800000左右之后,添加新元素的速度要慢得多 我尝试过增加要插入的字符串中的熵(仅在下面的代码中增加字符ACGT),但没有效果 对这个具体问题有什么建议吗?如果您能就使用Scala的内置类型是否适合拥有数十亿条目的哈希表发表意见,我也将不胜感激

我试图找出Scala的散列函数在大型散列表中的伸缩性(具有数十亿个条目,例如存储特定DNA位出现的频率)

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

我认为这是因为在第一次800000左右之后,添加新元素的速度要慢得多

我尝试过增加要插入的字符串中的熵(仅在下面的代码中增加字符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
    }
}
import scala.collection.mutable.{HashMap,OpenHashMap}
导入scala.util.Random
对象HelloWorld{
def main(参数:数组[字符串]){
val h=new collection.mutable.HashMap[String,Int]{
覆盖def初始大小=8388608
}
//valh=newscala.collection.mutable.OpenHashMap[Int,Int](8388608);

对于(i这些是错误的数据结构。你会很快达到ram限制(除非你有100+gb,即使这样,你也会很快达到限制)


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

首先,您不能覆盖initialSize,我认为scala让您这样做是因为它在哈希表中是包私有的:

private[collection] final def initialSize: Int = 16
第二,如果你想设置初始大小,你必须给它一个你想要的初始大小的哈希表。因此,如果不从16开始,真的没有好的方法来构建这个映射,但是它确实增加了2的幂,所以每次调整大小都应该更好

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

最后,对于大多数硬件来说,数十亿个条目有点多,您可能会耗尽内存。您很可能需要使用内存映射文件,下面是一个很好的示例(但没有哈希):

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

更新2 我运行了您的代码,它确实降低了大约800000个条目的速度,但后来我提高了java堆的大小,它运行得很好

-Xmx2G
或者,如果您想使用内存的最后一点:

-Xmx256G

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

  • Java HashMap中的最大存储桶数是2^30(~1B),因此
    • 使用默认加载因子,当贴图尝试在750 M条目后调整大小时,您将失败
    • 您需要使用一个大于1的负载系数(例如,理论上5将为您提供50亿件物品)
    • 使用高负载系数,您将获得大量哈希冲突,并且读写性能将开始严重下降
    • 一旦您实际超过Integer.MAX_整数值,我就不知道存在什么问题了--.size()在地图上就无法返回实际计数,例如
  • 我会非常担心在Java中运行256 GB的堆——如果你碰到一个完整的GC,它将在很长一段时间内锁定世界,以检查旧gen中数十亿的对象
如果是我,我会考虑一个堆外解决方案:某种类型的数据库。如果只是存储(hashcode,count),那么许多键值存储中的一个可能会起作用。最大的障碍是找到一个可以支持数十亿条记录(一些最大值为2^32)的数据库


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

你不会耗尽内存吗?32或64位jvm?关于忽略初始大小:不会,你可以检查HashMap的源代码谢谢你的回答。为了澄清,这将部署到n台内存为256G+的机器。@Noah:但每次翻倍后,它都必须复制存储桶内容,对吗?但即使这是真的,它也不能向我解释为什么在800.000次左右的迭代后会出现性能下降——我希望在执行重新排列时性能会急剧下降,然后恢复到全速。@Arjan:64位。除了我描述的性能下降外,无论我设置的初始大小如何,我程序的内存占用都不会改变。请参阅下面的更新,您需要增加最大heep大小。我认为高比例库在这方面没有帮助。无论如何,与映射大小相关的问题也没有。高比例库提供的数据结构即使有许多CPU同时使用,性能也很好。我不认为处理大量集合有什么特别的地方。你认为他将如何构建一个十亿入口hashmap?必须使用一堆CPU进行多线程处理,否则将花费很长时间。