Scala

ScalaでのNumericの動作を教えてもらったのでベクトル計算のベンチマークをAO Benchでとってみた。
ソースはgithubhttps://gist.github.com/1029695

イミュータブル

素直に数式を書けるように定義してみる。

case class Vec3d(x:Double, y:Double, z: Double) {
  def +(v: Vec3d) = Vec3d(x + v.x, y + v.y, z + v.z)
  def -(v: Vec3d) = Vec3d(x - v.x, y - v.y, z - v.z)
}

def ray_sphere_intersect(isect: Isect, ray: Ray, sphere: Sphere) {
  val rs = ray.org - sphere.center

  val B = rs * ray.dir
  val C = rs * rs - sphere.radius * sphere.radius
  val D = B * B - C
  …
}
$ scala -J-server jp.peppermint.vec.immutable.App
List(9494, 9508, 9448, 9471, 9445)
9471.0

Java版(http://leonardo-m.livejournal.com/79346.html)の半分くらいの実行速度。

ミュータブル

2オペランドのような感じで中間オブジェクトの生成を抑えてみる。

class Vec3d(var x:Double = 0, var y:Double = 0, var z: Double = 0) {
  def this(v: Vec3d) =
    this(v.x, v.y, v.z)

  def +=(v: Vec3d) {
    x += v.x
    y += v.y
    z += v.z
  }
...
}

def ray_sphere_intersect(isect: Isect, ray: Ray, sphere: Sphere) {
  val rs = new Vec3d(ray.org)
  rs -= sphere.center

  val B = rs * ray.dir
  val C = rs * rs - sphere.radius * sphere.radius
  val D = B * B - C
...
$ scala -J-server jp.peppermint.vec.mutable.App
List(3846, 3853, 3854, 3833, 3840)
3846.3333333333335

ほぼJava版と同じ。

イミュータブル ジェネリック

ベクトルの要素を型パラメータで変えられるように。

case class Vec3[T](x: T, y: T, z: T)(implicit num: Fractional[T], real: Real[T]) {
  import num._
  import real._

  def +(v: Vec3[T]) = Vec3[T](x + v.x, y + v.y, z + v.z)
  def -(v: Vec3[T]) = Vec3[T](x - v.x, y - v.y, z - v.z)
$ scala -J-server jp.peppermint.vec.immutable.gen.App
List(20906, 20901, 20805, 21094, 21006)
20937.666666666668

だいぶ遅い。ボクシング・暗黙の型変換でのラッパクラス生成・interface経由のメソッドコールあたりが原因かな?

まとめ

プリミティブ型もジェネリックにできるとはいえレンダラの中でループぶんまわす用途には向かない様子。イミュータブルにするのは記述性や汎用性とのトレードオフでありかもしれない。

追記

64bit JVMでは特性が違うようなので後で調査