Archivo de la etiqueta: c

Como usar bibliotecas de C en Python

Para determinadas aplicaciones, la eficiencia y el rendimiento es clave. Python es un buen lenguaje para programar rápido y mucho en pocas líneas, pero a veces queremos bajar más de nivel para ofrecer mejores prestaciones.

Pero esta decisión no debe implicar, perder nuestra comodidad que tenemos con Python. Existe una biblioteca incluida nativamente desde Python 2.5 llamada ctypes que nos permite utilizar funciones y bibliotecas compartidas de C (en cualquier sistema operativo) obteniendo la eficiencia del lenguaje C pero sin renunciar a las bondades de sintaxis de Python.

Ejemplo práctico y sencillo

Veamos un ejemplo sencillo para ilustrar esta gran funcionalidad. Para empezar podemos crear un simple archivo de código fuente en C y por ejemplo llamarlo ‘libtest.c’.

Por ejemplo, en esta biblioteca C, haremos una simple función que multiplique enteros:

int multiply(int num1, int num2)
{
    return num1 * num2;
}

Ahora necesitaremos compilar nuestra biblioteca ‘libtest.c’ en una biblioteca válida, para ello utilizaremos los siguientes comandos de compilación:

gcc -c -fPIC libtest.c
gcc -shared libtest.o -o libtest.so

Ello creara una versión final de la biblioteca para GNU/Linux llamada libtest.so que podamos usar en Python.

Ahora probemos nuestra biblioteca (debes poner el path o camino de libtest.so en el código para que funcione o incluirlo en las rutas donde GNU/Linux buscara las bibliotecas comunes):

# A partir de Python >= 2.5 se incluye la libreria *ctypes* en el core, comprobar si esta disponible
import sys
try:
    from ctypes import *
except ImportError:
    print 'ERROR! La biblioteca *ctypes* para Python no esta disponible.'
    sys.exit(-1)

libtest = cdll.LoadLibrary('ruta_a_la_biblioteca/libtest.so')
print 'La multiplicación de 2 * 2 es:', libtest.multiply(2, 2)

Este ejemplo imprimirá 4 si todo ha ido correctamente. Como se aprecia, no es nada complicado usar código C nativo en Python.

Ejemplo con parámetros

Podemos crear ejemplos más complejos, incluso con funciones que tengan argumentos como punteros o pasados por referencia. Por ejemplo una lista donde se sumen todos los valores, su media:

/* suma de una lista de valores */
double sum(double *data, int n)
{
    int i=0;
    double sum=0.0;

    for (i=0; i < n; i++)
        sum += data[i];
    return sum;
}

/* media de una lista de valores */
double mean(double *data, int n)
{
    double s = sum(data, n);
    return s/((double)n);
}

Lo compilamos de nuevo con:t:

$ gcc -shared -fPIC liblist.c -o liblist.so

Y en python de nuevo usamos ctypes:

from ctypes import *
so = CDLL("liblist.so")

# Establecemos las interfaces con los argumentos

# y los valores que devuelven
so.mean.argtypes= [POINTER(c_double), c_int]
so.mean.restype = c_double
so.sum.argtypes = [POINTER(c_double), c_int]
so.sum.restype = c_double

# Llamamos a las funciones
def cmean(self, dlist):
    doubleArray = c_double*len(dlist) # Tipo de dato (double[n])
    cx = doubleArray(*dlist) # Array actual
    n = len(cx) # Longitud del dato

    result = so.mean(cx, n)
    return float(result)

def csum(self, dlist):
    doubleArray = c_double*len(dlist)
    cx = doubleArray(*dlist)
    n = len(cx)

    result = so.sum(cx, n)
    return float(result)

# Ahora podemos usar estas funciones como si fuera python puro!
data = [1,2,3,4,5,6]
print cmean(data)
print csum(data)

Con ctypes podemos crear un wrapper (envoltura o adaptador) para una biblioteca matemática u otras funciones que nos interesen.

Ejemplo con MPI

Otro ejemplo práctico sería poder escribir MPI en python desde C, como el ejemplo que escribí hace unos días. Notad que ya existen implementaciones python de MPI, pero nunca esta demás saber como funcionan y que principios siguen, por ejemplo para iniciarse en openMPI desde python, podríamos hacer:

