¿Vale la pena cambiar de Python a Nim por rendimiento?

Nim es una mezcla de sintaxis de Python y rendimiento de C







Hace unas semanas estaba navegando en GitHub y encontré un repositorio curioso: el proyecto fue escrito completamente en Nim . No me había encontrado con él antes, y esta vez decidí averiguar qué tipo de animal era.



Al principio pensé que estaba atrasado, que este es uno de los lenguajes de programación comunes que muchos, a diferencia de mí, usan activamente. Y luego decidí estudiarlo.



Aquí están mis conclusiones:



  • Este idioma es realmente popular entre un círculo reducido de personas.
  • Quizás debería ser así.


Entonces, les contaré un poco sobre mi experiencia con Nim, hablaré brevemente sobre las características de la programación en él, y también intentaré compararlo con Python y C. De cara al futuro, noto que este lenguaje me parece muy prometedor.



¡Código en el estudio!



Como ejemplo, decidí escribir algo más complejo en Nim que hola, mundo:







parece nada superfluo, ¿verdad? Parece tan simple que puedes descubrir fácilmente qué hace, incluso si nunca antes has oído hablar de Nim. (El programa generará: "num: 5 i: 5")



Entonces, analicemos lo que nos parece familiar de alguna parte.



Declaración de variable



Esto es dolorosamente familiar para los desarrolladores de JavaScript. Mientras que algunos lenguajes usan var y otros usan let, JS y Nim le permiten usar ambos al declarar variables. Sin embargo, es importante tener en cuenta que funcionan de manera diferente en Nim que en JS. Pero más sobre eso más adelante.



Bloques



Para denotar un nuevo bloque en Nim, usamos dos puntos seguidos de una línea con sangría. Todo es como en Python.



Palabras clave



Ambos bucles y la instrucción if parecen ser una parte del código Python. De hecho, todo desde la línea 5 en adelante es código Python (asumiendo que tenemos la función echo definida).



Así que sí, muchas palabras clave y operadores de Python también se pueden usar en Nim: not, is, and, or, y así sucesivamente.



Es decir, hasta el momento no vemos nada especial en Nim: la peor versión de Python (en términos de

sintaxis), teniendo en cuenta el hecho de que necesitas usar let o var para declarar variables.



Podríamos detenernos allí, pero hay un gran "pero": Nim es un lenguaje escrito de forma estática que funciona casi tan rápido como el lenguaje C.



Bueno, ahora para otra conversación. Vamos a ver.



Prueba de desempeño







Antes de sumergirnos en la sintaxis de Nim (especialmente la parte de tipo estático que no hemos visto hasta ahora), intentemos evaluar su rendimiento. Para hacer esto, escribí una implementación ingenua para calcular el número n de Fibonacci en Nim, Python y C.



Para mantener las cosas justas, estandaricé la implementación basada en la solución Leetcode (Opción 1) y traté de ceñirme a ella lo más estrictamente posible en los tres idiomas.



Por supuesto, puedes recordarme sobre LRU Cache . Pero por ahora, mi tarea es utilizar un enfoque estándar y no intentar optimizar los cálculos. Así que elegí una implementación ingenua.


Aquí están los resultados para calcular el número 40 de Fibonacci:







Sí, estrictamente hablando, el experimento no se puede llamar limpio, pero esto se correlaciona con los resultados de otros entusiastas que hicieron pruebas más serias [1] [2] [3] .



Todo el código que escribí para este artículo está disponible en GitHub, incluidas las instrucciones sobre cómo ejecutar este experimento.



Entonces, ¿por qué Nim es mucho más rápido que Python?



Bueno, diría que hay dos razones principales:



  1. Nim es un lenguaje compilado y Python es un lenguaje interpretado (más sobre eso aquí ). Esto significa que se realiza más trabajo cuando se ejecuta un programa Python porque el programa debe interpretarse antes de que pueda ejecutarse. Esto generalmente hace que el idioma sea más lento.
  2. Nim está tipado estáticamente. Aunque no había ninguna declaración de tipo en el ejemplo que mostré anteriormente, veremos más adelante que de hecho es un lenguaje escrito estáticamente. En el caso de Python, que se escribe dinámicamente, el intérprete tiene mucho más trabajo que hacer para definir y manejar los tipos de manera adecuada. También reduce el rendimiento.


La velocidad del trabajo aumenta, la velocidad de codificación disminuye.



Esto es lo que dice Python Docs sobre los lenguajes interpretados:

« / , , ».


