En primer lugar, me gustaría dejar en claro que esta no es una pregunta de lenguaje X versus lenguaje Y para determinar cuál es mejor.
He estado usando Java durante mucho tiempo y tengo la intención de seguir usándolo. Paralelo a esto, actualmente estoy aprendiendo Scala con gran interés: aparte de cosas menores que me llevan un tiempo acostumbrarme a mi impresión, es que realmente puedo trabajar muy bien en este idioma.
Mi pregunta es: ¿cómo se compara el software escrito en Scala con el software escrito en Java en términos de velocidad de ejecución y consumo de memoria? Por supuesto, esta es una pregunta difícil de responder en general, pero esperaría que las construcciones de nivel superior, como la coincidencia de patrones, las funciones de orden superior, etc., introduzcan algunos gastos generales.
Sin embargo, mi experiencia actual en Scala se limita a pequeños ejemplos bajo 50 líneas de código y no he ejecutado ningún punto de referencia hasta ahora. Entonces, no tengo datos reales.
Si resultó que Scala tiene algo de sobrecarga de Java, ¿tiene sentido tener proyectos mixtos de Scala / Java, donde uno codifica las partes más complejas en Scala y las partes críticas para el rendimiento en Java? ¿Es esta una práctica común?
EDITAR 1
He ejecutado un pequeño punto de referencia: construya una lista de enteros, multiplique cada entero por dos y colóquelo en una nueva lista, imprima la lista resultante. Escribí una implementación de Java (Java 6) y una implementación de Scala (Scala 2.9). He ejecutado ambos en Eclipse Indigo en Ubuntu 10.04.
Los resultados son comparables: 480 ms para Java y 493 ms para Scala (promedio de más de 100 iteraciones). Aquí están los fragmentos que he usado.
// Java
public static void main(String[] args)
{
long total = 0;
final int maxCount = 100;
for (int count = 0; count < maxCount; count++)
{
final long t1 = System.currentTimeMillis();
final int max = 20000;
final List<Integer> list = new ArrayList<Integer>();
for (int index = 1; index <= max; index++)
{
list.add(index);
}
final List<Integer> doub = new ArrayList<Integer>();
for (Integer value : list)
{
doub.add(value * 2);
}
for (Integer value : doub)
{
System.out.println(value);
}
final long t2 = System.currentTimeMillis();
System.out.println("Elapsed milliseconds: " + (t2 - t1));
total += t2 - t1;
}
System.out.println("Average milliseconds: " + (total / maxCount));
}
// Scala
def main(args: Array[String])
{
var total: Long = 0
val maxCount = 100
for (i <- 1 to maxCount)
{
val t1 = System.currentTimeMillis()
val list = (1 to 20000) toList
val doub = list map { n: Int => 2 * n }
doub foreach ( println )
val t2 = System.currentTimeMillis()
println("Elapsed milliseconds: " + (t2 - t1))
total = total + (t2 - t1)
}
println("Average milliseconds: " + (total / maxCount))
}
Entonces, en este caso, parece que la sobrecarga de Scala (usando rango, mapa, lambda) es realmente mínima, lo que no está lejos de la información proporcionada por World Engineer.
¿Quizás hay otras construcciones Scala que deberían usarse con cuidado porque son particularmente pesadas de ejecutar?
EDITAR 2
Algunos de ustedes señalaron que las impresiones en los bucles internos ocupan la mayor parte del tiempo de ejecución. Los eliminé y establecí el tamaño de las listas en 100000 en lugar de 20000. El promedio resultante fue de 88 ms para Java y 49 ms para Scala.
fuente
Respuestas:
Hay una cosa que puede hacer de manera concisa y eficiente en Java que no puede hacer en Scala: las enumeraciones. Para todo lo demás, incluso para construcciones que son lentas en la biblioteca de Scala, puede obtener versiones eficientes trabajando en Scala.
Por lo tanto, en su mayor parte, no necesita agregar Java a su código. Incluso para el código que usa la enumeración en Java, a menudo hay una solución en Scala que es adecuada o buena: pongo la excepción en las enumeraciones que tienen métodos adicionales y cuyos valores constantes int se usan.
En cuanto a qué tener cuidado, aquí hay algunas cosas.
Si usa el patrón enriquecer mi biblioteca, siempre convierta a una clase. Por ejemplo:
Tenga cuidado con los métodos de recolección: debido a que son polimórficos en su mayor parte, JVM no los optimiza. No necesita evitarlos, pero preste atención en las secciones críticas. Tenga en cuenta que
for
en Scala se implementa a través de llamadas a métodos y clases anónimas.Si usa una clase Java, como
String
,Array
oAnyVal
clases que corresponden a primitivas Java, prefiera los métodos proporcionados por Java cuando existan alternativas. Por ejemplo, uselength
onString
y enArray
lugar desize
.Evite el uso descuidado de conversiones implícitas, ya que puede encontrarse usando conversiones por error en lugar de por diseño.
Extiende las clases en lugar de los rasgos. Por ejemplo, si está extendiendo
Function1
, extienda en suAbstractFunction1
lugar.Uso
-optimise
y especialización para obtener la mayor parte de Scala.Entiende lo que está sucediendo:
javap
es tu amigo, y también lo son un montón de banderas Scala que muestran lo que está sucediendo.Los modismos de Scala están diseñados para mejorar la corrección y hacer que el código sea más conciso y fácil de mantener. No están diseñados para la velocidad, por lo que si necesita usarlos en
null
lugar deOption
en una ruta crítica, ¡hágalo! Hay una razón por la cual Scala es multi-paradigma.Recuerde que la verdadera medida del rendimiento es ejecutar código. Vea esta pregunta para ver un ejemplo de lo que puede suceder si ignora esa regla.
fuente
Según el Benchmarks Game para un sistema de núcleo único de 32 bits, Scala tiene una mediana del 80% más rápido que Java. El rendimiento es aproximadamente el mismo para una computadora Quad Core x64. Incluso el uso de la memoria y la densidad del código son muy similares en la mayoría de los casos. Diría que, en base a estos análisis (bastante poco científicos), usted tiene razón al afirmar que Scala agrega algo de sobrecarga a Java. No parece agregar toneladas de sobrecarga, por lo que sospecho que el diagnóstico de artículos de orden superior que ocupan más espacio / tiempo es el más correcto.
fuente
Int
,Char
, etc., cuando se puede. Mientras que los bucles son tan eficientes en Scala.Function
clases. Si pasa una lambda amap
, la clase anónima necesita ser instanciada (y algunos locales pueden necesitar ser pasados), y luego cada iteración tiene una sobrecarga de llamadas a funciones adicionales (con algún paso de parámetros) de lasapply
llamadas.scala.util.Random
son solo envoltorios alrededor de clases JRE equivalentes. La llamada de función adicional es un poco inútil.java.lang.Math.signum(x)
es mucho más directo quex.signum()
, lo que convierte aRichInt
y de regreso.fuente
reduce el tiempo de 800 ms a 20 en mi sistema.
fuente