Si realmente se preocupan acerca de cómo escribir código de ultra alto rendimiento (y es posible que piensa que haces, pero será muy mal sobre esto) y luego clases de tipos van a causar algo de dolor por las siguientes razones:
- Muchos método virtual adicional llama
- boxeo probable de las primitivas (por ejemplo, si se utiliza clases de tipos de scalaz para monoides etc)
- creaciones de objetos a través de
def
que son necesarios porque las funciones no puede ser párrafo meterized
- creaciones objeto para acceder los métodos "pimped"
En tiempo de ejecución, la JVM puede optimizar algunas de las creaciones erróneas de distancia (por ejemplo, la creación de un MA
simplemente para llamar al <*>
), pero scalac
no ayuda mucho. Puede ver esto trivialmente compilando un código que usa clases de tipos y usando -Xprint:icode
como argumento.
He aquí un ejemplo:
import scalaz._; import Scalaz._
object TC {
def main(args: Array[String]) {
println((args(0).parseInt.liftFailNel |@| args(1).parseInt.liftFailNel)(_ |+| _))
}
}
y aquí está la icode:
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();
()
}
}
}
Se puede ver que hay una cantidad enorme de creación de objetos pasando aquí
Así que uno sugerencia que leí de su respuesta podría ser reemplazar 'scalaz.Monoid', que de hecho estoy usando, por una versión propia con spe cialización? Aunque la especialización parece ser muy defectuosa ... incluso 'Numeric' no está especializada, parece. – ziggystar
Me gustaría alejarme claramente de la especialización tbh. Si estuviera en tus zapatos, me gustaría estar * muy, muy seguro * de que tuve que exprimir hasta el último detalle del rendimiento del código.¿Qué te asegura que lo haces? Si algo tiene que estar cerca del metal, entonces diría que tendrás que volver a colecciones mutables, código imperativo y ciclos while. Realmente no hay otra respuesta –
No REALMENTE tiene que ser lo más rápido posible. Pero no quiero tomar el impacto en el desempeño del boxeo. Actualmente estoy usando matrices primitivas (sin mutarlas). Con respecto al problema, es posible mantener el código muy genérico para aplicarlo a un gran espacio problemático (incluso estoy usando anillos algebraicos como abstracción). Actualmente no quiero decidir entre el rendimiento (como el boxeo teniendo un éxito de 10x) y la abstracción; no es una opción fácil y me pregunto hasta dónde podría llegar con ambas. – ziggystar