/* mips_mflops.c
 * Stefano Salvi - 4/10/02
 * Benchmark che calcola:
 *  - MIPS basandosi su di un ciclo in C;
 *  - MIPS basandosi su di un ciclo in Assembler;
 *  - MFLOPS basandosi su di un ciclo in C
 */
#include <stdio.h>
#include <time.h>
#include <sched.h>

#define DIM_MIPS	1000000000	/*1 miliardo - Ripetizioni per MIPS*/
#define DIM_MFLOPS	100000000	/*100 milioni - Ripetizioni per MFLOPS*/

/* Calcolo MIPS con ciclo ASSEMBLY
 * Esegue un ciclo che fa' la somma di una costante a 2 registri,
 * quindi ripete l'operazione con 3 registri.
 * Calcola il tempo per ciascuno dei due cicli.
 * Calcola la differenza, che dovrebbe essere il tempo impiegato per una somma per ciclo
 * divide per il numero di ripetizione ed ottiene il tempo di una ripetizione.
 */
void asm_mips()
{
clock_t inizio;		// Tempo iniziale Ciclo
clock_t	tempoDueAdd;	// Tempo ciclo con due somme
clock_t tempoTreAdd;	// Tempo ciclo con tre somme
double tempo;		// Tempo in secondi
double MIPS;

  sched_yield();		// Cede il passo, cosi' dopo lo scheduler non lo disturba

  inizio = clock();		// tempo iniziale primo ciclo

  __asm__(
    "pushl %ecx\n"		// Salva tutti i registri usati
    "\tpushl %ebx\n"
    "\tpushl %eax\n"
    "\tpushl %edx\n"
    "\tmovl $1000000000,%ecx\n"	// Inizializza contatore
    "\tmovl $1,%eax\n"		// Inizializza variabili
    "\tmovl $2,%ebx\n"
    "\tmovl $3,%edx\n"
"lp:\n"
    "\taddl $1,%eax\n"		// Prima somma
    "\taddl $1,%ebx\n"		// Seconda somma
    "\tdecl %ecx\n"		// Decrementa contatore
    "\tjnz lp\n"		// Ciclo finche' decremento non da' 0
    "\tpopl %edx\n"		// Ripristina i registri 'sporcati'
    "\tpopl %eax\n"		// Ripristina i registri 'sporcati'
    "\tpopl %ebx\n"		// Ripristina i registri 'sporcati'
    "\tpopl %ecx\n"
  );

  tempoDueAdd = clock()- inizio; // Tempo ciclo con due somme

  sched_yield();		// Cede il passo, cosi' dopo lo scheduler non lo disturba

  inizio = clock();		// tempo iniziale secondo ciclo

  __asm__(
    "pushl %ecx\n"		// Salva i registri usati
    "\tpushl %ebx\n"
    "\tpushl %eax\n"
    "\tpushl %edx\n"
    "\tmovl $1000000000,%ecx\n"	// Inizializza contatore
    "\tmovl $1,%eax\n"		// Inizializza variabili
    "\tmovl $2,%ebx\n"
    "\tmovl $3,%edx\n"
    "\tmovl $0,%ebx\n"		// Inizializza variabile
"lp1:\n"
    "\taddl $1,%eax\n"		// Prima somma
    "\taddl $1,%ebx\n"		// Seconda somma
    "\taddl $1,%edx\n"		// Terza Somma
    "\tdecl %ecx\n"		// decrementaConteggio cicli
    "\tjne  lp1\n"		// Ciclo
    "\tpopl %edx\n"		// Ripristina i registri usati
    "\tpopl %eax\n"
    "\tpopl %ebx\n"
    "\tpopl %ecx\n"
  );

  tempoTreAdd=clock() - inizio; // Tempo ciclo con tre somme

  printf("tempi: 2 somme %f secondi, tre somme %f secondi\n",
    ((double)tempoDueAdd)/CLOCKS_PER_SEC, ((double)tempoTreAdd)/CLOCKS_PER_SEC);

  /* sottraendo al tempo del secondo ciclo il tempo del primo si otterra' il tempo puro
   * dell'incremento b++; */

  tempo = (tempoTreAdd - tempoDueAdd) / (double)CLOCKS_PER_SEC;
	
  printf("Il tempo di %d somme e' %f secondi\n", DIM_MIPS, tempo);
	
  /*Quante istruzioni fa al secondo = MIPS*/
  MIPS=(DIM_MIPS/tempo)/1000000;	
  printf("\nMIPS: %lf\n",MIPS);
}

/* Calcola i MIPS con un ciclo in C
 * Esegue un ciclo che fa' la somma di una costante a 2 variabili,
 * quindi ripete l'operazione con 3 registri.
 * Calcola il tempo per ciascuno dei due cicli.
 * Calcola la differenza, che dovrebbe essere il tempo impiegato per una somma per ciclo
 * divide per il numero di ripetizione ed ottiene il tempo di una ripetizione.
 */
