Anatomía de np.add: del bucle SIMD en C al intérprete de Python

Fuentes: Tracing np.add, all the way down

Este artículo descompone paso a paso la ejecución de np.add(a, b) sobre dos arreglos float64 en NumPy 2.5.2, recorriendo el código fuente real desde la llamada en Python hasta el núcleo SIMD que realiza la suma en C. np.add no es una función de Python ordinaria, sino una instancia del tipo C numpy.ufunc, que implementa el protocolo vectorcall y delega en ufunc_generic_vectorcall y, seguidamente, en ufunc_generic_fastcall. Antes de ejecutar nada, NumPy consulta si algún argumento define array_ufunc, lo que permite a bibliotecas como Dask o CuPy hacerse cargo de la operación. Cuando no hay anulación, NumPy aplica promoción y despacho para localizar el bucle interno dd->d adecuado: primero comprueba una caché, luego realiza una resolución completa solo la primera vez para cada combinación de tipos y, en caso de no encontrar coincidencia directa, recurre a la maquinaria clásica de resolución de tipos. El resultado es un PyArrayMethodObject, que en el caso de float64 envuelve el puntero a la función C heredada ufunc->functions[i], una firma PyUFuncGenericFunction mantenida durante décadas. El artículo también distingue entre los bucles internos SIMD —escritos a mano para aprovechar instrucciones vectoriales— y los caminos que usan NpyIter, la estrategia de iteración más reciente. Todo el recorrido se apoya en enlaces directos al repositorio en la etiqueta v2.5.2, lo que permite al lector verificar cada paso mientras se introducen conceptos como NEP 13 (override de ufuncs) y NEP 43 (ufuncs extensibles). El texto sirve como mapa de referencia para quien quiera entender el funcionamiento interno de NumPy sin necesidad de experiencia previa en sus tripas.