在scala中减去两个数组的最快方法是什么?

时间:2012-12-18 20:24:18

标签: scala micro-optimization scala-2.9

我有两个数组(我从矩阵中取出(Array [Array [Int]]),我需要从另一个中减去一个。

目前我正在使用这种方法,当我对它进行分析时,它就是瓶颈。

def subRows(a: Array[Int], b: Array[Int], sizeHint: Int): Array[Int] = {
   val l: Array[Int] = new Array(sizeHint)
   var i = 0
   while (i < sizeHint) {
     l(i) = a(i) - b(i)
     i += 1
   }
   l
 }

我需要这么做数十亿次,所以速度的提高是有利的。

我尝试使用List代替Array来收集差异,但速度要快得多,但当我将其转换回Array时,我将失去所有好处。

我确实修改了下游代码以获取List以查看是否有帮助,但我需要不按顺序访问列表中的内容,以便再次失去任何收益。

似乎任何一种类型的转换都是昂贵的,我想知道是否有某种方法可以使用更快的地图等。

有更好的方法吗?


修改

不确定我第一次做了什么!?

所以我用来测试它的代码是:

def subRowsArray(a: Array[Int], b: Array[Int], sizeHint: Int): Array[Int] = {
  val l: Array[Int] = new Array(sizeHint)
  var i = 0
  while (i < sizeHint) {
    l(i) = a(i) - b(i)
    i += 1
  }
  l
}

def subRowsList(a: Array[Int], b: Array[Int], sizeHint: Int): List[Int] = {
  var l: List[Int] = Nil
  var i = 0
  while (i < sizeHint) {
    l = a(i) - b(i) :: l
    i += 1
  }
  l
}

val a = Array.fill(100, 100)(scala.util.Random.nextInt(2))
val loops = 30000 * 10000

def runArray = for (i <- 1 to loops) subRowsArray(a(scala.util.Random.nextInt(100)), a(scala.util.Random.nextInt(100)), 100)

def runList = for (i <- 1 to loops) subRowsList(a(scala.util.Random.nextInt(100)), a(scala.util.Random.nextInt(100)), 100)

def optTimer(f: => Unit) = {
  val s = System.currentTimeMillis
  f
  System.currentTimeMillis - s
}

我认为我第一次这样做的结果恰恰相反......我必须误解或混淆方法。

对于提出一个糟糕的问题我很抱歉。

2 个答案:

答案 0 :(得分:6)

使用标准JVM管理单线程的代码是最快的。如果您认为List更快,那么您要么自欺欺人,要么实际上没有告诉我们您在做什么。将Int放入List需要两个对象创建:一个用于创建列表元素,另一个用于设置整数。对象创建所需的时间比数组访问长约10倍。因此,以任何其他方式做到这一点并不是一个成功的主张。

如果你真的,真的需要更快,并且必须保持单个线程,你应该切换到C ++等,并明确使用SSE指令。例如,请参阅this question

如果你真的,真的需要更快,并且可以使用多个线程,那么最简单的是打包这样的一大块工作(即需要减去的合理数量的矢量对 - 可能至少只要计算机上的处理器数量,就可以将每个块的几百万个元素放入一个列表中,然后调用list.par.map(yourSubtractionRoutineThatActsOnTheChunkOfWork)

最后,如果你是破坏性的,

a(i) -= b(i)
当然,内循环中的

更快。同样,如果您可以重复使用空格(例如使用System.arraycopy),那么您最好不要继续分配空间。但是这会改变你所展示的界面。

答案 1 :(得分:1)

您可以使用Scalameter尝试对两个实现进行基准测试,这两个实现至少需要运行JRE 7 update 4和Scala 2.10。我使用了scala 2.10 RC2。

使用scalac -cp scalameter_2.10-0.2.jar RangeBenchmark.scala进行编译。

使用scala -cp scalameter_2.10-0.2.jar:. RangeBenchmark运行。

这是我使用的代码:

import org.scalameter.api._

object RangeBenchmark extends PerformanceTest.Microbenchmark {
  val limit = 100
  val a = new Array[Int](limit)
  val b = new Array[Int](limit)
  val array: Array[Int] = new Array(limit)
  var list: List[Int] = Nil
  val ranges = for {
    size <- Gen.single("size")(limit)
  } yield 0 until size

  measure method "subRowsArray" in {
    using(ranges) curve("Range") in {
      var i = 0
      while (i < limit) {
        array(i) = a(i) - b(i)
        i += 1
      }
      r => array
    }
  }

  measure method "subRowsList" in {
    using(ranges) curve("Range") in {
      var i = 0
      while (i < limit) {
        list = a(i) - b(i) :: list
        i += 1
      }
      r => list
    }
  }
}

结果如下:

::Benchmark subRowsArray::
Parameters(size -> 100): 8.26E-4

::Benchmark subRowsList::
Parameters(size -> 100): 7.94E-4

您可以得出自己的结论。 :)

堆叠在较大的limit值上爆炸。我猜这是因为它多次测量性能。