SDCC vs z88dk: Comparando tamaño y velocidad de los binarios generados para Amstrad CPC En este tutorial vamos a comparar los dos compiladores de C más famosos para Amstrad (desde el PC, claro). Vamos a comparar tanto el tamaño del binario que generan como de su velocidad de ejecución, haciendo varias pruebas/medidas diferentes. En el momento de hacer este tutorial, las versiones oficiales de los dos compiladores son z88dk v1.9 y SDCC 3.1.0. He tratado de probar varias compilaciones recientes de z88dk (Nightly builds/snapshots) pero he de decir que son muy inestables (al menos para Amstrad CPC) generando cuelgues o resultados inesperados, por lo que finalmente he decidido hacer la comparativa con las versiones oficiales. Para hacer las medidas de tiempo de ejecución podríamos usar el comando del firmware del Amstrad CPC llamado KL TIME PLEASE (BD0D), pero como ya vimos en el tutorial Midiendo tiempos y optimizando Campo de estrellas 2D (C con SDCC) esto no funciona en z88dk ya que las interrupciones están siempre deshabilitadas al arrancar nuestro programa... Para las medidas de velocidad de ejecución usaremos un emulador grabando la ejecución en video para luego desde un editor contar fácilmente el tiempo por ejemplo entre dos textos mostrados en pantalla. Nota: Para los tamaños vamos a compararlos sin incluir la cabecera Amsdos. Primer test, nos creamos dos variables enteras sin signo de 16 bits y hacemos un for de 65535 iteraciones mostrando un texto antes y después del bucle, el código fuente es exactamente el mismo en SDCC y en z88dk: //////////////////////////////////////////////////////////////////////// // Test01sdcc.c // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// #include <stdio.h> main() { unsigned int nCounter = 0; unsigned int nLoops = 0; printf("Start\n\r"); for(nCounter = 0; nCounter < 65535; nCounter++) nLoops++; printf("End %u\n\r", nLoops); while(1) {}; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // Test01z88dk.c // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// #include <stdio.h> main() { unsigned int nCounter = 0; unsigned int nLoops = 0; printf("Start\n\r"); for(nCounter = 0; nCounter < 65535; nCounter++) nLoops++; printf("End %u\n\r", nLoops); while(1) {}; } //////////////////////////////////////////////////////////////////////// Compilamos en ambos compiladores de la manera que ya hemos visto en anteriores tutoriales: Para SDCC (Test01sdcc.bat): Para z88dk (Test01z88dk.bat): Los dos binarios generan la misma salida en la pantalla del Amstrad CPC: Grabamos en video con el emulador las dos ejecuciones y comparamos:
El binario de SDCC ocupa más del doble que el z88dk pero se ejecuta 7 veces más rápido, o lo que es lo mismo, el binario de z88dk ocupa menos de la mitad que el de SDCC pero se ejecuta 7 veces más lento. A la vista de estos resultados tan diferentes en un programa tan simple solo podemos decir: ¡Pero que cojones es esto! :-) Tras analizar un poco el ensamblador y el mapa de memoria que genera cada compilador llegamos a la conclusión que la gran diferencia de tamaño en este caso nos la da el 'printf' de la librería de C, en este caso se ve que z88dk la tiene mucho más optimizada (al menos en tamaño) que la de SDCC. Para comparar mejor con nuestro programa el código que genera únicamente de nuestro código fuente, vamos a quitar los 'printf' y los vamos a sustituir por una simple llamada al comando del firmware TXT_OUTPUT (BB5A), quedando así: //////////////////////////////////////////////////////////////////////// // Test02sdcc.c // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// main() { unsigned int nCounter = 0; unsigned int nLoops = 0; __asm ld a, #83 ;'S' call #0xBB5A ;TXT_OUTPUT __endasm; for(nCounter = 0; nCounter < 65535; nCounter++) nLoops++; __asm ld a, #69 ;'E' call #0xBB5A ;TXT_OUTPUT __endasm; while(1) {}; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // Test02z88dk.c // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// main() { unsigned int nCounter = 0; unsigned int nLoops = 0; #asm ld a, 83 ;'S' call $BB5A ;TXT_OUTPUT #endasm for(nCounter = 0; nCounter < 65535; nCounter++) nLoops++; #asm ld a, 69 ;'E' call $BB5A ;TXT_OUTPUT #endasm while(1) {}; } //////////////////////////////////////////////////////////////////////// Compilamos, ejecutamos ambos programas y obtenemos los siguientes resultados:
Como puede verse, para este programa en C tan simple de apenas 20 lineas, z88dk ha generado un binario que ocupa prácticamente el doble que el generado por SDCC y en cuanto a la velocidad, el binario de z88dk tarda 7 veces lo que el generado por SDCC. En resumen, en este ejemplo concreto SDCC es 'espectacularmente' mejor que z88dk. Si analizamos el código fuente en ensamblador que generan ambos compiladores, vemos claramente las diferencias. Vamos a analizar únicamente el bloque que generan con el bucle for: for(nCounter = 0; nCounter < 65535; nCounter++) nLoops++; SDCC: ld de,#0x0000 ld bc,#0xFFFF 00106$: inc de dec bc ld a,b or a,c jr NZ,00106$ z88dk: ld hl,0 ;const pop de pop bc push hl push de jp i_5 .i_3 ld hl,2 ;const add hl,sp push hl call l_gint ; inc hl pop de call l_pint dec hl .i_5 ld hl,2 ;const add hl,sp call l_gint ; push hl ld hl,65535 ;const pop de call l_ult jp nc,i_4 ld hl,0 ;const add hl,sp push hl call l_gint ; inc hl pop de call l_pint dec hl jp i_3 .i_4 Para empezar se aprecia una clara diferencia de tamaño, el código que genera SDCC es claro y directo, usa un par de registros de 16bit (de y bc) y opera directamente con ellos (inc, dec) finalmente comparación y salto relativo hasta cumplir las iteraciones. Si observamos el código generado por z88dk vemos que hay un poco de desorden, cuesta seguirlo y el mayor problema es que está continuamente usando la pila (push, pop) y llamadas a funciones externas incomprensiblemente (l_gint, l_pint, l_ult) de ahí la enorme diferencia de rendimiento. Vamos a modificar el programa para que use únicamente enteros de 8 bits, a ver si hay cambios en los resultados. Los programas quedarían de la siguiente forma: //////////////////////////////////////////////////////////////////////// // Test03sdcc.c // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// main() { unsigned char nCounter1 = 0; unsigned char nCounter2 = 0; unsigned char nLoops = 0; __asm ld a, #83 ;'S' call #0xBB5A ;TXT_OUTPUT __endasm; for(nCounter1 = 0; nCounter1 <= 254; nCounter1++) for(nCounter2 = 0; nCounter2 <= 254; nCounter2++) nLoops++; __asm ld a, #69 ;'E' call #0xBB5A ;TXT_OUTPUT __endasm; while(1) {}; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // Test03z88dk.c // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// main() { unsigned char nCounter1 = 0; unsigned char nCounter2 = 0; unsigned char nLoops = 0; #asm ld a, 83 ;'S' call $BB5A ;TXT_OUTPUT #endasm for(nCounter1 = 0; nCounter1 <= 254; nCounter1++) for(nCounter2 = 0; nCounter2 <= 254; nCounter2++) nLoops++; #asm ld a, 69 ;'E' call $BB5A ;TXT_OUTPUT #endasm while(1) {}; } //////////////////////////////////////////////////////////////////////// Compilamos, ejecutamos ambos programas y obtenemos los siguientes resultados:
Pues los resultados no varían mucho respecto a la prueba anterior, el binario generado por SDCC ocupa menos de la mitad del tamaño generado por z88dk y además ejecuta en casi la 7 parte del tiempo que el generado por z88dk. Nuevamente SDCC gana en esta prueba y con mucha diferencia. Vamos a modificar un poco el programa para que use un bucle while y una variable de 32bits long, a ver como se comportan los compiladores, los programas quedarían así: //////////////////////////////////////////////////////////////////////// // Test04sdcc.c // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// main() { unsigned long nCounter = 0; __asm ld a, #83 ;'S' call #0xBB5A ;TXT_OUTPUT __endasm; while(nCounter < 131070L) nCounter++; __asm ld a, #69 ;'E' call #0xBB5A ;TXT_OUTPUT __endasm; while(1) {}; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // Test04z88dk.c // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// main() { unsigned long nCounter = 0; #asm ld a, 83 ;'S' call $BB5A ;TXT_OUTPUT #endasm while(nCounter < 131070L) nCounter++; #asm ld a, 69 ;'E' call $BB5A ;TXT_OUTPUT #endasm while(1) {}; } //////////////////////////////////////////////////////////////////////// Compilamos, ejecutamos ambos programas y obtenemos los siguientes resultados:
Pues parece que z88dk debe tener 'roto' el soporte de variables long (de 32bits) ya que no es capaz de compilar/generar bien este programa tan simple, otro punto negativo para z88dk. Vamos a probar ahora con un algoritmo 'famoso' como es el algoritmo de ordenación quicksort (fuente) y le vamos a hacer ordenar 960 enteros de 16 bits. Los programas quedarian de la siguiente forma: //////////////////////////////////////////////////////////////////////// // Test05sdcc.c // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// void quicksort(int* data, int N) { int i, j; int v, t; if( N <= 1 ) return; // Partition elements v = data[0]; i = 0; j = N; for(;;) { while(data[++i] < v && i < N) { } while(data[--j] > v) { } if( i >= j ) break; t = data[i]; data[i] = data[j]; data[j] = t; } t = data[i-1]; data[i-1] = data[0]; data[0] = t; quicksort(data, i-1); quicksort(data+i, N-i); } const int aNumbers[960] = { 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, }; main() { __asm ld a, #83 ;'S' call #0xBB5A ;TXT_OUTPUT __endasm; quicksort(aNumbers, 960); __asm ld a, #69 ;'E' call #0xBB5A ;TXT_OUTPUT __endasm; while(1) {}; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // Test05z88dk.c // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// void quicksort(int* data, int N) { int i, j; int v, t; if( N <= 1 ) return; // Partition elements v = data[0]; i = 0; j = N; for(;;) { while(data[++i] < v && i < N) { } while(data[--j] > v) { } if( i >= j ) break; t = data[i]; data[i] = data[j]; data[j] = t; } t = data[i-1]; data[i-1] = data[0]; data[0] = t; quicksort(data, i-1); quicksort(data+i, N-i); } static int aNumbers[960] = { 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, 12, 67, 123, 456, 789, 98, 64, 43, 32, 1, 4, 9, 345, 444, 777, }; main() { #asm ld a, 83 ;'S' call $BB5A ;TXT_OUTPUT #endasm quicksort(aNumbers, 960); #asm ld a, 69 ;'E' call $BB5A ;TXT_OUTPUT #endasm while(1) {}; } //////////////////////////////////////////////////////////////////////// Compilamos, ejecutamos ambos programas y obtenemos los siguientes resultados (esta vez, restamos los 960 enteros al tamaño para limitarnos al tamaño que genera del algoritmo, no de los datos):
Pues esta vez la diferencia de tamaño es menor, aunque sigue ganando SDCC. En cuanto a la ejecución, SDCC tarda 780 ms menos en ordenar los 960 numeros, o lo que es lo mismo, SDCC es un 30% más rápido que z88dk. Como prueba final, vamos a compilar un programa un poco más complejo y grande, que pinte en pantalla varias lineas usando el famoso algoritmo de Bresenham y las funciones de pintar en pantalla que se hicieron para el tutorial Pintando pixeles: Introducción a la memoria de video. El programa es exactamente igual para SDCC que para z88dk, diferenciándose únicamente en las dos lineas de código en ensamblador para poner el modo 0: //////////////////////////////////////////////////////////////////////// // Test06sdcc.c // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// void SetMode0PixelColor(unsigned char *pByteAddress, unsigned char nColor, unsigned char nPixel) { unsigned char nByte = *pByteAddress; if(nPixel == 0) { nByte &= 85; if(nColor & 1) nByte |= 128; if(nColor & 2) nByte |= 8; if(nColor & 4) nByte |= 32; if(nColor & 8) nByte |= 2; } else { nByte &= 170; if(nColor & 1) nByte |= 64; if(nColor & 2) nByte |= 4; if(nColor & 4) nByte |= 16; if(nColor & 8) nByte |= 1; } *pByteAddress = nByte; } void PutPixelMode0(unsigned char nX, unsigned char nY, unsigned char nColor) { unsigned char nPixel = 0; unsigned int nAddress = 0xC000 + ((nY / 8) * 80) + ((nY % 8) * 2048) + (nX / 2); nPixel = nX % 2; SetMode0PixelColor((unsigned char *)nAddress, nColor, nPixel); } /** * Draws a line between two points p1(p1x,p1y) and p2(p2x,p2y). * This function is based on the Bresenham's line algorithm and is highly * optimized to be able to draw lines very quickly. There is no floating point * arithmetic nor multiplications and divisions involved. Only addition, * subtraction and bit shifting are used. * * Note that you have to define your own customized setPixel(x,y) function, * which essentially lights a pixel on the screen. */ void swap(int n1, int n2) { int nAux = n1; n1 = n2; n2 = nAux; } void lineBresenham(int p1x, int p1y, int p2x, int p2y) { int F, x, y; int dy; int dx; int dy2; int dx2; int dy2_minus_dx2; int dy2_plus_dx2; if (p1x > p2x) // Swap points if p1 is on the right of p2 { swap(p1x, p2x); swap(p1y, p2y); } // Handle trivial cases separately for algorithm speed up. // Trivial case 1: m = +/-INF (Vertical line) if (p1x == p2x) { if (p1y > p2y) // Swap y-coordinates if p1 is above p2 { swap(p1y, p2y); } x = p1x; y = p1y; while (y <= p2y) { PutPixelMode0(x, y, 1); y++; } return; } // Trivial case 2: m = 0 (Horizontal line) else if (p1y == p2y) { x = p1x; y = p1y; while (x <= p2x) { PutPixelMode0(x, y, 1); x++; } return; } dy = p2y - p1y; // y-increment from p1 to p2 dx = p2x - p1x; // x-increment from p1 to p2 dy2 = (dy << 1); // dy << 1 == 2*dy dx2 = (dx << 1); dy2_minus_dx2 = dy2 - dx2; // precompute constant for speed up dy2_plus_dx2 = dy2 + dx2; if (dy >= 0) // m >= 0 { // Case 1: 0 <= m <= 1 (Original case) if (dy <= dx) { F = dy2 - dx; // initial F x = p1x; y = p1y; while (x <= p2x) { PutPixelMode0(x, y, 1); if (F <= 0) { F += dy2; } else { y++; F += dy2_minus_dx2; } x++; } } // Case 2: 1 < m < INF (Mirror about y=x line // replace all dy by dx and dx by dy) else { F = dx2 - dy; // initial F y = p1y; x = p1x; while (y <= p2y) { PutPixelMode0(x, y, 1); if (F <= 0) { F += dx2; } else { x++; F -= dy2_minus_dx2; } y++; } } } else // m < 0 { // Case 3: -1 <= m < 0 (Mirror about x-axis, replace all dy by -dy) if (dx >= -dy) { F = -dy2 - dx; // initial F x = p1x; y = p1y; while (x <= p2x) { PutPixelMode0(x, y, 1); if (F <= 0) { F -= dy2; } else { y--; F -= dy2_plus_dx2; } x++; } } // Case 4: -INF < m < -1 (Mirror about x-axis and mirror // about y=x line, replace all dx by -dy and dy by dx) else { F = dx2 + dy; // initial F y = p1y; x = p1x; while (y >= p2y) { PutPixelMode0(x, y, 1); if (F <= 0) { F += dx2; } else { x++; F += dy2_plus_dx2; } y--; } } } } main() { //SCR_SET_MODE 0 __asm ld a, #0 call #0xBC0E __endasm; lineBresenham(0, 0, 159, 199); lineBresenham(0, 199, 159, 0); lineBresenham(80, 0, 80, 199); lineBresenham(0, 100, 159, 100); while(1) {}; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // Test06z88dk.c // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// void SetMode0PixelColor(unsigned char *pByteAddress, unsigned char nColor, unsigned char nPixel) { unsigned char nByte = *pByteAddress; if(nPixel == 0) { nByte &= 85; if(nColor & 1) nByte |= 128; if(nColor & 2) nByte |= 8; if(nColor & 4) nByte |= 32; if(nColor & 8) nByte |= 2; } else { nByte &= 170; if(nColor & 1) nByte |= 64; if(nColor & 2) nByte |= 4; if(nColor & 4) nByte |= 16; if(nColor & 8) nByte |= 1; } *pByteAddress = nByte; } void PutPixelMode0(unsigned char nX, unsigned char nY, unsigned char nColor) { unsigned char nPixel = 0; unsigned int nAddress = 0xC000 + ((nY / 8) * 80) + ((nY % 8) * 2048) + (nX / 2); nPixel = nX % 2; SetMode0PixelColor((unsigned char *)nAddress, nColor, nPixel); } /** * Draws a line between two points p1(p1x,p1y) and p2(p2x,p2y). * This function is based on the Bresenham's line algorithm and is highly * optimized to be able to draw lines very quickly. There is no floating point * arithmetic nor multiplications and divisions involved. Only addition, * subtraction and bit shifting are used. * * Note that you have to define your own customized setPixel(x,y) function, * which essentially lights a pixel on the screen. */ void swap(int n1, int n2) { int nAux = n1; n1 = n2; n2 = nAux; } void lineBresenham(int p1x, int p1y, int p2x, int p2y) { int F, x, y; int dy; int dx; int dy2; int dx2; int dy2_minus_dx2; int dy2_plus_dx2; if (p1x > p2x) // Swap points if p1 is on the right of p2 { swap(p1x, p2x); swap(p1y, p2y); } // Handle trivial cases separately for algorithm speed up. // Trivial case 1: m = +/-INF (Vertical line) if (p1x == p2x) { if (p1y > p2y) // Swap y-coordinates if p1 is above p2 { swap(p1y, p2y); } x = p1x; y = p1y; while (y <= p2y) { PutPixelMode0(x, y, 1); y++; } return; } // Trivial case 2: m = 0 (Horizontal line) else if (p1y == p2y) { x = p1x; y = p1y; while (x <= p2x) { PutPixelMode0(x, y, 1); x++; } return; } dy = p2y - p1y; // y-increment from p1 to p2 dx = p2x - p1x; // x-increment from p1 to p2 dy2 = (dy << 1); // dy << 1 == 2*dy dx2 = (dx << 1); dy2_minus_dx2 = dy2 - dx2; // precompute constant for speed up dy2_plus_dx2 = dy2 + dx2; if (dy >= 0) // m >= 0 { // Case 1: 0 <= m <= 1 (Original case) if (dy <= dx) { F = dy2 - dx; // initial F x = p1x; y = p1y; while (x <= p2x) { PutPixelMode0(x, y, 1); if (F <= 0) { F += dy2; } else { y++; F += dy2_minus_dx2; } x++; } } // Case 2: 1 < m < INF (Mirror about y=x line // replace all dy by dx and dx by dy) else { F = dx2 - dy; // initial F y = p1y; x = p1x; while (y <= p2y) { PutPixelMode0(x, y, 1); if (F <= 0) { F += dx2; } else { x++; F -= dy2_minus_dx2; } y++; } } } else // m < 0 { // Case 3: -1 <= m < 0 (Mirror about x-axis, replace all dy by -dy) if (dx >= -dy) { F = -dy2 - dx; // initial F x = p1x; y = p1y; while (x <= p2x) { PutPixelMode0(x, y, 1); if (F <= 0) { F -= dy2; } else { y--; F -= dy2_plus_dx2; } x++; } } // Case 4: -INF < m < -1 (Mirror about x-axis and mirror // about y=x line, replace all dx by -dy and dy by dx) else { F = dx2 + dy; // initial F y = p1y; x = p1x; while (y >= p2y) { PutPixelMode0(x, y, 1); if (F <= 0) { F += dx2; } else { x++; F += dy2_plus_dx2; } y--; } } } } main() { //SCR_SET_MODE 0 #asm ld a, 0 call $BC0E #endasm lineBresenham(0, 0, 159, 199); lineBresenham(0, 199, 159, 0); lineBresenham(80, 0, 80, 199); lineBresenham(0, 100, 159, 100); while(1) {}; } //////////////////////////////////////////////////////////////////////// Compilamos, ejecutamos ambos programas y obtenemos los siguientes resultados:
SDCC vuelve a ganar y con una diferencia aplastante de nuevo, el binario de SDCC se ejecuta 6 veces más rápido que el de z88dk. Como una imagen vale más que mil palabras, he aquí una comparación visual:
Como la versión z88dk v1.9 es del año 2009 he decidido probar a compilar el último ejemplo con una beta reciente para ver si han mejorado algo en estos años, concretamente he usado la beta del día 2012-05-19, estos son los resultados:
No ha cambiado apenas la cosa....
ACTUALIZADO: el 09/07/2012 ha sido liberada la version 3.2.0 de SDCC, vamos a ver si la cosa ha mejorado o empeorado:
¡¡Ha mejorado aun más!!
ACTUALIZADO: el 06/11/2012 ha sido liberada la version 1.10 de z88dk, vamos a ver si la cosa ha mejorado o empeorado:
Como vemos, con la nueva version de z88dk no ha cambiado mucho la cosa respecto a la versión v1.9, los resultados siguen siendo penosos comparandolo con SDCC.
ACTUALIZADO: El 20/05/2013 ha sido liberada la version 3.3.0 de SDCC, vamos a ver si la cosa ha mejorado o empeorado:
Conclusiones:
A la vista de estos resultados tan abrumadores yo me despido definitivamente de z88dk: No malgastéis con z88dk vuestro tiempo (ciclos de cpu :-)) , ¡¡Larga vida a SDCC!!
Podéis bajar un zip con todos ficheros (código fuente, bat's para compilar, binarios y dsk's) aquí: SDCC_vs_z88dk.zip |
www.CPCMania.com 2012 |