jueves, julio 02, 2009

Prueba con Visual C++

Seguimos con el programa de generación del primo más grande (del año 1994).

Hicimos una prueba de compilación con Dev C++ en un PC decente. El resultado fue un poco decepcionante porque los 27 minutos que tardaba eran 6 minutos más que en un viejo portátil XP.

Es verdad que no me preocupé demasiado por ajustar los parámetros del compilador. Pero he preferido pasar directamente al compilador comercial por excelencia de Windows XP: Visual C++.

1.-Instalo Visual Studio 2005 (en los próximos días probaremos el Visual Studio 2008).

2.-Iniciamos un nuevo proyecto Visual Studio. Seleccionamos el lenguaje Visual C++ y "Aplicación de consola Win32"



3.-Así tendremos el proyecto vacío:


4.-Añado el programa C original de Slowinski y lo adapto ligeramente para el Visual C++. Concretamente hay que:
  • Modificar la sintaxis de main().
  • Comentar las líneas para el control de tiempos.
  • Comentar la cabecera que Visual C++ ha añadido por defecto: stdafx.h
  • Añadir la cabecera stdlib.h para poder usar la función atoi.

Al final de este post añado el código fuente entero para el que lo quiera probar.

5.-Lanzamos la compilación-ejecución y a esperar. En total ha tardado 23 minutos 45 segundos. Mejor que el Dev C++ pero un resultado bastante pobre. Ahora toca hacer unos pequeños ajustes.

6.-Priorizamos el rendimiento frente al tamaño del ejecutable:

Para ello vamos al Menú Proyecto, propiedades del proyecto, propiedades de configuración, C/C++, Entramos en la sección "Optimización" y seleccionamos "Maximizar velocidad" que se corresponde con la opción /O2.



Ahora intentamos la compilación y...SORPRESA. Se produce un error que dice lo siguiente: "las opciones de líneas de comandos '/O2' y '/RTC1' son incompatibles".


Bueno, pues hay que volver a Proyecto, propiedades del proyecto, propiedades de configuración, C/C++, General. Vamos a "formato de la información de depuración" y seleccionamos "Deshabilitado".

Además de esto, también hay que ir a C/C++, Generación de Código, "Comprobaciones básicas en tiempo de ejecución" y marcarlo como PREDETERMINADO.



Pues aunque volvamos a intentar compilar nos sale un nuevo error: '/Gm' requiere '/Zi' o '/ZI'.


Jeje, ya estaremos un poco aburridos tanto toquetear, pero esta es la última vez (de momento). Volvemos a Proyecto, propiedades del proyecto, propiedades de configuración, C/C++, Generación de código. Vamos a "habilitar regeneración mínima" y ponemos que NO.


7.-Ahora sí, ya podemos compilar y ejecutar tranquilamente. Y el rendimiento mejora bastante. El programa tarda ahora 18 minutos 31 segundos. Nuevo record, aunque todavía insuficiente.

Saludos.

Ahora el código fuente... (cuidado, algunos símbolos se han podido modificar. Cosas de blogger):


// Programa visualizador. 2009-07-02.

//#include "stdafx.h"

#include
#include
#include
#include

//Para que funcione la función atoi es necesario incluir stdlib.h.
#include

// Pequeña modificación en main:
//main(argc, argv)
//int argc;
//char *argv[];

int main(int argc, char* argv[])
{
#define EXP 859433 /* will calculate 2^859433 - 1, the 33rd Mersenne prime */
//Quitamos el cálculo de tiempos:
//double t, ftp0, ftp1;
double c0 = 0, c1 = 0, c2 = 0;
int n;
int *a, *b, *c;
int ddigit = 4; /* number decimal digits per part */
int vdig = (int)pow((double)10, (double)ddigit); /* decimal value of part */
int i, j, k, maxpart, mx;
unsigned int size;
char format[64];

n = EXP;
if (argc == 2) /* command-line param for new n? */
n = (atoi(argv[1]) > 0) ? atoi(argv[1]) : EXP;
printf("calculating 2^%d - 1\n", n);

size = (n+1)*sizeof(int);
printf ("allocating %d bytes for working arrays\n", 3*size);

if ((a = (int *)malloc(size)) == NULL) {
fprintf(stderr, "cannot allocate %d bytes for a\n", size);
exit(1);
}
if ((b = (int *)malloc(size)) == NULL) {
fprintf(stderr, "cannot allocate %d bytes for b\n", size);
exit(1);
}
if ((c = (int *)malloc(size)) == NULL) {
fprintf(stderr, "cannot allocate %d bytes for c\n", size);
exit(1);
}

for (i = 1; i <= n; i++) { a[i] = 0; /* set results array to null */ b[i] = 2; /* load up multiplicand array with n 2s */ c[i] = 0; /* set carry array to null */ } a[1] = 1; /* set initial intermediate result to 1 */ mx = 1; //#ifdef FTIME // ftime(&tp0); //#else // gettimeofday (&tp0, (struct timezone *)NULL); //#endif //#pragma _CRI parallel shared(n, a, b, c, mx, vdig) private(i, j) for (j = 1; j <= n; j++) { //#pragma _CRI taskloop vector for (i = 1; i <= mx; i++) { a[i] *= b[j]; } //#pragma _CRI taskloop vector for (i = 2; i <= mx+1; i++) c[i] = a[i-1]/vdig; //#pragma _CRI guard if (c[mx+1] > 0)
mx++;
//#pragma _CRI endguard

//#pragma _CRI taskloop vector
for (i = 1; i <= mx; i++) { a[i] = (a[i] % vdig) + c[i]; } } //#pragma _CRI endparallel /* propogate carry through one last time */ j = 0; for (i = 1; i <= n; i++) { k = a[i] + j; a[i] = k % vdig; j = k/vdig; } //#ifdef FTIME //ftime(&tp1); //ftp0 = (double)tp0.time+(double)tp0.millitm/(double)1000000; //ftp1 = (double)tp1.time+(double)tp1.millitm/(double)1000000; //#else //gettimeofday (&tp1, (struct timezone *)NULL); //ftp0 = (double)tp0.tv_sec+(double)tp0.tv_usec/(double)1000000; //ftp1 = (double)tp1.tv_sec+(double)tp1.tv_usec/(double)1000000; //#endif //t = (ftp1-ftp0 == 0) ? 0.000001 : ftp1-ftp0; //printf("wall time for main loop: %.5lf seconds\n", t); maxpart = 0; for (i = 0; i <= n; i++) if (a[i] != 0) maxpart = i; printf ("max parts used: %d (each part is %d decimal digits)\n", maxpart, ddigit); /* subtract one from answer and see if we match Slow's number */ a[1]--; if (a[1] < i =" 2;" j =" 0;" i =" maxpart;">= 1; i--) {
printf (format, a[i]);
j++;
if (j % 15 == 0)
printf ("\n");
}
printf ("\n");

return 0;
}

No hay comentarios: