Cómo el compilador JIT de Java mejora la velocidad de ejecución

Históricamente, los lenguajes siempre se habían dividido entre interpretados y compilados, en lo que se refiere a su ejecución. A día de hoy, con los compiladores JIT se crea una nueva categoría: los lenguajes que son una combinación de ambos. Los compiladores JIT son omnipresentes, por ejemplo los navegadores modernos disponen de uno para el lenguaje Javascript. También las máquinas virtuales de Java han evolucionado para incluir esta técnica, y es el caso concreto en el que se va a profundizar en este artículo.

Las implementaciones modernas de Java emplean un proceso de compilación dividido en dos pasos en el cual se produce una compilación en tiempo de ejecución, es decir, las máquinas virtuales de Java actuales disponen de un compilador JIT, just-in-time por sus siglas en inglés. En primer lugar, el compilador de Java compila en bytecode el código fuente, este bytecode es independiente de la plataforma de hardware donde se ejecuta. La máquina virtual de Java (JVM) es la encargada de ejecutar (interpretar mediante un intérprete) este bytecode. La máquina virtual sí es dependiente de la plataforma: Cada plataforma requiere de su propia máquina virtual para poder ejecutar el bytecode.

El compilador JIT de la máquina virtual es el encargado de detectar partes de este bytecode que se están ejecutando con mucha frecuencia y compilarlas al código máquina de la CPU de la plataforma para aumentar la velocidad de ejecución. El componente del compilador JIT encargado de detectar estos «puntos calientes» («hotspots» o «hot code» en inglés) del código es el profiler. Además, lo realiza en tiempo de ejecución, es decir, a medida que el programa se está ejecutando, detecta cuáles nuevas partes del código se han calentado y cuáles se han enfriado. De esta manera se supera la principal ineficiencia de los intérpretes: cuando código que se está ejecutando de forma reiterada (por ejemplo debido a un bucle while / for) debe ser interpretado una y otra vez. Además, esta característica es realmente potente, pues consigue que determinados tipos de programas se ejecuten más rápido que su equivalente en un lenguaje compilado orientado a objetos.

Para ver un caso concreto de cómo se optimiza en tiempo de ejecución un programa realizado en Java, pongamos a trabajar al ordenador: calculará 100 veces el número situado en la duodécima posición de la serie de Fibonacci, una forma como otra cualquiera de poner a calcular un ordenador. Este número es el 144, pero lo que realmente nos interesa es el tiempo transcurrido en cada una de las 100 ejecuciones.

fibonacci en Java Sigue leyendo

Estructura de álgebra de Boole

En una entrada anterior publicada hace más de 4 años, en primer lugar se explicaba cómo simplificar expresiones booleanas en nuestro código a partir de las propiedades fundamentales o axiomas del álgebra de Boole y de sus propiedades derivadas, así como a partir del complemento de una función booleana. Finalmente, se añadía una nota en la que se comentaba la similitud entre el álgebra de Boole, la lógica de primer orden y la teoría básica de conjuntos (la formulada por Georg Cantor). Con el presente artículo se pretender dar orden y exponer correctamente el porqué de está relación que se dejó caer en forma de una breve nota.

En su breve obra titulada «El análisis matemático de la lógica» publicada en 1847, el matemático británico autodidacta George Boole tuvo la originalidad de utilizar las técnicas algebraicas para tratar expresiones de la lógica. En su sistema se definen unas operaciones sobre unas variables abstractas que tienen que cumplir unas propiedades, de forma similar a como en el álgebra de las fracciones se definen las operaciones de suma, resta, multiplicación y división y deben cumplir unas determinadas propiedades. Con esta obra puso fin a la lógica aristotélica e inició la lógica formal matemática contemporánea.

No obstante, no fue hasta 1860 que en los trabajos del también británico economista y lógico William Jevons y el filósofo, lógico y matemático norteamericano Charles Sanders Peirce apareció el concepto más general de álgebra de Boole. Un álgebra de Boole es una estructura algebraica, es decir, consta de un conjunto no vacío de elementos y un conjunto no vacío de operaciones sobre dicho conjunto. Además, una estructura algebraica es axiomática, es decir, debe cumplir una serie de propiedades.

