Está en la página 1de 6

Experiencias con Python y CUDA en

Computacin de Altas Prestaciones


Sergio Armas, Lionel Mena, Alejandro Samarn,
Vicente Blanco 1 , Alberto Morales y Francisco Almeida

Resumen--- La computacin paralela no ha cesado de ejecutar algoritmos casi arbitrarios en sus GPU. El
explorar nuevos horizontes con el objetivo de obtener lenguaje de programacin diseado para ello es una
mejoras tangibles de rendimiento en la ejecucin de
algoritmos de toda clase. Si bien durante muchos aos variacin de C que contiene extensiones para traba-
se ha seguido el camino de innovar en la arquitectura jar con la GPU, amn de ciertas restricciones (no
de las CPU y crear software que se aproveche de esos permite recursividad ni punteros a funciones, solo
benecios, la consolidacin de la que vienen disfru-
tando en la ltima dcada los dispositivos grcos co-
permite nmeros en precisin simple en la mayora
mo hardware de cmputo general es difcil de ignorar. de tarjetas lanzadas al mercado hasta ahora, etc.).
Este cambio de paradigma trae consigo nuevas formas El trmino anglosajn wrapper se emplea en com-
de programar, nuevas herramientas y por supuesto,
nuevos desafos. El hecho de que el lenguaje C y sus
putacin para designar, a grandes rasgos, un tipo de
derivados sean la lingua franca de este tipo de pro- software que aade una capa de cdigo para traducir
gramacin no debera sorprender a propios ni a ex- de una interfaz existente a otra, generalmente con la
traos, pero otros lenguajes se van haciendo hueco nalidad de ganar portabilidad, sencillez o compati-
poco a poco. Es el caso de Python, que gracias al
wrapper PyCUDA [1] es capaz de ofrecer al progra- bilidad. PyCUDA, que ha sido desarrollado por An-
mador acceso a la computacin de altas prestaciones dreas Klckner 2 , es un ejemplo de esto: ejerce la fun-
sobre dispositivos grcos sin renunciar a la como- cin de interfaz en Python para las funciones nativas
didad y dinamismo de este lenguaje. El propsito de
este artculo es comprobar las facilidades que promete escritas en C que proporciona la SDK de NVIDIA.
PyCUDA as como su rendimiento frente a problemas La principal ventaja que representa la utilizacin de
reales. PyCUDA en el desarrollo, en contraposicin al uso
Palabras clave--- Python, CUDA, PyCUDA, Py- de las funciones propias de NVIDIA bajo C/C++, es
CUBLAS
sin duda la comodidad. PyCUDA permite abstraer,
por ejemplo, de todo lo relacionado con la reserva
I. Introduccin y liberacin de memoria en el dispositivo, lo cual
La capacidad de cmputo de las unidades de proce- hubiera representado una carga adicional de trabajo
samiento grco (GPU) ha alcanzado en los ltimos destacable en la versin C/C++. En este artculo se
aos un desarrollo notable, que ha crecido de ma- analizar si las ventajas de utilizar un lenguaje inter-
nera paralela a un fuerte incremento en la produc- pretado de muy alto nivel para ejecutar algoritmos
cin y demanda de dispositivos que las integran, tales en una GPU compensa la menor velocidad inherente
como smartphones, tablets, etc., adems de seguir a los lenguajes interpretados.
presentes en tarjetas grcas o placas base con cada
vez ms relevancia. Precisamente, dicho aumento de II. Acerca del modelo de programacin
potencia ha comenzado a hacer atractivo su empleo CUDA
para la manipulacin de cantidades masivas de datos El diseo de CUDA tiene como el objetivo el desar-
en mbitos ajenos al del video tales como criptologa, rollo de software que, de manera transparente, escale
biologa computacional, clculo cientco etc., que, el paralelismo de manera que se pueda aprovechar
por su naturaleza paralela, son susceptibles de eje- el incremento del nmero de procesadores al tiem-
cutarse con ms eciencia, incluso, que en una CPU po que mantiene una baja curva de aprendizaje para
tradicional. Esta tcnica de usar la GPU en aplica- los programadores familiarizados con lenguajes es-
ciones que tradicionalmente se haban ejecutado en tndares como el C. Para lograr esto fundamental-
CPU recibe el nombre de GPGPU (General-purpose mente posee tres puntos clave:
computing on graphics processing units).
A pesar de que existen diversos fabricantes es- Jerarqua de hilos
pecializados en dispositivos grcos que ofrecen al- Jerarqua de memoria
gn tipo de framework para desarrollo de aplica- Sincronizaciones por barrera
ciones paralelas sobre GPGPU, e incluso alterna-
A. Jerarqua de Hilos
tivas ms generales como OpenCL [2], NVIDIA es
probablemente el que ms ha apostado por este en- Se dene en base a 3 elementos: hilo, bloque y grid.
foque. Prcticamente desde los albores de esta com- As pues, cada grid contiene bloques y estos a su vez
putacin, ha ido desarrollando un modelo de progra- contienen hilos.
macin denominado CUDA (Compute Unied De- Por conveniencia, cada hilo se identica por un
vice Architecture) [3], que permite al programador vector de tres componentes (x, y, z) denomina-
1 Dpto. Estadstica, I.O. y Computacin, Univ. La Laguna, 2 Courant Institute of Mathematical Sciences - New York
e-mail: vblanco@ull.es University - http://mathema.tician.de/aboutme
C. Sincronizaciones por Barrera
Como los distintos hilos colaboran entre ellos y
pueden compartir datos, se requieren directivas de
sincronizacin. En CUDA se puede especicar una
sincronizacin del tipo barrera, en la que todos los
hilos esperan a que los dems lleguen al mismo punto.

