Objeto Phyton en Mojo

Andres Felipe Ocampo
8 min readAug 12, 2024

--

Capítulo 2

En este artículo aprenderemos qué es un objeto Python, cómo funciona y cómo se puede usar para interactuar con el código Python de Mojo. El objeto Python es un tipo especial en Mojo que nos permite ejecutar código Python desde Mojo y obtener un puntero a un objeto Python. Esto significa que podemos usar todas las características y bibliotecas de Python desde Mojo, y también pasar datos entre los dos lenguajes.

Para entender el concepto de objeto Python, crearé un nuevo archivo y lo nombraremos como datatypes.ipynb esto nos abre la consola similar a la que conocemos como Jupyter.

Y este será nuestro cuaderno Jupyter en visual Studio, pero no obstante si nos encontramos con problemas de configuración, podemos instalar Jupyter lab y las instrucciones las verás aquí, vamos a ello agregaré un nuestra celda de código la importación de Phyton y desde Python importaré el objeto Python y luego declararé una variable y usaremos una función llamada evaluar en esta función daré una expresión entonces agregaré dos números y luego al final imprimiré la variable, super fácil no?

Ejecutamos la celda y vemos cuanto tarda el bloque de código en resolver, agregaré una nueva celda sobre esta y le daremos el nombre e PythonObject y personalizamos la celda como markdown

Bien al ejecutar la celda se usará la función de evaluación de Python y sumará dos números y luego imprimirá el resultado que debería ser 15.

En esencia esta x apunta a una ubicación de memoria, vamos a ejecutar el siguiente codigo, ¿cómo lo hacemos? usaré el doble porcentaje de Python y el resultado es 15.

En este cuaderno de Jupyter, hemos usado el porcentaje de Python en la parte superior de esta celda esto ejecuta el código a través de Python en lugar de Mojo, como te dije antes, x es un puntero a la memoria asignada al montón ahora vamos a entender el concepto de montón y pila.

Vamos a crear un nuevo archivo en blanco, le voy a llamar test.mojo, para entender hay dos tipos de memoria en el sistema operativo una es la memoria de pila, que es más pequeña pero muy rápida normalmente se usan para almacenar los valores estáticos y los tipos primitivos, y los punteros que apuntan a la ubicación de la memoria en el montón y el puntero es una dirección para buscar los valores en otro lugar de la memoria.

Como puedes ver visualmente, la memoria del montón es enorme y el tamaño puede cambiar en tiempo de ejecución, pero necesita un puntero para acceder a los datos, lo que es relativamente lento por tanto, para acceder a todas las palabras clave de Python.

var py = Python.import_module("builtins")
py.print("este es una impresion de python")

Juguemos un poco mas con el codigo, vamos a usar la funcion py.print() y dentro le vamos a pedir que tipo es y un identificador para ver la dirección de memoria.

py.print(py.type(x))
py.print(py.id(x))

Puedes ver que el tipo es entero, y esta es la dirección que está almacenada en x en la pila usando la

Vamos a sobreescribir la variable x de acuerdo ?

x = "Hola desde python"
py.print(x)
py.print(py.type(x))
py.print(py.id(x))

Como puedes ver, el tipo se ha cambiado de int a cadena o string, los datos que se pueden almacenar usando estas variables de referencia pueden ser grandes o pequeños no tenemos que pensar en cuándo debemos asignar al montón y como has visto, también podemos cambiar el tipo de forma dinámica y cuando esta variable no hace referencia a una memoria, se desasignará del montón mediante la recolección de basura. Por lo que el sistema operativo puede usar esa ubicación de memoria para otra cosa.

Sin embargo, estas características antiguas también tienen la desventaja de que se utiliza mucha memoria adicional para los campos adicionales, y se necesitan instrucciones de CPU para asignar los datos, recuperarlos, recolectar basura, etc., pero en Mojo podemos eliminar toda esta sobrecarga. Por lo tanto, en Mojo podemos escribir la misma declaración.

Por ejemplo.

x = 5+10
print(x)

Como x es igual a cinco más diez y luego usamos la función de impresión de Mojo para imprimir el valor x así que esta fue la primera optimización de Mojo. En lugar de buscar un objeto en el montón a través de una dirección, x ahora es solo un valor en la pila con 64 bits que se pueden pasar a través de registros. Esto tiene numerosas implicaciones en el rendimiento. Ya no se necesitan todas las costosas asignaciones, recolección de basura e indirección el compilador puede hacer enormes optimizaciones cuando sabe cuál es el tipo numérico el valor se puede pasar directamente a los registros para operaciones matemáticas y no hay sobrecarga asociada con la compilación en código de bytes y la ejecución a través de un intérprete los datos se pueden empaquetar en un vector para obtener enormes ganancias de rendimiento.

