用于矢量化数值计算的最佳Scala集合类型

时间:2012-12-05 15:17:02

标签: api scala scala-collections numerical-computing

在设计特定于域的数字计算库时,寻找适当的数据类型(例如IndexedSeq[Double])。对于这个问题,我将范围限制在使用Double的1维数组。该库将定义一些通常应用于一维数组中每个元素的函数。

考虑:

  • 首选不可变数据类型,例如VectorIndexedSeq
  • 希望最大限度地减少数据转换
  • 空间和时间合理有效
  • 对使用图书馆的其他人友好
  • 优雅而干净的API

我应该在集合层次结构中使用更高级的内容,例如Seq

或者更好的方法是定义单元素函数并将映射/迭代留给最终用户?

这似乎效率较低(因为一些计算可以在每组调用中完成一次),但同时也是一个更灵活的API,因为它适用于任何类型的集合。

有什么建议吗?

2 个答案:

答案 0 :(得分:11)

如果您的计算要远程执行计算密集型操作,请使用Array生成或包装在您自己的类中。您可以提供与集合兼容的包装器,但只能使其成为互操作性的显式包装器。 Array以外的所有内容都是通用的,因此是盒装的,因此相对较慢且笨重。

如果你不使用Array,人们就会被迫放弃你拥有的任何东西,而只是在表现很重要时使用Array。也许那没关系;也许你想要计算是为了方便而不是效率。在这种情况下,我建议使用IndexedSeq作为接口,假设您想让人们知道索引不是非常慢(例如不是List),并且使用Vector下的引擎盖。您将使用比Array[Double]多4倍的内存,并且对于大多数低效操作(例如乘法)来说,速度要慢3到10倍。

例如,这个:

val u = v.map(1.0 / _)   //  v is Vector[Double]

比这慢三倍:

val u = new Array[Double](v.length)
var j = 0
while (j<u.length) {
  u(j) = 1.0/v(j)      // v is Array[Double]
  j += 1
}

如果您在map上使用Array方法,则它与Vector[Double]方式一样慢; Array上的操作是通用的,因此装箱。 (这就是大部分惩罚来自的地方。)

答案 1 :(得分:3)

当我处理数值时,我一直在使用Vectors,因为它提供了非常有效的随机访问以及追加/前置。

另请注意,不可变索引序列的当前默认集合是Vector,因此如果编写类似for (i <- 0 until n) yield {...}的代码,则返回IndexedSeq[...],但运行时类型为Vector。因此,总是使用Vectors可能是个好主意,因为一些采用两个序列作为输入的二元运算符可能会受益于两个参数具有相同实现类型的事实。 (现在情况并非如此,但是有人指出,由于第二个参数被简单地视为一般序列,因此向量级联可以在log(N)时间内,而不是当前的线性时间。)< / p>

尽管如此,我相信Seq[Double]应该已经提供了您需要的大多数功能接口。由于Range的映射结果不直接产生Vector,我通常将Seq[Double]作为参数类型作为我的输入,因此它具有一定的通用性。我希望在底层实现中优化效率。

希望有所帮助。