scala - studio - mpu6050 arduino tutorial
¿Qué puedo hacer con mi código de Scala para que se compile más rápido? (5)
Tengo una gran base de código de Scala. ( https://opensource.ncsa.illinois.edu/confluence/display/DFDL/Daffodil%3A+Open+Source+DFDL )
Es como 70K líneas de código de Scala. Estamos en Scala 2.11.7
El desarrollo se está poniendo difícil debido a la compilación: el ciclo edit-compile-test-debug es demasiado largo para pequeños cambios.
Los tiempos de recompilación incrementales pueden ser de un minuto, y esto sin la optimización activada. A veces más largo. Y eso con no haber editado muchos cambios en archivos. A veces un cambio muy pequeño provoca una gran recompilación.
Entonces, mi pregunta: ¿Qué puedo hacer para organizar el código, que mejorará el tiempo de compilación?
Por ejemplo, descomponer el código en archivos más pequeños? ¿Esto ayudará?
Por ejemplo, ¿bibliotecas más pequeñas?
¿Por ejemplo, evitar el uso de implicaciones? (tenemos muy pocos)
Por ejemplo, ¿evitar el uso de rasgos? (tenemos toneladas)
¿Por ejemplo, evitar muchas importaciones? (Tenemos toneladas: los límites de paquetes son bastante caóticos en este punto)
¿O realmente no hay mucho que pueda hacer al respecto?
Siento que esta compilación muy larga se debe de alguna manera a una cantidad inmensa de recompilación debido a las dependencias, y estoy pensando en cómo reducir las dependencias falsas ... pero eso es solo una teoría
Espero que alguien más pueda arrojar algo de luz sobre algo que podríamos hacer que mejoraría la velocidad de compilación para los cambios incrementales.
Aquí están las fases del compilador de Scala, junto con versiones ligeramente editadas de sus comentarios del código fuente. Tenga en cuenta que este compilador es inusual en su fuerte ponderación hacia la comprobación de tipos y las transformaciones que se parecen más a los desugarings. Otros compiladores incluyen una gran cantidad de código para: optimización, asignación de registros y traducción a IR.
Algunos puntos de nivel superior: hay una gran cantidad de reescritura de árboles. Cada fase tiende a leer en un árbol de la fase anterior y transformarla en un árbol nuevo. Los símbolos, en contraste, siguen siendo significativos a lo largo de la vida del compilador. Así que los árboles tienen punteros a los símbolos, y no al revés. En lugar de volver a escribir los símbolos, se les adjunta nueva información a medida que avanzan las fases.
Aquí está la lista de fases de Global:
analyzer.namerFactory: SubComponent,
analyzer.typerFactory: SubComponent,
superAccessors, // add super accessors
pickler, // serializes symbol tables
refchecks, // perform reference and override checking,
translate nested objects
liftcode, // generate reified trees
uncurry, // uncurry, translate function values to anonymous
classes
tailCalls, // replace tail calls by jumps
explicitOuter, // replace C.this by explicit outer pointers,
eliminate pattern matching
erasure, // erase generic types to Java 1.4 types, add
interfaces for traits
lambdaLift, // move nested functions to top level
constructors, // move field definitions into constructors
flatten, // get rid of inner classes
mixer, // do mixin composition
cleanup, // some platform-specific cleanups
genicode, // generate portable intermediate code
inliner, // optimization: do inlining
inlineExceptionHandlers, // optimization: inline exception handlers
closureElimination, // optimization: get rid of uncalled closures
deadCode, // optimization: get rid of dead cpde
if (forMSIL) genMSIL else genJVM, // generate .class files
un poco de trabajo con compilador Scala
Así, el compilador de Scala tiene que hacer mucho más trabajo que el compilador de Java, sin embargo, en particular, hay algunas cosas que hacen que el compilador de Scala sea drásticamente más lento, que incluye
- Resolución implícita . La resolución implícita (es decir, Scalac que trata de encontrar un valor implícito cuando realiza una declaración implícita) se refleja en cada ámbito principal de la declaración, este tiempo de búsqueda puede ser masivo (especialmente si hace referencia a la misma variable implícita muchas veces, y su declarado en alguna biblioteca a lo largo de su cadena de dependencia). El tiempo de compilación empeora aún más si se tienen en cuenta la resolución de rasgos implícita y las clases de tipo, que son utilizadas en gran medida por bibliotecas como scalaz y shapeless. También se usa una gran cantidad de clases anónimas (es decir, lambdas, bloques, funciones anónimas). Los Macros obviamente se agregan al tiempo de compilación.
Una muy buena reseña de Martin Odersky
Además, los compiladores de Java y Scala convierten el código fuente en un código de bytes JVM y hacen muy poca optimización. En la mayoría de los JVM modernos, una vez que se ejecuta el código de bytes del programa, se convierte en código de máquina para la arquitectura de la computadora en la que se está ejecutando. Esto se llama la compilación justo a tiempo. Sin embargo, el nivel de optimización del código es bajo con la compilación justo a tiempo, ya que tiene que ser rápido. Para evitar la recompilación, el llamado compilador HotSpot solo optimiza partes del código que se ejecutan con frecuencia.
Un programa puede tener un rendimiento diferente cada vez que se ejecuta. La ejecución del mismo fragmento de código (por ejemplo, un método) varias veces en la misma instancia de JVM puede dar resultados de rendimiento muy diferentes dependiendo de si el código en particular se optimizó entre las ejecuciones. Además, la medición del tiempo de ejecución de un fragmento de código puede incluir el tiempo durante el cual el compilador JIT estaba realizando la optimización, dando así resultados inconsistentes.
Una causa común de un deterioro en el rendimiento también es el boxeo y el desempaquetado que ocurren implícitamente cuando se pasa un tipo primitivo como argumento a un método genérico y también a GC frecuente.
Existen varios métodos para evitar los efectos anteriores durante la medición, como Debería ejecutarse utilizando la versión de servidor de HotSpot JVM, que realiza optimizaciones más agresivas. Visualvm es una excelente opción para crear perfiles de una aplicación JVM. Es una herramienta visual que integra varias herramientas JDK de línea de comando y capacidades de creación de perfiles ligeras. Sin embargo, las abstracciones de Scala son muy complejas y, lamentablemente, VisualVM todavía no admite este. son métodos de colecciones de Scala que llevan predicados, predicados a FOL y, por lo tanto, pueden pasar la secuencia completa maximizando el rendimiento.
También hacer que los módulos sean cooperativos y menos dependientes es una solución viable. Hay que tener en cuenta que la generación de códigos intermedios a veces depende de la máquina y que varias arquitecturas dan resultados variados.
Una alternativa : Typesafe ha lanzado Zinc que separa el compilador incremental rápido de sbt y permite que las herramientas de compilación / otras herramientas lo utilicen. Por lo tanto, usar Zinc con el plugin Scala Maven ha hecho que la compilación sea mucho más rápida.
Un problema simple: dada una lista de enteros, elimine el más grande. El pedido no es necesario.
A continuación se muestra la versión de la solución (un promedio, supongo).
def removeMaxCool(xs: List[Int]) = {
val maxIndex = xs.indexOf(xs.max);
xs.take(maxIndex) ::: xs.drop(maxIndex+1)
}
Es Scala idiomático, conciso, y utiliza algunas funciones de lista agradables. También es muy ineficiente. Atraviesa la lista al menos 3 o 4 veces.
Ahora considera esto, una solución similar a Java. También es lo que escribiría un desarrollador de Java razonable (o un novato en Scala).
def removeMaxFast(xs: List[Int]) = {
var res = ArrayBuffer[Int]()
var max = xs.head
var first = true;
for (x <- xs) {
if (first) {
first = false;
} else {
if (x > max) {
res.append(max)
max = x
} else {
res.append(x)
}
}
}
res.toList
}
Totalmente no-Scala idiomático, no funcional, no conciso, pero es muy eficiente. ¡Atraviesa la lista una sola vez!
Por lo tanto, las compensaciones también deben ser priorizadas y, a veces, es posible que tenga que trabajar cosas como un desarrollador de Java si no hay ninguna otra.
Además de mejoras menores en el código como (por ejemplo, anotaciones @tailrec
), dependiendo de qué tan valiente te sientas, también puedes jugar con Dotty que ofrece tiempos de compilación más rápidos entre otras cosas.
Algunas ideas que podrían ayudar, dependen de su caso y estilo de desarrollo:
- Use compilación incremental
~compile
en SBT o proporcionado por su IDE. - Use sbt-revolver y tal vez JRebel para recargar su aplicación más rápido. Más adecuado para aplicaciones web.
- Use TDD: en lugar de ejecutar y depurar todas las pruebas de escritura de la aplicación y solo ejecutarlas.
- Divide tu proyecto en bibliotecas / JARs. Utilícelos como dependencias a través de su herramienta de construcción: SBT / Maven / etc. O una variación de este próximo ...
- Divide tu proyecto en subproyectos (SBT). Compila por separado lo que se necesita o el proyecto raíz si lo necesitas todo. La compilación incremental todavía está disponible.
- Divide tu proyecto a microservicios.
- Espera a que Dotty resuelva tu problema hasta cierto punto.
- Si todo falla, no utilice las funciones avanzadas de Scala que hacen que la compilación sea más lenta: implicaciones, metaprogramación, etc.
- No olvide verificar que está asignando suficiente memoria y CPU para su compilador Scala. No lo he probado, pero quizás pueda usar un disco RAM en lugar de un disco duro para sus fuentes y compilar artefactos (fácil en Linux).
Está tocando uno de los principales problemas del diseño orientado a objetos (sobre ingeniería), en mi opinión, tiene que aplanar su jerarquía de rasgos de objeto de clase y reducir las dependencias entre clases. Frene los paquetes a diferentes archivos jar y utilícelos como mini bibliotecas que están "congeladas" y concéntrese en el nuevo código.
Vea también algunos videos de Brian Will, quien presenta un caso en contra de OO sobre ingeniería
es decir, https://www.youtube.com/watch?v=IRTfhkiAqPw (puede tomar los puntos positivos)
No estoy de acuerdo con él al 100%, pero es un buen caso contra el exceso de ingeniería.
Espero que ayude.
Puedes intentar usar el compilador Fast Scala .