William Jevons

William Jevons, principalmente conocido por la paradoja de Jevons.

Sigue leyendo

La simplificación de la complejidad de la arquitectura de las aplicaciones web

La nueva generación de javascripters cree que inventó justo ayer la programación web, pero resulta que hace 25 años ya se libraba la misma batalla que libran ahora, sólo que exclusivamente con LAMP. A Docker y Kubernetes no se llegó precisamente de sopetón… La solución a los problemas fue aumentar la complejidad, lo que a su vez creó nuevos problemas, esta situación y su evolución se describe en este breve artículo de Ben Johnson de lectura muy recomendable.

Asimismo, propone una solución en la que se deshace gran parte de la complejidad añadida durante más de 2 décadas. Esta solución consiste en aumentar la capacidad de un servidor en vez de aumentar el número de los mismos y emplear… ¡el denostado SQLite! (Al menos denostado para las aplicaciones web) Hoy en día Amazon, a través de AWS, ofrece servidores con 96 núcleos y cientos de gigas de RAM. En un servidor bastante más grande, pero dentro de lo que puede administrar un kernel de Linux, se ha conseguido con SQLite la friolera de 4 millones de consultas SQL por segundo con un solo hilo.

El inconveniente de SQLite, es que a diferencia de los sistemas gestores de bases de datos relacionales como MySQL u Oracle, no tiene capacidades de red, con lo que no hay replicación de los datos a otros servidores, entre otros inconvenientes, por lo que si el servidor revienta, se perdieron los datos desde la última copia de seguridad realizada. Para solucionar este problema, Ben ha creado Litestream, una herramienta que se encarga de replicar continuamente una base de datos SQLite en un Amazon S3 (un servicio de almacenamiento en la nube).

Curiosa e interesante iniciativa, que lleva el principio KISS (Keep it simple, stupid!) al extremo. A ver cómo evoluciona…

Difícilmente habrá inmunización cuando empiece el verano

En los últimos meses se ha vendido la esperanza de que una vez llegue la vacuna esta pandemia será historia, a más tardar llegado el verano, pero esta idea parece que no pasa de las buenas intenciones si hacemos un elemental cálculo matemático. A continuación, se verá el caso concreto de España pero fácilmente podrá el lector extrapolarlo a su país.

vacunas covid-19 Sigue leyendo

¿Qué representa el producto escalar?

En un artículo anterior acerca del producto escalar, se explicó detalladamente cómo se define matemáticamente esta operación. En la presente entrada se explicará qué representa realmente esta operación entre dos vectores, la noción «intuitiva» del mismo; algo imprescindible para entender qué estamos calculando realmente en otras ciencias (por ejemplo física) cuando es empleado.

Dados dos vectores, desde un punto de vista estrictamente matemático podríamos definir infinitas operaciones con ellos, por ejemplo el «producto payaso» de los vectores de n dimensiones u y v, se denota mediante u 🤡 v y se define así:

u 🤡 v = (u1·vn·|u|, u2·vn-1·|v|, u3·vn-2·|u|, …, un·v1·|v|)

Donde las coordenadas impares se multiplican por |u| y las pares por |v|.

Entonces, ¿por qué la operación conocida como producto escalar es importante? Un profesor de matemáticas podría responder a esta pregunta de sus alumnos afirmando que entenderán su importancia en la asignatura de física, donde es muy usado, pues tal vez en matemáticas no tenga un valor especial, pero esta respuesta es un tanto esquiva. La razón por la que es importante en física es precisamente por su interpretación geométrica (ergo matemática), pero esta sistemáticamente se omite, de manera que en física los alumnos deben resignarse a aplicarlo ciegamente.

La explicación a grosso modo no debería rehuirse pues no es ningún concepto especialmente complejo ni requiere de matemáticas «superiores»: el producto escalar de dos vectores, u·v, expresa «cuánto» de u «descansa» en la dirección de v, escalado al tamaño de v. (Esto ya se expresó sucintamente en el artículo anterior) Una vez expuesto esto, resulta mucho más intuitivo entender porqué en física se emplea para:

El trabajo

En la mítica representación del trabajo en Bachillerato:

trabajo en fisica

Sigue leyendo