在SCALA中读取文件的性能

时间:2017-07-27 14:26:45

标签: scala file

我正在阅读一个包含277272行的文件,其中包含Int三元组(s,p,o),如:

2,126088,113358
2,126101,126102
2,126106,126107
2,126111,126112
2,126128,126129
2,126136,126137
2,126141,126142
2,126146,126147
4,1,3
4,7,8
4,15,16
4,26,27
4,41,94825
4,63,15764
4,68,69
4,89,94836
4,90,91
4,93,94
4,105,94844 

这个三元组按顺序排序,然后是p。我写了下面的代码,用s搜索一个值,一旦找到值,我写一个条件来过滤p。

 def SP_O (yi:Int, zi:Int):List[Vector[Int]]={
    val file = new RandomAccessFile("patSPO.dat", "r")
    var lengthfile = (file.length().asInstanceOf[Int])
    var initpointer = lengthfile/2

    while (band == true){
      var p = bs.firstvalue(initpointer,"patSPO.dat")  // p es la lista del valor del predicado con su respectivo puntero
      var newpointer = p(0)
      var subj = p(1)

      //Comienzo el analisis para saber si el predicado es igual al del BGP
      if (subj == yi){
        // Hay que imprimir los valores de la presente linea
        if (firsttime == true){
          val fields = bs.getvalueSP_O(newpointer,zi)
          if (fields.size != 0){
            S_POlistbuffer += fields
            //yld(Record(fields, schema))
          }

          firsttime = false
          pointerad = newpointer
          pointerat = newpointer
        } // Ahora tengo que analizar si hay valores atras o adelante

        while (boolad == true || boolat == true){
          if (boolat == true) {
            //Valores para atras
            pointerat -= 6
            if (pointerat <= 0) {
              pointerat = 0
              boolat = false
            }
            p = bs.firstvalue(pointerat, "patSPO.dat") // p ds la lista del valor del sujeto en pointerat y da su respectivo pointer
            if (p(1) == yi) {
              val fields = bs.getvalueSP_O(p(0),zi)
              if (fields.size != 0){
                S_POlistbuffer += fields
                //yld(Record(fields, schema))
              }
            } else {
              boolat = false
            }
          }
          if (boolad == true){ //valores par adelante
            p = bs.nextvalue(pointerad,"patSPO.dat")
            if (p(1)==yi){
              val fields = bs.getvalueSP_O(p(0),zi)
              pointerad = p(0)
              if (fields.size != 0){
                S_POlistbuffer += fields
                //yld(Record(fields, schema))
              }
            }else{
              boolad = false
            }
          }
          if (boolad == false && boolat == false){
            band = false
          }

        }

      }
      else if ( subj > yi ){
        initpointer = initpointer/2
      }
      else if ( subj < yi) {
        initpointer = (initpointer*6)/4
      }

    }
    val listf:List[Vector[Int]] = S_POlistbuffer.toList
    listf
  } 

一般的想法是代码开始从文件的一半开始查看值,使用filerandomaccess,然后我应该提取该行的第一个值,并在找到正确的值后与我需要的值进行比较值我必须分析下一行是否也有正确的值,在并行中我分析前一行到我选择的行,以查看该行是否也是正确的值。对于匹配的每一行,我分析第二个值是否正确。如果行匹配,我提取o值,并将其存储在List中。

问题是当我打印结果时,这需要花费很多时间。但是我开发了另一个在整个文件中运行的解决方案:

while (in.hasNext) {
        val s  = in.next(',').toInt
        val p = in.next(',').toInt
        val o  = in.next('\n').toInt

        //val fields = schema map (n => in.next(if (n == schema.last) '\n' else ','))
        if (p == yi && s == xi){
          val fields = schema map (n => o)
          yld(Record(fields, schema))
        }
      }

使用此代码我遍历整个文件,我得到的结果比第一个代码更快。我的一个大问题是,为什么如果最好的情况下的第一个代码运行文件的一部分比运行整个文件的第二个代码慢?是否有另一种方法来编写具有更好性能的代码? 第一个代码执行的时间是750秒,第二个代码需要10秒。

1 个答案:

答案 0 :(得分:0)

我认为你错过了两条重要的信息:

  1. 没有“并行”读取文件的事情。即使在今天,很多磁盘读取正在旋转该死的磁盘,直到它处于正确的位置,然后读取。因此,您可以使用多个线程,您执行的并发磁盘访问越多,读取性能就越差。一个简化的观点是,每次你“向后”,你实际上让你的磁盘完全旋转到达前一行!

  2. 顺序从磁盘读取比随机磁盘访问快几个数量级。这与1相关,但实际上,逐行读取文件是访问磁盘的最佳方式!事实证明,这是你天真的版本所做的!

  3. 这让我相信这些是合乎逻辑的后续步骤:

    1. 将智能算法应用于先前已将其加载到内存中的文件
    2. 再次与天真版本进行比较
    3. 找不到显着差异
    4. 得出结论,您的工作流程肯定是受I / O限制的,并停止尝试优化其中无关紧要的部分!
    5. 请注意,如果你有SSD,你的里程可能会有所不同(我没想到,因为我真的没办法想到SSD需要750秒的随机访问线)。