Esta es una buena generalización de la compensación entre Python y C, por ejemplo. Cualquier cosa que pueda hacer en Python, puede hacerlo en C, pero su programa en C se ejecutará muchas veces más rápido.



Pero pasará mucho más tiempo escribiendo y depurando su código C, será difícil de manejar y menos legible. Y es por eso que C ya no tiene demanda y Python es popular. En otras palabras, Python es mucho más simple (comparativamente, por supuesto).



Entonces, si Python está en un extremo del espectro y C está en el otro, entonces Nim está tratando de llegar a algún punto intermedio. Es mucho más rápido que Python, pero no tan difícil de programar como C.



Echemos un vistazo a nuestra implementación del cálculo de números de Fibonacci.



DESDE:



#include <stdio.h>
int fibonacci(int n) {
    if (n <= 1) {
        return n;
    } 
    return fibonacci(n-1) + fibonacci(n-2);
}

int main(void) {
    printf("%i", fibonacci(40));
}


Pitón:



def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(40))


Nim:



proc fibonacci(n: int): int = 
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

echo(fibonacci(40))


Aunque Nim usa el signo "=" en su sintaxis de procedimiento (función), en general es mucho más fácil escribir código que en C. ¿



Quizás esto sea realmente una compensación valiosa? Un poco más difícil de escribir que Python, pero funciona diez veces más rápido. Podría vivir con eso.



Sintaxis de Nim



import strformat

#    https://nim-lang.org/

type
  Person = object
    name: string
    age: Natural #      

let people = [
  Person(name: "John", age: 45),
  Person(name: "Kate", age: 30)
]

for person in people:

  echo(fmt"{person.name} is {person.age} years old")


Solo señalaré las características clave.



Variables



Usamos var, let o const para declarar variables.



var y const funcionan de la misma manera que en JavaScript, pero es una historia diferente.



JavaScript let difiere de var en términos de alcance, y Nim let denota una variable cuyo valor no puede cambiar después de la inicialización. Me parece Swift.



¿Pero no es eso lo mismo que una constante? - usted pregunta.



No. En Nim, la diferencia entre const y let es la siguiente:

Para const, el compilador debe poder determinar el valor en tiempo de compilación, mientras que para let se puede determinar en tiempo de ejecución.


Ejemplo de la documentación:



const input = readLine(stdin) # Error: constant expression expected
let input = readLine(stdin)   #  


Alternativamente, las variables se pueden declarar e inicializar así:



var
   a = 1
   b = 2
   c = 3
   x, y = 10 #   x  y   10


Funciones



Las funciones en Nim se denominan procedimientos:



proc procedureName(parameterName: parameterType):returnType =
   return returnVar


Dado que el lenguaje es similar a Python en muchos aspectos, los procedimientos parecen un poco extraños cuando los ve por primera vez.



Usar "=" en lugar de "{" o ":" es claramente confuso. Todo es un poco mejor escribiendo el procedimiento en una línea:



proc hello(s: string) = echo s


También puede obtener el resultado de la función:



proc toString(x: int): string =
   result =
       if x < 0: “negative”
       elif x > 0: “positive”
       else: “zero”


Parece que todavía necesita devolver el resultado de alguna manera, pero en este caso, el resultado no es una variable, es una palabra clave. Entonces, el fragmento de código anterior es correcto desde el punto de vista de Nim.



También puede sobrecargar procedimientos:




proc toString(x: int): string =   
    result =     
        if x < 0: "negative"     
        elif x > 0: "positive"     
        else: "zero"  
proc toString(x: bool): string =   
    result =     
        if x: "yep"     
        else: "nope"
echo toString(true) #  "yep"
echo toString(5) #  "positive"


Condiciones y ciclos



Tiene mucho que ver con Python.



# if true:

# while true:

# for num in nums:


Para iterar sobre una lista, por ejemplo, en lugar de range (), puede usar countup (inicio, fin) o countdown (inicio, fin) . Puede hacerlo aún más fácil y usar para i en start..finish



Entrada y salida del usuario



let input = readLine(stdin)
echo input


En comparación con Python, readLine (stdin) es equivalente a input () y echo es equivalente a print.



echo se puede utilizar con o sin paréntesis.



Mi objetivo es brindarle una comprensión básica de Nim, no volver a contar toda su documentación. Así que termino con la sintaxis y paso a otras características del lenguaje.



Características adicionales



Programación orientada a objetos



Nim no es un lenguaje orientado a objetos, pero proporciona un soporte mínimo para trabajar con objetos . Por supuesto, está lejos de las clases de Python.