void c_mips()
{
static int a,b,c,i;
clock_t inizio;		// Tempo iniziale Ciclo
clock_t	tempoDueAdd;	// Tempo ciclo con due somme
clock_t tempoTreAdd;	// Tempo ciclo con tre somme
double tempo;		// Tempo in secondi
double MIPS;

  a=0;				// Inizializza le variabili
  b=0;
  c=0;

  sched_yield();		// Cede il passo, cosi' dopo lo scheduler non lo disturba

  inizio = clock();		// tempo iniziale primo ciclo

  for(i=0;i < DIM_MIPS;i++) 	// primo ciclo con due somme
  {
    a+=3;
    b+=4;
  }

  tempoDueAdd = clock()- inizio; // Tempo ciclo con due somme

  sched_yield();		// Cede il passo, cosi' dopo lo scheduler non lo disturba

  inizio = clock();		// tempo iniziale secondo ciclo

  for(i=0;i<DIM_MIPS;i++)	//secondo ciclo con tre somme
  {	
    a+=2;
    b+=3;
    c+=4;
  }
	
  tempoTreAdd=clock() - inizio; // Tempo ciclo con tre somme

  printf("tempi: 2 somme %f secondi, tre somme %f secondi\n",
    ((double)tempoDueAdd)/CLOCKS_PER_SEC, ((double)tempoTreAdd)/CLOCKS_PER_SEC);

  /* sottraendo al tempo del secondo ciclo il tempo del primo si otterra' il tempo puro
   * dell'incremento b++; */

  tempo = (tempoTreAdd - tempoDueAdd) / (double)CLOCKS_PER_SEC;
	
  printf("Il tempo di %d somme e' %f secondi\n", DIM_MIPS, tempo);
	
  /*Quante istruzioni fa al secondo = MIPS*/
  MIPS=(DIM_MIPS/tempo)/1000000;	
  printf("\nMIPS: %lf\n",MIPS);
}

/* Calcolo dei MFLOPS
 * Esegue un ciclo che fa' la somma di una costante a 2 variabili double,
 * quindi ripete l'operazione con 3 registri.
 * Calcola il tempo per ciascuno dei due cicli.
 * Calcola la differenza, che dovrebbe essere il tempo impiegato per una somma per ciclo
 * divide per il numero di ripetizione ed ottiene il tempo di una ripetizione.
 */
void c_mflops() /*Calcolo MFLOPS*/
{
static int i;
clock_t inizio;		// Tempo iniziale Ciclo
clock_t	tempoDueAdd;	// Tempo ciclo con due somme
clock_t tempoTreAdd;	// Tempo ciclo con tre somme
double tempo;		// Tempo in secondi
double MFLOPS;
double a,b,c;

  a=0.0;		// Inizializza le variabili
  b=0.0;
  c=0.0;

  sched_yield();		// Cede il passo, cosi' dopo lo scheduler non lo disturba

  inizio = clock();		// tempo iniziale primo ciclo

  for(i=0;i<DIM_MFLOPS;i++) /*primo ciclo con una istruzione*/
  {
    a=a+0.1;
    b=b+0.1;
  }

  tempoDueAdd = clock()- inizio; // Tempo ciclo con due somme

  sched_yield();		// Cede il passo, cosi' dopo lo scheduler non lo disturba

  inizio = clock();		// tempo iniziale secondo ciclo

  for(i=0;i<DIM_MFLOPS;i++) /*secondo ciclo con due istruzioni*/
  {	
    a=a+0.1;
    b=b+0.1;
    c=c+0.1;
  }
	
	
  tempoTreAdd=clock() - inizio; // Tempo ciclo con tre somme

  printf("tempi: 2 somme %f secondi, tre somme %f secondi\n",
    ((double)tempoDueAdd)/CLOCKS_PER_SEC, ((double)tempoTreAdd)/CLOCKS_PER_SEC);

  /* sottraendo al tempo del secondo ciclo il tempo del primo si otterra' il tempo puro
   * dell'incremento b++; */

  tempo = (tempoTreAdd - tempoDueAdd) / (double)CLOCKS_PER_SEC;
	
  printf("Il tempo di %d somme e' %f secondi\n", DIM_MFLOPS, tempo);
	
  /*Quante istruzioni fa al secondo = MFLOPS*/
  MFLOPS=(DIM_MFLOPS/tempo)/1000000;	
  printf("\nMFLOPS: %lf\n",MFLOPS);
}

int main()
{
int scelta;

  // Stampa il menu'
  printf("1. MIPS con C\n");
  printf("2. MIPS con ASM\n");
  printf("3. MFLOPS\n");
  printf("Inserisci la scelta: ");

  // Legge la scelta
  scanf("%d",&scelta);

  // Esegue il lavoro
  switch(scelta)
  {
    case 1:
      c_mips();
      break;

    case 2:
      asm_mips();
      break;

    case 3:
      c_mflops();
      break;

    default:
      printf("La scelta non e' presente!");
      break;
  }

  return 0;
}