D. Kernel
CUDA extiende el lenguaje permitiendo denir
funciones llamadas kernels, que cuando son invo-
cadas, son ejecutadas N veces en paralelo por N difer-
ente hilos de CUDA. Estas abstracciones permiten
un granulado no en el paralelismo de los datos y
los hilos, conduciendo al programador a dividir el
problema en subproblemas que pueden ser tratados
independientemente y en paralelo por bloques de hi-
los, y su vez dividir estos subproblemas en elementos
individuales que pueden ser resueltos en paralelo y de
manera cooperativa por todos los hilos de un mismo
bloque.
Esta estructura preserva la expresividad del
lenguaje permitiendo que los hilos cooperen en la
resolucin de cada subproblema, y al mismo tiem-
po permite la escalabilidad. En efecto, cada bloque
de hilos puede ser programado en cualquier ncleo
de procesamiento que este disponible, en cualquier
orden, concurrente o secuencialmente. Por lo que
Fig. 1. Jerarqua de hilos y patrones de acceso cualquier programa CUDA ya compilado puede eje-
cutarse en sistemas con distinto nmero de ncleos de
procesamiento, y solo el sistema de tiempo de ejecu-
cin debe conocer el dicho nmero de ncleos fsicos.
do threadIdx, as los hilos pueden identicados por Todo esto desde el punto de vista del programador
un ndice threadIdx unidimensional, bidimensional consiste en la extensin del lenguaje con un conjunto
o tridimensional, formando a su vez un bloque uni- reducido de instrucciones, lo que supone un curva de
dimensional, bidimensional o tridimensional. Esto aprendizaje suave; cabe notar, sin embargo, que a pe-
provee de una manera natural de realizar clculos sar de que CUDA permite la programacin de kernels
sobre elementos tales como un vector o una matriz. con pocas restricciones sobre el lenguaje, es necesario
adoptar ciertas pautas a la hora de generar los ker-
nels de las aplicaciones de inters, ya que de no seguir
B. Jerarqua de Memoria estas directrices el rendimiento se ver afectado seve-
ramente. Existen varias de ellas, pero las ms impor-
Los hilos en CUDA pueden acceder a distintas tantes son dos: garantizar la coalescencia en el acceso
memorias, unas compartidas y otras privadas. En a memoria (tanto en operaciones de lectura como de
primer lugar tenemos la memoria local privada de escritura) y usar la memoria compartida comn a los
cada hilo. Cada bloque de hilos posee memoria com- hilos de un mismo bloque, para aprovechar su mayor
partida visible solo por los hilos del bloque y con el velocidad de acceso en comparacin con la memoria
mismo tiempo de vida del bloque. Finalmente cada global del dispositivo [4]. Si se tienen en cuenta am-
hilo en cada bloque de cada grid puede acceder a la bas caractersticas, es muy probable que el kernel en
memoria global. cuestin tenga un rendimiento excelente.
Adicionalmente existen dos espacios de memoria
III. Computacin con PyCUDA
de slo lectura accesible por todos los hilos: la memo-
ria de texturas y la memoria de constante, opti- A. Requerimientos previos
mizadas para usos especcos. Las memorias global,
Antes de nada, es conveniente mencionar que
de textura y constante persisten mientras el kernel
para poder utilizar PyCUDA en una determinada
permanezca en accin.
mquina de pruebas, es necesario proveer primero de
Asi como se puede identicar los hilos dentro de todo un framework asociado que necesita dicho soft-
un bloque, se pueden identicar los bloques dentro de ware. En lneas generales, PyCUDA necesita obvia-
un grid, mediante una variable blockIdx que tambin mente de Python instalado en el sistema, as como
puede ser un ndice unidimensional, bidimensional o de NumPy/SciPy y de las libreras boost. Estos a su
tridimensional. vez tienen algunas dependencias de software con la
b = np . random . randn ( 1 6 )