Macros



Nim admite macros y metaprogramación, y los desarrolladores parecen poner mucho énfasis en esto. Este es el tema de su propia sección de la serie de tres lecciones.



Pequeño ejemplo:



import macros  macro myMacro(arg: static[int]): untyped =  
   echo arg

myMacro(1 + 2 * 3)


Tipos de datos básicos



string, char, bool, int, uint  float.


También puede utilizar estos tipos:



int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64


Además, las cadenas en Nim son tipos mutables, a diferencia de Python.



Comentarios



A diferencia de Python, Nim usa el carácter "#" en combinación con "[" y "]" para comentarios de varias líneas.



# a comment#[
a
multi
line
comment
]#


Compilar JavaScript



Nim puede traducir su código a JavaScript. No estoy seguro si mucha gente viene a usar esto. Pero hay un ejemplo de un juego de navegador de serpientes escrito en Nim.



Iteradores



Los iteradores de Nim se parecen más a los generadores de Python:



iterator countup(a, b: int): int =
   var res = a
   while res <= b:
       yield res
       inc(res)


Sensibilidad a

mayúsculas y minúsculas y subrayado Nim solo distingue entre mayúsculas y minúsculas para el primer carácter.



Es decir, distingue HelloWorld y helloWorld, pero no helloWorld, helloworld y hello_world. Por tanto, el siguiente procedimiento funcionará sin problemas, por ejemplo:



proc my_func(s: string) =
   echo myFunc("hello")


¿Qué tan popular es Nim?







Nim tiene casi 10,000 estrellas en GitHub. Esta es una clara ventaja. Sin embargo, traté de estimar la popularidad del idioma a partir de otras fuentes y, por supuesto, no es tan alta.



Por ejemplo, Nim ni siquiera se mencionó en la Encuesta de desbordamiento de pila de 2020 . No pude encontrar trabajos de desarrollador de Nim en LinkedIn (incluso con geografía mundial) y una búsqueda de [nim-lang] en Stack Overflow solo arrojó 349 preguntas (en comparación con ~ 1,500,000 para Python o 270,000 para Swift)



. Por lo tanto, sería justo asumir que la mayoría de los desarrolladores no lo usaron y muchos nunca han oído hablar del lenguaje Nim.



¿Reemplazando Python?



Para ser honesto, creo que Nim es un lenguaje muy bueno. Para escribir este artículo, estudié el mínimo requerido, pero eso fue suficiente. Aunque no lo he profundizado demasiado, planeo usar Nim en el futuro. Personalmente, soy un gran admirador de Python, pero también me gustan los lenguajes de tipado estático. Entonces, para mí, la mejora del rendimiento en algunos casos compensa con creces la ligera redundancia sintáctica.



Si bien la sintaxis básica es muy similar a la de Python, es más compleja. Por lo tanto, la mayoría de los fanáticos de Python probablemente no estarán interesados ​​en él.



Además, no se olvide del idioma Go. Estoy seguro de que muchos de ustedes han pensado en esto mientras leían, y con razón. A pesar de que la sintaxis de Nim se acerca más a la sintaxis de Python, en términos de rendimiento compite precisamente con lenguajes como "C ++ simplificado".

Una vez probé el rendimiento de Go. En particular, para Fibonacci (40), funcionó tan rápido como C.


Pero aún así: ¿puede Nim competir con Python? Lo dudo mucho. Estamos viendo una tendencia hacia el aumento del rendimiento de las computadoras y la simplificación de la programación. Y, como señalé, incluso si Nim ofrece una buena compensación de sintaxis / rendimiento, no creo que sea suficiente para vencer a Python puro y versátil.

Hablé con uno de los desarrolladores de Nim Core. Él cree que Nim es más adecuado para quienes cambian de C ++ que para los pitonistas.


¿Puede Nim competir con Go? Posiblemente (si Google "lo permite"). El lenguaje Nim es tan poderoso como Go. Además, Nim tiene un mejor soporte para las funciones de C / C ++, incluidas las macros y la sobrecarga.



Pero más sobre eso la próxima vez.






Publicidad



Los servidores Epic son servidores virtuales asequibles con procesadores de AMD, frecuencia de núcleo de CPU de hasta 3,4 GHz. La configuración máxima le permitirá llegar al máximo: 128 núcleos de CPU, 512 GB de RAM, 4000 GB de NVMe. ¡Date prisa para hacer tu pedido!






All Articles