scala - 为什么在 Scala 中压缩比 zip 快?

我编写了一些 Scala 代码来对集合执行元素操作。在这里,我定义了两个执行相同任务的方法。一种方法使用 zip其他用途zipped .

def ES (arr :Array[Double], arr1 :Array[Double]) :Array[Double] = arr.zip(arr1).map(x => x._1 + x._2)

def ES1(arr :Array[Double], arr1 :Array[Double]) :Array[Double] = (arr,arr1).zipped.map((x,y) => x + y)

为了在速度方面比较这两种方法,我编写了以下代码:
def fun (arr : Array[Double] , arr1 : Array[Double] , f :(Array[Double],Array[Double]) => Array[Double] , itr : Int) ={
  val t0 = System.nanoTime()
  for (i <- 1 to itr) {
       f(arr,arr1)
       }
  val t1 = System.nanoTime()
  println("Total Time Consumed:" + ((t1 - t0).toDouble / 1000000000).toDouble + "Seconds")
}

我调用 fun方法和通行证ESES1如下:
fun(Array.fill(10000)(math.random), Array.fill(10000)(math.random), ES , 100000)
fun(Array.fill(10000)(math.random), Array.fill(10000)(math.random), ES1, 100000)

结果表明,该方法名为ES1使用 zipped比方法更快 ES使用 zip .
基于这些观察,我有两个问题。

为什么是 zippedzip 快?

有没有更快的方法对 Scala 中的集合进行元素操作?

最佳答案

回答你的第二个问题:

Is there any more faster way to do element wise operation on a collection in Scala?


可悲的事实是,尽管它简洁、提高了生产力和对错误的适应能力,但函数式语言不一定是最高效的。在 OP 的示例中,使用高阶函数来定义要针对集合执行的投影具有开销,并且紧密循环会放大这一点。正如其他人指出的那样,中间和最终结果的额外存储分配也会产生开销。
如果性能很重要,尽管绝不是通用的,在像您这样的情况下,您可以将简洁的功能代码展开回命令式等效代码,以便重新获得对内存使用的更直接控制并消除函数调用开销。
在您的具体示例中, zipped可以通过预先分配正确大小的固定可变数组来强制执行求和(因为当其中一个集合用完元素时 zip 停止),然后将适当索引处的元素添加在一起(因为按序数索引访问数组元素是一个非常快的操作)。
例如,添加第三个函数,ES3到您的测试套件:
def ES3(arr :Array[Double], arr1 :Array[Double]) :Array[Double] = {
   val minSize = math.min(arr.length, arr1.length)
   val array = Array.ofDim[Double](minSize)
   for (i <- 0 to minSize - 1) {
     array(i) = arr(i) + arr1(i)
   }
  array
}
在我的 i7 上,我得到以下响应时间:
OP ES Total Time Consumed:23.3747857Seconds
OP ES1 Total Time Consumed:11.7506995Seconds
--
ES3 Total Time Consumed:1.0255231Seconds
更令人发指的是对两个数组中较短的数组进行直接就地变异,这显然会破坏这个较短数组的内容,因此只有在不需要原始数组进行进一步工作时才应该实现调用者:
def ES4(arr :Array[Double], arr1 :Array[Double]) :Array[Double] = {
   val minSize = math.min(arr.length, arr1.length)
   val array = if (arr.length < arr1.length) arr else arr1
   for (i <- 0 to minSize - 1) {
      array(i) = arr(i) + arr1(i)
   }
  array
}

Total Time Consumed:0.3542098Seconds
但显然,直接改变作为参数传递的数组元素并不符合 Scala 的精神——这段代码有副作用。
老实说,如果您需要在紧密循环中进行这种级别的性能优化,那么最好用 Rust、Go 或 C 编写这些类型的算法。

https://stackoverflow.com/questions/59598239/

相关文章:

.net - ObservableCollection-无法实现,因为它没有匹配的返回类型

android - 修复了C代码中的错字,仍然无法构建

flash - 无法解析 to a component imple

xcode - 项目清理后,Cocos2d/box2d项目将无法编译

android - 使用PendingIntent的编译错误

ruby-on-rails - 使用Mysql在共享服务器中部署Rails应用程序时出错

python - 错误 : invalid mode ('r' ) - Cannot conduct

compilation - 在我的编译器中完全可以正常工作,但在将其提交到在线社区时会出现编译错误

winapi - InterlockedPushListSList丢失。它在哪里?

ruby-on-rails - SCSS编译错误