Как влияет на производительность использование шаблона класса типов в Scala - PullRequest
13 голосов
/ 17 февраля 2012

В настоящее время я широко использую шаблон класса типов, чтобы быть частью моего кода, относящейся к производительности. Я разобрал как минимум два потенциальных источника неэффективности.

  1. Неявные параметры передаются по вызовам сообщений. Я не знаю, так ли это на самом деле. Возможно, scalac может просто вставить неявные параметры, где они используются, и удалить их из сигнатуры метода. Это, вероятно, невозможно в случаях, когда вы вставляете неявные параметры вручную, поскольку они могут быть разрешены только во время выполнения. Какие оптимизации применяются в отношении передачи неявных параметров ?

  2. Если экземпляр класса типа предоставляется def (в отличие от val), объект должен быть воссоздан при каждом вызове «метода с классом типа». Эта проблема может быть решена JVM, которая может оптимизировать создание объекта. Эта проблема также может быть устранена скалярным путем повторного использования этих объектов. Какие оптимизации применяются в отношении создания объектов неявных параметров?

И, конечно, могут быть дополнительные источники неэффективности при применении шаблона класса типов. Пожалуйста, расскажите мне о них.

1 Ответ

8 голосов
/ 17 февраля 2012

Если вы искренне заботитесь о написании сверхвысокопроизводительного кода (и вы можете думать , что вы делаете, но ошибаетесь в этом ), тогда классы типов причинят некоторую больследующие причины:

  • Множество дополнительных вызовов виртуальных методов
  • Вероятный бокс примитивов (например, при использовании классов типов скалаза для моноидов и т. д.)
  • Создание объектов с помощью defкоторые необходимы, потому что функции не могут быть параметризованы
  • Создание объектов для доступа к «скрытым» методам

Во время выполнения JVM может оптимизировать некоторые ошибочные создания (например, созданиеMA просто позвонить <*>), но scalac мало чем поможет.Вы можете увидеть это тривиально, скомпилировав некоторый код, который использует классы типов и используя -Xprint:icode в качестве аргумента.

Вот пример:

import scalaz._; import Scalaz._
object TC {
  def main(args: Array[String]) {
    println((args(0).parseInt.liftFailNel |@| args(1).parseInt.liftFailNel)(_ |+| _))
  }
}

А вот и код:

final object TC extends java.lang.Object with ScalaObject {
  def main(args: Array[java.lang.String]): Unit = scala.this.Predef.println(scalaz.this.Scalaz.ValidationMA(scalaz.this.Scalaz.StringTo(args.apply(0)).parseInt().liftFailNel()).|@|(scalaz.this.Scalaz.StringTo(args.apply(1)).parseInt().liftFailNel()).apply({
  (new anonymous class TC$$anonfun$main$1(): Function2)
}, scalaz.this.Functor.ValidationFunctor(), scalaz.this.Apply.ValidationApply(scalaz.this.Semigroup.NonEmptyListSemigroup())));
def this(): object TC = {
  TC.super.this();
  ()
}
};
@SerialVersionUID(0) final <synthetic> class TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$2 extends scala.runtime.AbstractFunction0 with Serializable {
  final def apply(): Int = TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$2.this.v1$1;
  final <bridge> def apply(): java.lang.Object = scala.Int.box(TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$2.this.apply());
  <synthetic> <paramaccessor> private[this] val v1$1: Int = _;
  def this($outer: anonymous class TC$$anonfun$main$1, v1$1: Int): anonymous class TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$2 = {
    TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$2.this.v1$1 = v1$1;
    TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$2.super.this();
    ()
  }
};
@SerialVersionUID(0) final <synthetic> class TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$1 extends scala.runtime.AbstractFunction0$mcI$sp with Serializable {
  final def apply(): Int = TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$1.this.apply$mcI$sp();
  <specialized> def apply$mcI$sp(): Int = TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$1.this.v2$1;
  final <bridge> def apply(): java.lang.Object = scala.Int.box(TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$1.this.apply());
  <synthetic> <paramaccessor> private[this] val v2$1: Int = _;
  def this($outer: anonymous class TC$$anonfun$main$1, v2$1: Int): anonymous class TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$1 = {
    TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$1.this.v2$1 = v2$1;
   TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$1.super.this();
  ()
  }
};
@SerialVersionUID(0) final <synthetic> class TC$$anonfun$main$1 extends scala.runtime.AbstractFunction2$mcIII$sp with Serializable {
  final def apply(x$1: Int, x$2: Int): Int = TC$$anonfun$main$1.this.apply$mcIII$sp(x$1, x$2);
  <specialized> def apply$mcIII$sp(v1$1: Int, v2$1: Int): Int = scala.Int.unbox(scalaz.this.Scalaz.mkIdentity({
(new anonymous class TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$2(TC$$anonfun$main$1.this, v1$1): Function0)
}).|+|({
    (new anonymous class TC$$anonfun$main$1$$anonfun$apply$mcIII$sp$1(TC$$anonfun$main$1.this, v2$1): Function0)
}, scalaz.this.Semigroup.IntSemigroup()));
final <bridge> def apply(v1: java.lang.Object, v2: java.lang.Object): java.lang.Object = scala.Int.box(TC$$anonfun$main$1.this.apply(scala.Int.unbox(v1), scala.Int.unbox(v2)));
  def this(): anonymous class TC$$anonfun$main$1 = {
    TC$$anonfun$main$1.super.this();
    ()
   }
 }

}

Вы видите, что здесь происходит огромное количество созданий объектов

...