2012-02-17 10 views
13

Actualmente estoy haciendo un uso extenso del patrón de clase de tipo en partes de mi código que son relevantes para el rendimiento. Descubrí al menos dos posibles fuentes de ineficiencia.¿Cuál es el impacto en el rendimiento del uso del patrón de clase de tipo en Scala

  1. Los parámetros implícitos se pasan por las llamadas de mensajes. No sé si esto realmente sucede. Tal vez Scalac puede simplemente insertar los parámetros implícitos donde se usan y eliminarlos de la firma del método. Probablemente esto no sea posible en los casos en que inserte los parámetros implícitos manualmente, ya que podrían resolverse solo en tiempo de ejecución. ¿Qué optimizaciones se aplican con respecto al paso de los parámetros implícitos?

  2. Si la instancia de clase tipo es proporcionado por una (al contrario de un val) def, el objeto tiene que ser recreada en cada llamada de un "método de tipo clasificado". Este problema puede ser abordado por la JVM, que podría optimizar la creación de objetos. Este problema también podría ser abordado por scalac mediante la reutilización de estos objetos. ¿Qué optimizaciones se aplican a la creación de objetos de parámetros implícitos?

Y por supuesto que puede haber otras fuentes de ineficiencia cuando se aplica el patrón de clase de tipo. Por favor háblame de ellos.

Respuesta

8

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í

+0

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

+0

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 –

+0

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

Cuestiones relacionadas