Aunque las ms recientes GPUs soportan nmeros


en coma otante de doble precisin, la mayora de
los dispositivos disponibles actualmente slo sopor-
tan precisin simple, por lo que se impone la siguien-
te conversin de tipos:

a . astype ( np . float32 )
b . astype ( np . float32 )

Una vez cargados los datos en memoria, los sigu-


Fig. 2. Software necesario para ejecutar PyCUDA ientes pasos son transferirlos al dispositivo y ejecu-
tar el kernel. Con PyCUDA, ambos pasos pueden
concentrar en uno solo, porque en la propia lla-
que se debe tener cuidado, ya que de la correcta con- mada al kernel puede estar implcita la transferen-
guracin de ATLAS y LAPACK, por ejemplo, va cia de los datos a la memoria del dispositivo si se
a depender en buena medida el rendimiento poste- hace uso de la clase ArgumentHandler disponible en
rior de PyCUDA. Asimismo, para el ejemplo prc- pycuda.driver, la cual est preparada para trabajar
tico del desarrollo de un algoritmo de deteccin de con arrays numpy como argumentos de entrada/sal-
movimiento, se impone la necesidad de disponer de ida.
algunas libreras externas que faciliten sobremanera
el manejo de imgenes y videos de manera suciente- kernel = SourceModule ( " " "
mente transparente para el usuario, como es el caso __global__ v o i d name ( f l o a t *a ,
de PIL y OpenCV; y todo esto en su conjunto debe f l o a t *b ) {
hacer uso de las librerias que proporciona la SDK de i n t idx = threadIdx . x ;
NVIDIA. En la gura 2 se representan de manera b [ idx ] = b [ idx ] + a [ idx ] ;
clara estas dependencias de software. }
""")
B. Ejecucin de un kernel con PyCUDA f = kernel . get_function ( " name " )
No existe una nica manera de ejecutar un algorit- f ( cuda . In ( a ) , cuda . InOut ( b ) ,
mo en un dispositivo con PyCUDA y, por supuesto, block = ( 1 6 , 1 6 , 1 ) )
no todas resultan igual de ecientes. A continuacin,
se mostrar una sencilla secuencia de instrucciones
C. Overhead inducido
que ilustran de manera bastante exacta el nivel
de abstraccin que puede llegar a alcanzarse sin Obviamente, el hecho de trabajar con un lenguaje
menoscabo de la eciencia. interpretado como lo es Python, y con un wrapper
En primer lugar, se importan los mnimos paquetes como lo es PyCUDA, implica una carga de trabajo
necesarios para el funcionamiento de PyCUDA: extra para el procesador (no directamente relaciona-
do con el cmputo en GPU) que debe ser analizada
import pycuda . autoinit
y comparada con la misma ejecucin nominal en C
import pycuda . driver as cuda
para determinar si este overhead supone un grava-
from pycuda . compiler
men aceptable o no. En la gura 3 se puede observar
import SourceModule
una comparacin al ejecutar un ltro de convolucin
Conviene hacer notar que aunque separable [5] tanto en C como en Python + PyCU-
pycuda.autoinit se encarga de inicializar el DA, separando en cada caso (para distintos tamaos
dispositivo (seleccionando el de mayor capacidad de de matrices cuadradas) el tiempo empleado en eje-
cmputo si hay ms de uno), as como de crear un cutar nicamente el kernel en GPU y el programa
contexto automticamente, ambos aspectos pueden completo (que engloba reserva de memoria, creacion
ser congurados manualmente. de los arrays aleatorios, instanciacion del kernel, lib-
El siguiente paso consiste en cargar en memoria eracion de memoria... as como el tiempo de ejecu-
los datos que se desean procesar. En este punto, re- cin del propio kernel en la GPU). Como se puede
sulta muy aconsejable el uso del paquete NumPy de observar, y como era de esperar, los kernels se ejecu-
la librera SciPy, pues est provisto del potente tipo tan siempre ligeramente ms rpidos en C (un 50 %
de dato numpy.array, que facilita la preparacin y ms rpido como mnimo, aunque hay que recordar
manipulacin de los datos. En este ejemplo, consid- que en estos niveles se habla de milisegundos para
eraremos dos arrays aleatorios a y b, cuyas compo- procesar matrices cuadradas del orden de 2048x2048
nentes se suman dos a dos sobreescribiendo b con el elementos). Sin embargo, en el tiempo total de ejecu-
resultado. cin se puede observar como lo que a priori debera
ser una ventaja para C, a partir de 2048x2048 ele-
import numpy as np mentos el programa en C tarda de hecho ms que el
a = np . random . randn ( 1 6 ) mismo programa en Python. Esto puede deberse a
Fig. 3. Kernel para un ltro de convolucin: C y PyCUDA Fig. 4. Producto de matrices en CUDA: C y PyCUBLAS

diversas razones: en C, los arrays a procesar se re- una versin de altas prestaciones para estas opera-
llenan con nmeros aleatorios obtenidos mediante la ciones. Dicha implementacin ha sido denominada
funcin rand(), mientras que en Python es NumPy CUBLAS.
el encargado de generar las matrices aleatorias. Es Por su parte, PyCUBLAS es un wrapper [6] de
conocida la gran eciencia de NumPy a la hora de Python para CUBLAS creado por Derek Anderson
manejar matrices de gran tamao, por lo que este de- que ha centrado su diseo en la multiplicacin de
talle puede jugar a su favor. Otro posible punto de grandes matrices. Dispone, adems, de las ventajas
divergencia (ya que aunque los programas sean fun- ya comentadas de Python en cuanto a abstraccin,
cionalmente iguales, evidentemente hay ciertos ele- lo que posibilita la ejecucin de una operacin con
mentos del lenguaje que estn programados de for- muy pocas lneas de cdigo.
ma distinta) sea la utilizacin de los timers propios
import numpy as np
de NVIDIA para realizar las medidas en C (a travs
from pycublas import CUBLASMatrix
de la librera proporcionada por la SDK shrUtils);
sera interesante comprobar hasta que punto estn o
a = np . random . randn ( width , length ) .
no interriendo estos timers particulares en las medi-
astype ( np . float32 )
ciones totales.
b = np . random . randn ( width , length ) .
En cualquier caso, puede comprobarse como el
astype ( np . float32 )
overhead que introduce la utilizacin de Python +
PyCUDA no es, en ningn caso, alarmante. S que
a = CUBLASMatrix ( np . mat ( a ) )
es necesario hacer notar, sin embargo, que en el caso
b = CUBLASMatrix ( np . mat ( b ) )
de las primeras ejecuciones de algn programa que
use PyCUDA, la fase de carga de los import iniciales
c = a * b
s que es importante, llegando a tardar los mismos
ejemplos de la gura 3 cerca de 2 segundos, y donde Esta sencillez sintctica podra ser atractiva para
la mayora de los cuales se emplea en la precarga de extender el uso de CUDA entre investigadores de
estas directivas del wrapper: comunicacin inicial con otras ciencias cuyos estudios precisen de la manip-
el/los dispositivos existentes, seleccin de dispositi- ulacin de cantidades masivas de datos.
vo, carga de la interfaz con el compilador de NVIDIA En esta seccin se confrontan el tiempo de ejecu-
nvcc, etc. cin en el dispositivo de una multiplicacin de ma-
trices cuadradas de diferentes dimensiones, tanto a
IV. Producto matricial
travs de PyCUBLAS como de su correspondiente
La manera tradicional de abordar la multiplicacin versin nativa en C. No se ha contemplado en la gr-
de matrices pasa por ejecutar un algoritmo secuen- ca de la gura 4 los tiempos de ejecucin en la CPU,
cial de complejidad casi cbica en las mejores imple- puesto que estos llegan a ser hasta 35 millones de ve-
mentaciones. Su versin paralela, en cambio, permite ces mayor en el caso de una matrix de 2048x2048.
el clculo simultneo de las las de la primera matriz
multiplicando con la correspondiente columna de la V. Deteccin de movimiento
segunda para formar el resultado. Teniendo en cuenta las posibilidades del modelo
BLAS (Basic Linear Algebra Subprograms) es, de CUDA exploradas hasta ahora, parece lgico buscar
facto, una interfaz estndar de programacin de li- algn algoritmo que conlleve un esfuerzo computa-
breras que realizan operaciones bsicas de lgebra cional notable para ir un paso ms all en la explo-
lineal con vectores y/o matrices. racin del uso de Python como vehculo conductor
Desde su primera publicacin, en 1979, numerosos de los programas de cmputo. Una primera conside-
fabricantes de hardware han desarrollado versiones racin interesante es el RANSAC [7], un algoritmo
altamente optimizadas de BLAS. Tambin NVIDIA utilizado frecuentemente en campos muy diversos,
lo ha incluido en el SDK de CUDA para proporcionar pero no resulta fcilmente acomodable al cmputo
Fig. 5. Jerarqua de clases del detector de movimiento

paralelo bajo esta arquitectura debido a que requiere tros sobre los frames (imgenes) del video [9]. Para
de estructuras de datos complejas que adems con- entender el proceso basta con escoger 2 frames con-
templan mltiples dependencias entre ellas. secutivos del video y aplicar los siguientes pasos:
Finalmente, se ha escogido como objetivo de desar-
1. Conversin a escala de grises de las 2 imgenes
rollo el diseo e implementacin de un paquete que
2. Aplicacin del ltro de diferencia a las 2 im-
permita aplicar diferentes ltros a una imagen, a un
genes
conjunto de imgenes o a un video, en cuyo caso ser
3. Aplicacin del ltro Threshold
descompuesto en sus correspondientes frames. Este
4. Aplicacin del ltro de Erosin
paquete puede utilizarse para acelerar el clculo de
5. Mezcla en el canal R de la imagen original con
aplicaciones complejas que requieran de la aplicacin
la imagen resultado de los ltros
de ltros [8]. En concreto, una de las ms interesantes
consiste en un programa de deteccin de movimien- Una vez obtenidos las 2 imgenes en escala de gris-
to: a grandes rasgos, toma 2 frames de un video y es, se procede a aplicar el primer ltro:
discierne (marcando en color rojo sobre uno de los Filtro de Diferencia: Este ltro consiste en el
frames original) las partes en movimiento entre los valor absoluto de la resta de cada pxel (restando
mismos. Esto mismo se ejecuta de manera iterativa canal a canal en caso de imgenes de varios canales)
el nmero de veces necesario para realizar la detec- de las dos imgenes. Con esto obtenemos las zonas
cin de movimiento sobre un video compuesto de una donde se ha producido movimiento, como se puede
multitud de frames. observar en la primera imagen de la gura 6.
El paquete Filters desarrollado para este n se Filtro Threshold: La nalidad de este ltro es
estructura, esencialmente, en torno a dos clases: obtener una imagen binaria donde solo aparecen px-
CUDAHandler, concebido para abstraer de muchos eles blancos o negros. Este ltro compara cada pxel
detalles de la comunicacin con la GPU (informal- con un umbral, y si el valor del pxel est por debajo
mente, podra considerarse un wrapper del propio Py- se asigna el valor 0 (negro), en caso contrario (por
CUDA); y Filter, clase abstracta de la que heredan encima del umbral) se asigna el valor 255 (blanco).
cada uno de los ltros implementados. En la gu- As pues, la imagen resultante quedara como la se-
ra 5, pueden observarse tres clases hijas de Filter gunda imagen de la gura 6, donde adems se aprecia
que contienen las instrucciones para gestionar tres la aparicin de ruido a simple vista.
ltros necesarios para la deteccin de movimiento Filtro de Erosin: Contenido dentro los llama-
(Dierence, Threshold y Erosion), como se explica en dos Filtros Morfolgicos, este ltro determina si un
la posterior subseccin. Tambin se contempla una pxel se mantiene (se dene como blanco) o se elimi-
clase MotionDetector, encargada de dirigir la detec- na (se dene como negro). Para sopesar esta decisin
cin en s aplicando los ltros frame tras frame y que se hace uso de una mscara (denida a conveniencia)
hace uso del paquete Filters, adems por ltimo de que determina los pxeles vecinos a examinar; desde
la clase VideoHandler que la abstrae de la manipu- que al menos uno de estos vecinos est en negro, se
lacin de las operaciones de gestin de vdeo. elimina el pxel analizado (operacin and lgica. Por
el contrario, si todos los pxeles vecinos existen el px-
A. Implementacin del algoritmo el analizado permanecer en blanco. El objetivo de
El algoritmo de deteccin de movimiento ms sim- aplicar este ltro es eliminar el ruido expuesto por el
ple se basa en la aplicacin de una secuencia de l- ltro anterior, como se puede apreciar en la tercera
la misma como por rendimiento, a cambio de un pe-
queo e inevitable overhead inherente a la naturaleza
interpretada de Python y al uso de wrappers. De
cualquier forma, ese incremento en tiempo es lo su-
cientemente despreciable para que se pueda ignorar
sin temor, mxime si se tiene en cuenta el hecho de
que a mayor tamao del problema, ms impercepti-
ble se torna dicha carga de trabajo adicional. Por l-
timo, pero no por ello menos importante, los autores
desean recalcar que la curva de aprendizaje nece-
saria para poder manejarse en este entorno con cier-
ta solvencia es considerablemente ms suave que la
que conlleva el entorno tradicional de programacin
de CUDA bajo C/C++, lo cual consideran un ar-
gumento de suma importancia para atraer usuarios
potenciales, ya sean acadmicos de nueva hornada
o investigadores asentados que tienen necesidad de
clculo masivo paralelo y que utilizan para ello apli-
caciones desarrolladas hace aos en lenguajes como
FORTRAN cuyo mantenimiento se hace cada vez
ms inviable. En denitiva, si se tiene intencin de
migrar estas aplicaciones, Python y PyCUDA con-
forman una alternativa perfectamente capaz.

Fig. 6. Fases del algoritmo de deteccin de movimiento Referencias


[1] Andreas Klckner, Nicolas Pinto, Yunsup Lee, Bryan C.
Catanzaro, Paul Ivanov, and Ahmed Fasih, ``Pycuda:
imagen de la gura 6. Gpu run-time code generation for high-performance com-
puting,'' CoRR, vol. abs/0911.3456, 2009.
Por ltimo, queda realizar el fundido en el canal [2] Khronos Group, ``OpenCL - The open standard for par-
R de la imagen original con la imagen resultante de allel programming of heterogeneous systems,'' 2011.
[3] NVIDIA Corp., ``What is CUDA - NVIDIA Developer
aplicar el ltro de erosin. Este fundido no es ms que Zone,'' 2011.
una suma de pxel a pxel, obteniendo as el resultado [4] NVIDIA Corp., ``NVIDIA CUDA C Best Practices
nal de la gura 6. Guide,'' 2010.
[5] Victor Podlozhnyuk (NVIDIA Corp.), ``Image Convolu-
tion with CUDA,'' 2007.
VI. Conclusiones [6] Derek Anderson, ``PyCUBLAS - Easy
Como se ha podido observar, implementar la re- Python/NumPy/CUDA/CUBLAS Integration,'' 2009.
[7] Liu Jiayin, Wang Chuang, and Jae Ho Kim, ``Camera mo-
solucin de problemas relativamente complejos no tion detection for conversation scenes in movies,'' in Pro-
es especialmente costoso en tiempo con lenguajes ceedings of the 2010 International Conference on Compu-
dinmicos como Python. Si a eso se le aade la posi- tational and Information Sciences, Washington, DC, USA,
2010, ICCIS '10, pp. 725--728, IEEE Computer Society.
bilidad de utilizar todo el potencial de cmputo pa- [8] Zhiyi Yang, Yating Zhu, and Yong Pu, ``Parallel image
ralelo que ofrece CUDA a travs de wrappers como processing based on cuda,'' in CSSE (3). 2008, pp. 198--
201, IEEE Computer Society.
PyCUDA, el resultado es una herramienta muy po- [9] Andrew Kirillov, ``Motion detection algorithms,'' 2007.
tente que puede satisfacer las necesidades de investi-
gadores y cientcos en general tanto por madurez de

También podría gustarte