En esta parte del artículo aprenderás sobre el tipo de datos SIMD en Mojo. Simd significa Single Instruction Multiple data (Instrucción única, múltiples datos), que es un tipo de computación paralela donde una sola operación se aplica a múltiples puntos de datos simultáneamente.

Esto puede mejorar el rendimiento y la eficiencia de ciertas tareas, como el procesamiento de imágenes, el aprendizaje automático y las operaciones vectoriales vamos a ello, usaremos una variable y para almacenar un solo tipo de datos, por lo que usaremos la palabra clave SIMD y dentro de los corchetes especificaremos el DType y usaremos el int sin signo ocho y luego definiremos el tamaño de este vector cimd.

y = SIMD[DType.uint8, 4](1,2,3,4)
print(y)

Tenemos que especificar dos cosas una es el tipo de datos y la segunda es el tamaño también podemos definir los valores que se pueden almacenar en este vector entonces almacenaremos uno, dos, tres y cuatro y luego imprimiremos la variable y como puedes ver, hay un vector que contiene cuatro valores en la definición tenemos el tipo de datos y el tamaño.

Estos dos son conocidos como parámetros, lo que significa que deben ser de tiempo de compilación ahora bien, mientras que los valores como 1,2,3,4, cuatro son los argumentos que pueden ser de tiempo de compilación o de tiempo de ejecución ahora, por ejemplo, la entrada del usuario o los datos recuperados de una API son conocidos en tiempo de ejecución, por lo que no pueden utilizarse como parámetro durante el proceso de compilación en otros lenguajes, argumento y parámetros a menudo significan lo mismo, pero en Mojo es una distinción muy importante.

Así que hemos definido un vector de números de ocho bits que se empaquetan en 32 bits, entonces, este es un número de ocho bits y si calculamos el tamaño de todos estos cuatro, entonces ocho cuatros son 32 el total son 32 bits, podemos realizar una sola instrucción en todo esto en lugar de cuatro instrucciones separadas, este es el poder de mojo, vamos a una sola declaración vamos a multiplicar este vector por diez e imprimiré el valor de y.

Como puedes ver en una sola instrucción, hemos multiplicado este vector con un valor diferente, esta es la representación de cómo se almacenan nuestros datos en la memoria, la memoria es típicamente direccionable por bytes, lo que significa que cada dirección de memoria única apunta a un byte que consta de ocho bits por tanto tenemos un total de cuatro dígitos, significa que se consumirán un total de cuatro bytes de la memoria estos dígitos están representados por cero y uno, en donde uno significa encendido y cero significa apagado, lo que indica una carga eléctrica en los pequeños circuitos de nuestro ordenador.

Estamos empaquetando los datos junto con el SIMD en la pila para que se pueda pasar a un registro SIMD, volvamos al código Visual Studio y verifiquemos el tamaño del registro SIMD en mi CPU.

Para verificar el tamaño del registro SIMD en mi CPU, usaré la misma función de ancho de bits, así que importaré el módulo información de sys

from sys import info
print(info.simdbitwidth())

En este módulo de información hay una función -> simdbitwidth() para evaluar un ancho de bits, que nos indicará el tamaño de nuestro registro SIMD como puedes ver, nuestro tamaño de registro SIMD es 128, lo que significa que puedo empaquetar simultáneamente 32 números de ocho bits y realizar un cálculo sobre todos ellos con una sola instrucción.

t = SIMD[DType.uint8, 32](1)
print(t)
t *= 10
print(t)

Hemos multiplicado todos estos valores por diez dentro de una sola instrucción, en lugar de este, podemos usar números diferentes vamos a cambiar el nombre a esta la variable t por b copiamos y pegamos por algo diferente esta vez le daremos valores de 1 a 32 para utilizar toda la potencia de nuestro registro de CPU, he usado una nueva variable y le he dado valores de hasta 32 enteros de ocho bits, venga a imprimir todos estos valores y luego lo multiplicaremos por 10 dentro de una sola instrucción y finalmente imprimiremos el valor, se realiza una operación dentro de una sola instrucción sobre múltiples valores.

b = SIMD[DType.uint8, 32](1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32)
print(b)
b *= 10
print(b)

Este es un verdadero poder que Mojo nos brinda, este era el vector antes de la multiplicación puedes ver, 1, 2, 3, … 32 y después de multiplicar cada uno de estos valores por diez, ahora es diez, 20, 30 y así sucesivamente, muestra su gran magia.

En el siguiente capitulo de Mojo entraremos en los tipos basicos de Mojo a por mas chavales.

--

--