Aplicaciones Numéricas que desafían los conceptos sobre el desarollo de lenguajes
David Cardozo
Algunas Carácteristicas de Julia
- Dinámico: Interactivo, exploratorio, "vivo"
- Just-in-time compilación con especialización agresiva
- Sistema poderoso de despacho múltiple.
- Metaprogramación al estilo Lisp y macros.
- Fácil de usar, "funciones generadas" implícitamente invocadas.
Algunas Carácteristicas de Julia
- Dinámico: Interactivo, exploratorio, "vivo"
- Just-in-time compilación con especialización agresiva
- Sistema poderoso de despacho múltiple.
- Diseño cauteloso de igualdad,orden, y hashing
- Metaprogramación al estilo Lisp y macros.
- Fácil de usar, "funciones generadas" implícitamente invocadas.
Algunas Carácteristicas de Julia
- Dinámico: Interactivo, exploratorio, "vivo"
- Just-in-time compilación con especialización agresiva
- Sistema poderoso de despacho múltiple.
- Diseño cauteloso de igualdad,orden, y hashing
- Metaprogramación al estilo Lisp y macros.
- Fácil de usar, "funciones generadas" implícitamente invocadas.
¿Que imprime el siguiente programa en C?
int main() {
long x = 1e16;
double y = 1e16;
long z = x+1;
printf("%d\n", x == y);
printf("%d\n", y == z);
printf("%d\n", x == z);
return 0;
}
Hasheo de números
Supongamos d es un diccionario
d[1] = "Adelante"
¿Que deberían devolver las siguientes búsquedas de diccionario?
d[1.0]
d[1 + 0im]
d[2//2]
¿¿¿???
¿¿¿???
¿¿¿???
Ruby
irb(main):001:0> d = {};
irb(main):002:0> d[1] = "Adelante"
=> "Adelante"
irb(main):003:0> d[1.0]
=> nil
Sin embargo
irb(main):001:0> 1 == 1.0;
=> true
Python
>>> d = {}
>>> d[1] = "Adelante"
>>> d[1.0]
'Adelante'
>>> from cmath import *
>>> d[1+0j]
Julia
d = Dict();
d[1] = "Adelante"
juliaa> d[1.0]
"Adelante"
julia> d[1 + 0im]
"Adelante"
julia> d[2//2]
"Adelante"
En el pasado...
En Julia 0.2 hasheamos números con diferentes tipos de manera diferente:
d = Dict()
d[1] = "Adelante"
d[1.0] = "ECOOP" # <- diferente entrada
julia> d
Dict{Any,Any} with 2 entries:
1.0 => "ECOOP"
1 => "Adelante"
Introduciendo Diccionarios tipeados
¿Que ocurre si limitamos los tipos del diccionario?
d = Dict{Int,String}()
d[1] = "Adelante"
d[1.0] = "ECOOP" # <- ¿Que debería hacer esto?
Dos opciones
- Lanzar un error - lo cual es nada conveniente o dinámico
- Automáticamente convertir 1.0 a Int
Mismo codigo trabaja diferente dependiendo del tipo de la llave
La Solución:
Hashear números por valor
hash(x::Float64) = 3h(x) + h(trunc(Int64, x))
hash(x::Int64) = 3h(convert(Float64, x)) + h(x)
Hasheo eficiente por valor para Float64 y Int64
h es alguna función para hashear 64 bits
- ¿Pero como podemos hacer esto eficientemente y genéricamente?
¿Y para otros tipos?
La función "descomposición":
La función descomposición es el punto donde (con valores reales) tipos numéricos que soportan hasheo se integran con el protocolo de hasheo
$$ num \cdot \frac{2^{pod}}{den} $$
Hashing es entonces definido genéricamente en términos de estos 3 valores
-
descomposicion(x) debería retornar tres valores enteros: num, pod, den
- num,pod, y den pueden ser computados eficientemente para varios tipos
- Caso especial (rápidos) para valores de Int64, UInt64 o Float64
Programación escalonada:
Permitir al programador generar código en varios puntos del proceso de compilación
Pipeline simplificado
Código
Fuente
AST
Lexing
Parsing
AST
tipeado
Inferencia de tipos
Código
nativo
Generación de
código
Macros
JIT
funciones generadas
Ejemplo macro
Evaluar
- i.e evaluación de polinomios mediante el método de Horner
macro horner(x, p...)
ex = esc(p[end])
for i = length(p)-1:-1:1
ex = :(muladd(t, $ex, $(esc(p[i]))))
end
Expr(:block, :(t = $(esc(x))), ex)
end
Desventajas de Macro
- Se coloca muy temprano
- Solo ve sintáxis, y no significado
- Cambia el orden de evaluación y posibles problemas de orden
@foo a + b
@foo b + a
- No trabaja como una función
i.e no se pueden usar macros como argumentos a funciones de alto nivel
Programación escalonada con tipos
- Generar codigo como función de los tipos de los argumentos
- Integración con el método de la cache
Generación de código automática
Guardada para reuso (GC)
El problema de la vista de Arrays
El problema de la vista de Arrays
El problema de la vista de Arrays
El problema de la vista de Arrays
El problema de la vista de Arrays
El Problema del "doble lenguaje"
Tener un lenguaje de alto nivel que se pueda trabajar interactivamente
= fácil desarrollo, prototipado, exploración
=> lenguaje dinámicamente tipeado
Mucho software de donde escoger: Python, Matlab / Octave, R, Scilab
Históricamente, no podemos escribir codigo crítico de rendimiento ("lazos internos") en estos lenguajes... debemos cambiar a C/Fortran/... (estáticos).
[e.g SciPy git master es ~70% C/C++/Fortran]
Trabajable, pero Python → Python+C = un gran salto en complejidad
¿Y si vectorizamos todo el código?
Esto implica en depender en librerías externas maduras operando en grandes bloques de datos para codigo crítico de rendimiento
¡Buen Consejo! Pero...
- Alguien tiene que escribir esas librerías
- Eventualmente esa persona podría ser usted
- Algunos problemas son imposibles o complicados de vectorizar.
Lenguajes Dinámicos no tienen que ser lentos
Mucho progreso en compiladores JIT gracias a aplicaciones web & JIT ofrecido por herramientas de software libre como LLVM
Javascript en navegadores modernos alcanza velocidades como C
Muchos esfuerzos, se han dado para aumentar la velocidad de lenguajes dinámicos, e.g PyPy, Numba / Cython
¿Que pasaría si un lenguaje dinámico fuera designado para JIT desde el principio, con el objetivo de ser alto-nivel mientras esté en la velocidad de C?
El Problema del "doble lenguaje"
(y es mucho más fácil llamar SciPy desde Julia que desde PyPy)
El Problema del "doble lenguaje"
- Dinámicamente tipeado
- Despacho múltiple: Una generalización de OO
- Metaprogramación (código que escribe código)
- Llamadas directas de funciones en C y Python
- Corutinas, y I/O asíncrona
- Diseñado para Unicode
- Tipos definidos por el usuario == tipos internos... extensibles, promoción y reglas de conversion
- Librería interna gigante: regex, álgebra lineal, funciones especiales, integración, etcétera
- Manejador de paquetes basado en git
Julia está escrito casi 70% en Julia
El Problema del "doble lenguaje"
¿Que tal sobre problemas reales comparado con código muy optimizado?
Funciones especializadas en Julia
funciones especiales s(x): problema clásico que no puede ser vectorizado... cambiar de varios polinomios que dependen en x
Muchas de las funciones especiales en Julia vienen de las librerías usuales C/Fortran pero algunas están escritas en Julia.
Julia erfinv(x)
3-4x mas rápido que Matlab y 2-3x más rápido que SciPy
Julia polygamma(m,z)
2 más rápido que SciPy (C/Fortran) para reales y además soporta argumento complejo
Funciones especializadas en Julia
Julia erfinv(x)
3-4x mas rápido que Matlab y 2-3x más rápido que SciPy
Julia polygamma(m,z)
2 más rápido que SciPy (C/Fortran) para reales y además soporta argumento complejo
Código de Julia puede ser incluso mas rápido que código optimizado de C/Fortran utilizando técnicas de metaprogramación que son difíciles en lenguajes de bajo nivel
Julia el diseño de un lenguaje
By David Cardozo
Julia el diseño de un lenguaje
Una pequeña introducción al diseño de lenguaje de Julia
- 1,475