Estuve jugando un poco con assembler para ver la diferencia con C (usando GCC como compilador).
La operación realizada fue aplicar una máscara a una imagen en blanco y negro, es decir por un lado tengo el puntero de la imagen original y por el otro un puntero de una imagen que contiene todos los bytes en 0xFF menos aquellos bytes que deseo llevar a 0x00 (negro), para luego aplicar una AND entre las dos imágenes.
Se usa OpenCv para obtener la imagen original a partir de un archivo y al finalizar la rutina, se guarda el resultado en otro archivo.
Código en C:
PHP:
void enmascarar(void *ptr,void *image_data,unsigned int tamanio)
{
unsigned int cont_tamanio;
for(cont_tamanio=0;cont_tamanio<tamanio;cont_tamanio++)
{
*(unsigned char*)(image_data+cont_tamanio)&=*(unsigned char*)(ptr+cont_tamanio);
}
}
Código en Assembler común:
PHP:
.global enmascarar
.type enmascarar, %function
enmascarar:
push {r4-r10}
mov r4, #0
LOOP:
ldrb r5, [r0, r4]
ldrb r6, [r1, r4]
AND r5, r5, r6
strb r5, [r1, r4]
add r4, r4, #1
cmp r2, r4
BNE LOOP
pop {r4-r10}
mov pc, lr
En Assembler, r0-r3 son los registros usados como argumentos de la función (en este caso r3 no se usa), r4-r11 se pueden usar como registros de uso general, pero deberán guardarse antes de usar y al final de la rutina devolver sus valores. En este caso la función no devuelve nada, pero en caso de hacerlo, se hace mediante el registro r0-r1.
El código se puede mejorar un poco ahorrando la instrucción "ADD" y "CMP" usando directamente una resta ("SUB") y en vez de usar r4 como índice, se podrían usar los punteros originales de esta forma:
Carga en r5 el contenido apuntado por r0 y suma en 1 a r0. Será necesario usar otro registro como puntero de destino:
PHP:
...
mov r7, r1
LOOP:
...
strb r5, [r7]!
Código en Assembler usando instrucciones NEON:
PHP:
.global enmascarar
.type enmascarar, %function
enmascarar: @r0: ptr de la máscara, r1: ptr de la imagen (destino), r2: cantidad de píxeles
push {r4-r10}
mov r2, r2, LSR#4 @Al trabajar con variables de 128bits (qn), trabajo de a 16bytes => la cantidad de píxeles deberán ser divididos por 16 o desplazar 4 veces a la derecha los bits
mov r5, r1
mov r4, #0
LOOP:
vld1.8 {q0}, [r0]! @Cargo los 1eros 16 bytes de la máscara y le sumo 16 a r0 (!= suma el índice por la cantidad de bytes leídos)
vld1.8 {q1}, [r1]! @Similar al anterior
vand q2, q0, q1
vst1.8 {q2}, [r5]! @Almaceno en la memoria apuntada por r5 el resultado de la operación.
add r4, r4, #1
cmp r2, r4
BNE LOOP
pop {r4-r10}
mov pc, lr
Al igual que antes, el loop se podría resolver con el uso de "SUB" en vez de "ADD" y "CMP".
Llamada reiterativa en todos los casos:
PHP:
unsigned int current_timestamp() {
struct timeval te;
gettimeofday(&te, NULL); // get current time
return te.tv_usec;
}
main()
{
...
for(cont=0; cont<10; cont++)
{
t_0=current_timestamp();
enmascarar(ptr, img->imageData, img->width*img->height);
t_1=current_timestamp();
printf("La diferencia de tiempo (uS): %d\n",(t_1-t_0));
}
...
}
Resultados:
- Con C:
consola dijo:
La diferencia de tiempo (uS): 8691
La diferencia de tiempo (uS): 8669
La diferencia de tiempo (uS): 8647
La diferencia de tiempo (uS): 8661
La diferencia de tiempo (uS): 8652
La diferencia de tiempo (uS): 8700
La diferencia de tiempo (uS): 8995
La diferencia de tiempo (uS): 8687
La diferencia de tiempo (uS): 8683
La diferencia de tiempo (uS): 8678
- Con Assembler común:
consola dijo:
La diferencia de tiempo (uS): 3483
La diferencia de tiempo (uS): 3445
La diferencia de tiempo (uS): 3309
La diferencia de tiempo (uS): 3385
La diferencia de tiempo (uS): 3448
La diferencia de tiempo (uS): 3422
La diferencia de tiempo (uS): 3429
La diferencia de tiempo (uS): 3431
La diferencia de tiempo (uS): 3386
La diferencia de tiempo (uS): 3424
- Con Assembler usando instrucciones Neon:
consola dijo:
La diferencia de tiempo (uS): 1091
La diferencia de tiempo (uS): 1123
La diferencia de tiempo (uS): 1120
La diferencia de tiempo (uS): 1128
La diferencia de tiempo (uS): 1110
La diferencia de tiempo (uS): 1125
La diferencia de tiempo (uS): 1121
La diferencia de tiempo (uS): 1122
La diferencia de tiempo (uS): 1197
La diferencia de tiempo (uS): 1124
Hay una diferencia considerable a pesar de que la operación es simplemente una "AND", incluso el tiempo obtenido por la función "current_timestamp" tal vez no sea la mejor referencia de tiempo, pero dá una buena idea de la optimización del uso de Assembler en ciertas rutinas críticas.
Les dejo la imagen original y su resultado.
NOTA: originalmente en el programa guardo el archivo en BMP, por el tamaño, el foro no me dejo subir dicho archivo, por lo tanto lo convertí a jpg. Podía haber obtenido el jpg directamente desde el OpenCv, pero bue... no lo hice, tampoco cambia el resultado obtenido.