/*

This is a small test program which demonstrates the asymmetry in
memory access speeds on Athlon processors.  The asymmetric part
occurs when the memory is written _back_ to the array during a
traverse of an array.  This process is much faster while incrementing
up through the array than when decrementing down through it.  

*/

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>


int main(int argc, char *argv[]){
register int sum;
register int i,j;
register int v_j,v_i;
int asize,decrement;
int *a;
float scale;

  if(argc !=2){
    (void) fprintf(stdout,"usage memasymmetry [-]array_size\n");
    (void) fprintf(stdout,"  default is to increment up through the array\n");
    (void) fprintf(stdout,"  - decrements down through array instead\n");
    exit(EXIT_FAILURE);
  }
  if(sscanf(argv[1],"%d",&asize) != 1){
    (void) fprintf(stderr,"Noninteger array size specifie: [%s] \n",argv[1]);
    exit(EXIT_FAILURE);
  }
  if(asize < 0){
    decrement=1;
    asize=-asize;
  }
  else{
    decrement=0;
  }
  a=calloc(asize,sizeof(int));
  if(a==NULL){
    (void) fprintf(stderr,"Memory allocation failed\n");
    exit(EXIT_FAILURE);
  }
  sum=0;
  srand(time(0));
  scale = asize;
  scale /= RAND_MAX;
  i=rand()*scale;
  a[i]=1;
  if(decrement){
    for(i=asize-1,j=i-1; j>=0;      i--,j--){
      v_j=a[j];
      v_i=a[i];
      if(v_j <= v_i){
        a[i]=v_j;
	sum += (v_i + 1);
      }
    }
  }
  else {
    for(i=0,j=1;         j<asize;   i++,j++){
      v_j=a[j];
      v_i=a[i];
      if(v_j <= v_i){
        a[i]=v_j;
	sum += (v_i + 1);
      }
    }
  }

/*
Other variants which were tried:

same speed +/- 
       if(v_j <= v_i){
        sum += v_j + v_i;
      }
      
different speed +/-      
      if(v_j <= v_i){
        a[i]=v_j;;
	a[j]=v_i;
	sum += (v_i + 1);
      }
      
conclusion - memory writes (following reads?) are slower going
down through array than up.  Speed ratio is about 1.3 using
either gcc or Intel C on linux.
   
*/
  (void) fprintf(stdout,"Cells in array = %d\n",sum);
  exit(EXIT_SUCCESS);
  
}