#!/usr/bin/python

import sys

try:
from ctypes import CDLL, pythonapi, c_int, POINTER, c_char_p, byref, RTLD_GLOBAL
from ctypes.util import find_library
except ImportError:
print 'ERROR! La biblioteca *ctypes* para Python no esta disponible!'
sys.exit(-1)

libc = CDLL('libc.so.6')

print libc._handle, libc._name

f = pythonapi.Py_GetArgcArgv
argc = c_int()
argv = POINTER(c_char_p)()
f(byref(argc), byref(argv))

mpi = CDLL(find_library('mpi'), RTLD_GLOBAL)
print mpi._handle, mpi._name

libc.printf("Hola mundo");

mpi.MPI_Finalize()

En este ejemplo, utilizo el método find_library() de ctypes que me permite olvidarme de localizar la ruta de una biblioteca y así buscar por si mismo el programa su ruta. Se importa la biblioteca de C y la de MPI y se hace un simple hola mundo mostrando algunos datos sobre las bibliotecas también. Para ctypes únicamente he importando las funciones necesarias, evitando que se cargue toda la biblioteca.

En definitiva ctypes es una potente herramienta que todo desarrollador Python debería tener en cuenta cuando quiera realizar desarrollos más eficientes y aprovechar implementaciones de otras bibliotecas compartidas.

OpenMPI en Ubuntu con Geany

MPI es una interfaz, API o especificación para el paso de mensajes (síncronos y asíncronos) entre ordenadores para permitir una comunicación. Generalmente es usado en supercomputadores, clusters de computadores y en general sistemas distribuidos ya que no requiere memoria compartida.

Existen muchas implementaciones de MPI y en multitud de lenguajes. Las implementaciones más usadas son las escritas en C y C++.

OpenMPI Logo
OpenMPI Logo

En este artículo usaremos la implementación de software libre OpenMPI escrita en C por un consorcio de acádemicos, investigadores e industrias afines. Esta implementación soporta la versión MPI-2 de 1997 por lo que esta muy capacitada para desarrollos profesionales.

Para instalarla en Ubuntu, en mi caso Ubuntu Maverick 10.10:

$ sudo apt-get install libopenmpi-dev openmpi-bin openmpi-doc

Puedes probar un ejemplo simple con el siguiente código:

#include < stdio.h>
#include < mpi.h>

int main(int argc, char** argv)
{
    int myrank, nprocs;

    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
    MPI_Comm_rank(MPI_COMM_WORLD, &myrank);

    printf("Hello from processor %d of %d\n", myrank, nprocs);

    MPI_Finalize();
    return 0;
}

Compilarlo con:

$ mpicc MPI_Hello.c -o MPI_Hello

Y ejecutarlo con:

$ mpiexec -n 5 MPI_Hello

Obtendrás una salida similar a:

$ mpiexec -n 5 MPI_Hello
Hello from processor 1 of 5
Hello from processor 0 of 5
Hello from processor 2 of 5
Hello from processor 4 of 5
Hello from processor 3 of 5

Obviamente más ejecuciones pueden darte resultados distintos o diferir de este, ya que cada procesador puede ejecutarse en diferentes momentos.

Integrar entorno de desarrollo Geany

Si además deseas integrar un IDE como Geany para hacer el proceso de compilación y ejecución fácil y rápido, puedes seguir los siguientes pasos para poder compilar un programa MPI con Geany:

1. Descargar el archivo de etiquetas C para OpenMPI y copiarlo a ~/.config/geany/tags/

$ wget http://shakaran.net/blog/wp-content/uploads/openmpi.c.tags
$ cp openmpi.c.tags ~/.config/geany/tags/

Actualización: También puedes generar el fichero con:

$ geany -g -P openmpi.c.tags /usr/include/mpi.h

2. Abrir el ejemplo MPI anterior con Geany

3. Establecer las configuraciones de compilación. Para ello, pinchar en el menú “Construir > Establecer comandos de construcción

En compilar poner:

mpicc -Wall "%f" -o "%e"

En ejecutar:

mpiexec -n 5 "%e"

Debería quedarte algo como en esta imagen (pinchar para ampliar):

Configuraciones de compilacion
Configuraciones de compilacion