Vectorización de programas utilizando MMX

Anuncio
Arquitectura de Computadores
AP-1/1
Apéndice-1. Vectorización de programas utilizando MMX
Domingo Benítez
Universidad de Las Palmas de Gran Canaria, 2008
1. DESCRIPCIÓN DEL ALGORITMO “SELECCIÓN CONDICIONAL”.........................................................1
2. IMPLEMENTACIÓN C++..................................................................................................................2
3. EJECUCIÓN Y ANÁLISIS DE PRESTACIONES .....................................................................................6
4. PRUÉBELO USTED MISMO ...............................................................................................................7
BIBLIOGRAFÍA ...................................................................................................................................7
AGRADECIMIENTOS ...........................................................................................................................7
En este Apéndice se describe la implementación del algoritmo de Selección Condicional utilizando
dos versiones de código: una denominada “Convencional” y otra denominada “Vectorial”. En
primer lugar se describe el algoritmo, en un segundo apartado se describe el programa principal en
el que se incluyen dos procedimientos que utilizan selectivamente instrucciones vectoriales MMX,
y por último se muestra los resultados de la ejecución del programa.
1. Descripción del algoritmo “Selección Condicional”
El objetivo del algoritmo de Selección
Condicional consiste en procesar tres
imágenes denominadas “croma”, “fondo”
y “monigote”, para superponer la figura
que se encuentra dentro de la imagen
“monigote” sobre la imagen “fondo”
superponiendo el personaje (ver Figura
1). El color que se muestra como fondo
de la imagen “monigote” desaparece en la
superposición.
El algoritmo se divide en cuatro etapas o
pasos (ver Figura 2). En un primer paso
se comparan píxel-á-píxel a través de una
función XNOR los píxel de la imagen
“monigote” con los de la imagen “croma”
y se obtiene una nueva imagen donde los
píxeles del personaje se inicializan a 0.
En un segundo paso se realiza la función
Y-lógica píxel-á-píxel de los píxeles de la
imagen “fondo” con los píxel de la
imagen resultado del Paso 1. En un tercer
paso algorítmico, se invierten los píxeles
de la imagen resultado del Paso 1 y la
imagen “monigote” original. Y por
último, en el Paso 4 se realiza la función
O-lógica entre los resultados de los Pasos
2 y 3, para dar el resultado final.
Figura 1. Objetivo del algoritmo de Selección
Condicional.
Arquitectura de Computadores
AP-1/2
PASO 3
PASO 1
Color de
fondo
Personaje COMPARACIÓN
Personaje
Y-lógica
PASO 4
PASO 2
Escenario
O-lógica
Y-lógica
Figura 2. Representación del algoritmo de Selección Condicional.
2. Implementación C++
Para la implementación del algoritmo se utiliza el lenguaje C++ a través del entorno Visual Studio
2005. Adicionalmente, la parte del programa que se sectoriza con el repertorio MMX utiliza las
“funciones intrínsecas MMX” que se describen a continuación en la Tabla 1.
Tabla 1. Funciones intrínsecas MMX utilizadas en la implementación del algoritmo Selección
Condicional.
Función
Descripción
Se comparan los bytes de m1 con los respectivos bytes
_m_pcmpeqb de m2, y se inicializa los bytes de la variable __m64 con
los resultados de las comparaciones.
Ejemplo
__m64 = _m_pcmpeqb
(__m64 m1 , __m64
m2);
_m_pand
__m64 = _m_pand
Realiza una Y-lógica de los 64 bits de los dos operandos
(__m64 m1 , __m64
de la función.
m2);
_m_pandn
Realiza una inversión lógica del valor de 64 bits de m1, __m64 = _m_pandn
y utiliza el resultado para realizar una Y-lógica con el (__m64 m1 , __m64
valor de m2.
m2);
_m_por
__m64 = _m_por
Realiza una O-lógica de los 64 bits del valor de m1 con
(__m64 m1 , __m64
el valor de 64 de m2.
m2);
A continuación se muestra el código fuente en C++ que corresponde a las dos implementaciones del
algoritmo de Selección Condicional. La versión convencional de la implementación se realiza a
través del procedimiento “SeleccionCondicional”, y la versión vectorial utilizando MMX se realiza
con el procedimiento “SeleccionCondicional_MMX”. Todo el código se divide principalmente en
tres partes.
Arquitectura de Computadores
AP-1/3
Parte 1. Procedimiento vectorial “SeleccionCondicional_MMX”. Este procedimiento implementa
los cuatro pasos del algoritmo utilizando las funciones intrínsecas MMX que se muestran en la
Tabla 1. La primera de las funciones MMX llamadas (_m_pcmpeqb) aplica de forma vectorial la
instrucción MMX pcmpeqb sobre datos de 8 bits dentro de un registro MMX de 64 bits. Cada byte
de este registro corresponde a un píxel distinto de la imagen. Por ello, cada operación pcmpeqb
realiza 8 operaciones de comparación en paralelo utilizando una única instrucción MMX sobre 8
píxeles de las correspondientes imágenes. Las otras funciones intrínsecas MMX que se utilizan y se
indican en la Tabla 1 son de tipo lógica y se aplican de forma independiente a cada bit de los
registros MMX. Este procedimiento consta básicamente de un bucle que itera una vez cada 8
píxeles de las imágenes ya que obtiene 8 píxeles resultados después de aplicarles las cuatro
funciones intrínsecas MMX que se indican en la Tabla 1.
Parte 2. Procedimiento convencional “SeleccionCondicional”. Este procedimiento aplica los cuatro
pasos de algoritmo pero no utiliza ninguna función especial, sino que se codifica a través de las
estructuras convencionales de C++. Consta de un bucle cuyo número de iteraciones coincide con el
número de píxeles de las imágenes.
Parte 3. Programa principal “_tmain”. En esta parte se inicializan las estructuras de datos y las
ventana donde se van a mostrar las imágenes de entrada al algoritmo y los resultados del mismo.
Además se realizan dos bucles de 10000 iteraciones; el primero de ellos llama ese número de veces
al procedimiento vectorial “SeleccionCondicional_MMX”, y el segundo llama el mismo número de
veces al procedimiento convencional “SeleccionCondicional”. Por último, en esta parte es donde se
monitoriza el tiempo de ejecución a través de las funciones clock().
#include "stdafx.h"
#include <iostream>
#include <sstream>
#include <stdio.h>
#include <time.h>
#include <iomanip>
#include <cmath>
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include <emmintrin.h> //MMX instructions
#define MAINFRAME "MainFrame"
using namespace std;
static long double dif_mmx, dif;
// Procedimiento que implementa el algoritmo Selección condicional utilizando
// las funciones intrínsicas MMX
void SeleccionCondicional_MMX( BYTE* pMask, BYTE* pMonigote, BYTE* pFondo, BYTE*
pResult, int nNumberOfPixels) {
// 8 pixeles procesados en paralelo, 3 canales por cada pixel
int nLoop = (nNumberOfPixels/8)*3;
__m64*
__m64*
__m64*
__m64*
__m64
__m64
__m64
__m64
mMask
mMonigote
mFondo
mResult
=
=
=
=
(__m64*)
(__m64*)
(__m64*)
(__m64*)
tmp_compare_mask;
tmp_and_fondo;
tmp_andn_monigote;
tmp_or;
pMask;
pMonigote;
pFondo;
pResult;
//
//
//
//
//
//
//
//
croma key
monigote
fondo
result
variable
variable
variable
variable
temporal
temporal
temporal
temporal
Arquitectura de Computadores
AP-1/4
_mm_empty();// es neceario para inicializar los registros MMX
for ( int i = 0; i < nLoop; i++ ) {
// PASO 1 del algoritmo Selección Condicional
tmp_compare_mask = _m_pcmpeqb (*mMask , *mMonigote);
// PASO 2 del algoritmo Selección Condicional
tmp_and_fondo
= _m_pand
(*mFondo, tmp_compare_mask);
// PASO 3 del algoritmo Selección Condicional
tmp_andn_monigote = _m_pandn
(tmp_compare_mask, *mMonigote);
// PASO 4 del algoritmo Selección Condicional
tmp_or
= _m_por
(tmp_and_fondo, tmp_andn_monigote);
// 8 píxeles resultados del algoritmo Selección Condicional
*mResult = tmp_or;
mMask++;
mMonigote++;
mFondo++;
mResult++;
}
}
_mm_empty();
// se apunta a los siguientes 8 píxeles
// es neceario para inicializar la pila de punto flotante
// Procedimiento que implementa el algoritmo Selección condicional utilizando
// las funciones convencionales de C++
void SeleccionCondicional( BYTE* pMask, BYTE* pMonigote, BYTE* pFondo, BYTE*
pResult, int nNumberOfPixels) {
// 1 pixel es procesado en cada iteración, 3 canales por cada pixel
int nLoop = (nNumberOfPixels)*3;
BYTE*
BYTE*
BYTE*
BYTE*
BYTE
BYTE
BYTE
BYTE
mMask = pMask;
mMonigote = pMonigote;
mFondo = pFondo;
mResult = pResult;
tmp_compare_mask;
tmp_and_fondo;
tmp_andn_monigote;
tmp_or;
//
//
//
//
croma key
monigote
fondo
result
//
//
//
//
variable
variable
variable
variable
temporal
temporal
temporal
temporal
for ( int i = 0; i < nLoop; i++ ){
// PASO 1 del algoritmo Selección Condicional
tmp_compare_mask = (*mMask == *mMonigote)? 0xFF:0x00;
// PASO 2 del algoritmo Selección Condicional
tmp_and_fondo
= (*mFondo & tmp_compare_mask);
// PASO 3 del algoritmo Selección Condicional
tmp_andn_monigote = (~tmp_compare_mask & *mMonigote);
// PASO 4 del algoritmo Selección Condicional
tmp_or
= (tmp_and_fondo ^ tmp_andn_monigote);
// Resultados del algoritmo Selección Condicional
*mResult = tmp_or;
}
}
mMask++; // siguiente byte
mMonigote++;
mFondo++;
mResult++;
int _tmain(int argc, _TCHAR* argv[]) {
Arquitectura de Computadores
AP-1/5
cout << "SELECCION CONDICIONAL \n" << "Arquitectura de Computadores
2008/09 \n" << "Facultad de Informatica \n" << "University of Las Palmas G.C.
\n" << "Antonio Jose Sanchez Lopez \n";
IplImage
IplImage
IplImage
IplImage
IplImage
*img_fondo = 0;
*img_monigote = 0;
*img_croma = 0;
*img_result = 0;
*img_result2 = 0;
// se inicializan los punteros de memoria
// donde se alojan las imágenes tanto de
// entrada como de salida
img_fondo = cvLoadImage("./fondo.jpg"); // se leen las imágenes *.jpg
img_monigote = cvLoadImage("./monigote.jpg");
img_croma = cvLoadImage("./cromakey.jpg");
img_result
=
cvCreateImage(cvSize(img_fondo->width,img_fondo->height),
IPL_DEPTH_8U, 3);
img_result2
=
cvCreateImage(cvSize(img_fondo->width,img_fondo->height),
IPL_DEPTH_8U, 3);
cvNamedWindow("Fondo");
cvNamedWindow("Monigote");
cvNamedWindow("Croma");
int cx, cy;
cx = img_fondo->width;
cy = img_fondo->height;
cvShowImage( "Fondo", img_fondo );
cvShowImage( "Monigote", img_monigote );
cvShowImage( "Croma", img_croma );
cvMoveWindow("Fondo", 0, 0);
cvMoveWindow("Monigote", cx+50, 0);
cvMoveWindow("Croma", 2*cx+100, 0);
cvWaitKey(1);
clock_t start;
clock_t end;
start = clock();
// contadores de intervalos temporales
// se comienza a contar el tiempo de ejecución
int index = 0;
for (int i = 0; i<10001; i++) {
// bucle con la version vectorial
SeleccionCondicional_MMX((BYTE*)img_croma>imageData,(BYTE*)img_monigote->imageData,(BYTE*)img_fondo>imageData,(BYTE*)img_result->imageData, cx*cy);
if (index>=1000) {
std::stringstream sindex;
sindex << "Vectorial MMX, iter " << i;
cvNamedWindow(sindex.str().c_str());
cvShowImage( sindex.str().c_str(), img_result );
cvMoveWindow(sindex.str().c_str(),
((i/index)-1)*0.5*cx,
cy+50);
index=0;
cvWaitKey(1);
}
index++;
}
end = clock();
// se termina de contar el tiempo de ejecución
dif_mmx = end-start;
cout << "Seleccion Condicional con MMX tarda: " << dif_mmx << " ticks\n";
index = 0;
start = clock();
// se comienza a contar otra vez el tiempo de ejecución
Arquitectura de Computadores
AP-1/6
for (int i = 0; i<10001; i++) {
// bucle con la versión convencional
SeleccionCondicional((BYTE*)img_croma->imageData,
(BYTE*)img_monigote->imageData,(BYTE*)img_fondo->imageData,(BYTE*)img_result2>imageData, cx*cy);
if (index>=1000) {
std::stringstream sindex;
sindex << "Secuencial, iter " << i;
cvNamedWindow(sindex.str().c_str());
cvShowImage( sindex.str().c_str(), img_result2 );
cvMoveWindow(sindex.str().c_str(),
((i/index)-1)*0.5*cx,
cy*2+100);
index=0;
cvWaitKey(1);
}
index++;
}
end = clock();
// se termina de contar el tiempo de ejecución
dif = end-start;
cout << "Seleccion Condicional Secuencial tarda: " << dif << " ticks\n";
cout << "SpeedUp = ";
cout << fixed << setprecision(2)<< dif/(long double)dif_mmx;
cout << "X\n";
}
cvWaitKey(0);
3. Ejecución y análisis de prestaciones
Una vez compilado el código anterior utilizando los ficheros *.h necesarios y la librería OpenCV
que se utiliza para la visualización de imágenes, se ejecuta el código generado cuyos resultados se
muestran en la Figura 3. En ella se pueden observar las tres imágenes de partida en la parte
superior: fondo, monigote, y croma. En una segunda fila de ventanas se muestran las imágenes
resultado de las iteraciones número 1000, 2000, …, 10000 utilizando el procedimiento vectorial
MMX “SeleccionCondicional_MMX”. En una tercera fila aparece los mismos resultados de la
segunda fila pero esta vez utilizando el procedimiento que no incluye funciones MMX
“SeleccionCondicional”.
Cuando se está ejecutando el programa, se observa que los resultados del procedimiento vectorial
MMX se muestran más rápidamente que cuando se muestran los resultados del procedimiento
convencional y que no utiliza la arquitectura MMX. En la ventana de comandos de la parte superior
de la Figura 3 se observan los resultados de prestaciones donde se destaca que cuando se utiliza
MMX, el algoritmo se ha ejecutado en 1281 ticks (intervalos temporales de monitorización), y
cuando se utiliza la versión convencional, el resultado es 11313 intervalos temporales. Esto hace
que el Speed-Up cuando se utiliza MMX es de 8.83X. Este valor puede depender del ordenador y
de su estado en el momento que se ejecuta.
Arquitectura de Computadores
AP-1/7
Figura 3. Ejecuciones serie y MMX del algoritmo de Selección Condicional.
4. Pruébelo usted mismo
A este apéndice se adjunta un programa ejecutable denominado “SCmmx.exe” que realiza el
algoritmo de Selección Condicional sobre las imágenes que se muestran en la Figura 3. Pruebe a
ejecutar este programa en su ordenador y verifique unos resultados parecidos a los que se muestrean
en la Figura 3.
Bibliografía
Gary Bradski, Adrian Kaehler; Learning OpenCV: Computer Vision with the OpenCV Library.
O'Reilly Media, Inc.; 1st edition (October 3, 2008)
MMX Technology. Visual C++ Language Reference. Microsoft Visual Studio 2005/.NET
Framework 2.0 (http://msdn.microsoft.com/en-us/library/698bxz2w(VS.80).aspx)
Agradecimientos
Antonio José Sánchez López desarrolló en Octubre de 2008 el código C++ que aparece en este
apéndice.
Descargar