# include # include # include # include # include # include # define MACHINE APPLE_MAC # include "matmul.h" void abortm ( ); void chop(char*,int,int); void Docom(); void Explain ( ); void Flclos ( ); void Flopen ( ); void Gbye ( ); void Getcom ( ); void Getlda(char*); void Getn(char*); void Getord(char*, int*); void Getsho(char* ,int); void Hello ( ); void Help ( ); void Init ( ); int min(int,int); void Mult ( ); int Nstep ( ); void Pdump ( ); void Printr ( ); void Putinp(); void Putout(); void Record ( ); void Result(short lchop); #if MACHINE == VAX_VMS void second(int*); #elif MACHINE == IBM_PC void second(long*); #elif MACHINE == IBM_RS6 void second(long*); #elif MACHINE == CRAY extern float SDOT(); extern SAXPY(); extern SGEMM(); void second(long*); #else void second(long*,long*); #endif void Setval ( ); void Shoord ( ); void taxpy(int n,float *sa,float *sx,int incx,float *sy,int incy); void _TDOT(); float tdot(); void TSTART ( ); void TSTOP ( ); void TMSTART ( ); void TMSTOP ( ); void Zero ( int len ); /******************************************************************************/ int main ( ) /******************************************************************************/ /* Purpose: MAIN is the main program for MATMUL. Discussion: MATMUL is an interactive C program that sets up, carries out, and times a matrix multiplication. MATMUL can use several different algorithms and matrix sizes, and can be run on many different computers. The main program is very simple. First some initialization is done, and then the program gets a command from the user, executes it, and repeats. File usage: matmul.inp, record of input, created by MATMUL. matmul.out, record of input/output, created by MATMUL. matmul.hlp, help file. (Not available) matmul.dat, data file, created by MATMUL. matmul.exp, explanation file, read by MATMUL. Licensing: This code is distributed under the MIT license. Modified: 15 May 2009 Author: John Burkardt, Paul Puglielli. */ { Init ( ); for ( ; ; ) { Getcom ( ); Docom ( ); } return 0; } /******************************************************************************/ void abortm ( ) /******************************************************************************/ /* Purpose: ABORTM is called to terminate execution. Discussion: ABORTM dumps the results to a file, says "goodbye" to the user, and closes all files. */ { Pdump(); Gbye(); Flclos(); exit ( 1 ); } /******************************************************************************/ void chop ( char *STRING, int ILO, int IHI ) /******************************************************************************/ /* CHOP "chops" out entries ILO through IHI of STRING, shifts the string down, and pads the end of the string with NULLs. */ { size_t l; int i,j; l=strlen(STRING); i=ILO; for(j=IHI+1;j22) { getchar(); line=0; } } fclose ( ifilee ); ifilee=NULL; return; } /******************************************************************************/ void Flclos() /******************************************************************************/ /* FLCLOS closes all the files */ { if (ifilei) fclose(ifilei); if (ifileo) fclose(ifileo); if (ifileh) fclose(ifileh); if (ifiled) fclose(ifiled); return; } /******************************************************************************/ void Flopen() /******************************************************************************/ /* FLOPEN opens all the files. EXCEPT for matmul.dat, which is opened in Pdump. */ { char outline[80]; ifileo=fopen(fileo,"w"); if(!ifileo) { sprintf(outline,"MATMUL could not open the output file %s",fileo); Putout(outline); Putout("This is not a serious problem, but it does mean there\n"); Putout("will not be a transcript of the input and output\n"); } ifilei=fopen(filei,"w"); if(!ifilei) { sprintf(outline,"MATMUL could not open the output file %s\n",filei); Putout(outline); Putout("This is not a serious problem, but it does mean there\n"); Putout("will not be a record of you output.\n"); } /* HELP system not available yet! ifileh=fopen(fileh,"r"); if(!ifileh) { sprintf(outline,"MATMUL could not open the help file %s\n",fileh); Putout(outline); Putout("This is not a serious problem, but it does mean that\n"); Putout("the full help system will not be available.\n"); } */ ifileh=NULL; return; } /******************************************************************************/ void Gbye() /******************************************************************************/ /* GBYE says goodbye to the user, and prints a reminder about files created by the program. */ { char outline[80]; if(ifilei) sprintf(outline,"A copy of your input is in the file %s\n",filei); else sprintf(outline,"MATMUL could not create the input file %s\n",filei); Putout(outline); if(ifiled) sprintf(outline,"A copy of your results is in the file %s\n",filed); else sprintf(outline,"MATMUL could not create the result file %s\n",filed); Putout(outline); if(ifileo) sprintf(outline,"A copy of the entire session is in the file %s\n",fileo); else sprintf(outline,"MATMUL could not create the session file %s\n",fileo); Putout(outline); printf ( "\n" ); printf ( "MATMUL:\n" ); printf ( " Normal end of execution.\n" ); return; } /******************************************************************************/ void Getcom() /******************************************************************************/ /* GETCOM reads the next command from the user. */ { short i; char cx; Putout("Command? (Type H for help):"); for(i=0;((cx=getchar()) != '\n'); i++) command[i]=toupper(cx); Putinp(); return; } /******************************************************************************/ void Getlda(char *str) /******************************************************************************/ /* GETLDA gets a new value of LDA from the user. */ { char outline[80]; int temp; CHRCTI(str,&temp); if (str == NULL) Putout("I did not understand your definition of LDA.\n"); if(temp < 0) { Putout("The assignment of LDA was not acceptable!\n"); Putout("LDA must be positive.\n"); } else if (temp > LENA) { Putout("The assignment of LDA was not acceptable!\n"); sprintf(outline,"LDA must be no greater than %d.\n",LENA); Putout(outline); } else { lda=temp; sprintf(outline,"LDA has been set to %d.\n",lda); Putout(outline); } if (lda < n) { n=lda; Putout("\nNote: Since the value of N must always be no greater\n"); Putout("than LDA, MATMUL has just decreased the value of N.\n"); sprintf(outline,"N has been set to %d\n",n); Putout(outline); } return; } /******************************************************************************/ void Getn ( char *str ) /******************************************************************************/ /* GETN reads a new value of N from the user. */ { char outline[80]; char s[80]; int temp; int i; int j; int l; int mult; if ( !str[0] ) { Putout("I could not understand your definition of N.\n"); return; } l=strlen(str); memset(s,'\0',l); /* Get first number */ for(i=0;( (i LENA) { Putout("The value of N that you chose is not acceptable!\n"); sprintf(outline,"N must be no greater than %d\n",LENA); Putout(outline); } else { n=temp; nlo=temp; nhi=temp; sprintf(outline,"N has been set to %d\n",n); Putout(outline); } /* Read second number */ /* if (str[i] == NULL) */ if ( !str[i] ) return; i++; for(j=0;((i LENA) { Putout("The value of NHI that you chose is not acceptable!\n"); sprintf(outline,"NHI must be no greater than %d\n",LENA); Putout(outline); } else { nhi=temp; sprintf(outline,"NHI has been set to %d\n",nhi); Putout(outline); ninc=1; } /* Check for multipy */ i++; if (str[i] == '*') { mult=TRUE; i++; } else mult=FALSE; /* Get third and last number */ if ( !str[i] ) return; for(j=0;((i nhi) && (ninc > 1)) ninc*=-1; sprintf(outline,"NINC has been set to %d\n",ninc); Putout(outline); } else { ninc=0; nmult=temp; sprintf(outline,"NMULT has been set to %d\n",nmult); Putout(outline); } return; } /******************************************************************************/ void Getord(char *str, int *error) /******************************************************************************/ /* GETORD reads a new value of ORDER from the user. */ { char ctemp[10],outline[80]; strncpy(ctemp,str,10); if ( (strncmp(ctemp,"ALL",3)!=0) && (strncmp(ctemp,"IJK",3)!=0) && (strncmp(ctemp,"IKJ",3)!=0) && (strncmp(ctemp,"JIK",3)!=0) && (strncmp(ctemp,"JKI",3)!=0) && (strncmp(ctemp,"KIJ",3)!=0) && (strncmp(ctemp,"KJI",3)!=0) && (strncmp(ctemp,"R8_IJK",6)!=0) && (strncmp(ctemp,"UIJK",4)!=0) && (strncmp(ctemp,"IUJK",4)!=0) && (strncmp(ctemp,"IJUK",4)!=0) && (strncmp(ctemp,"PIJKA",5)!=0) && (strncmp(ctemp,"PIJKP",5)!=0) && (strncmp(ctemp,"SDOT",4)!=0) && (strncmp(ctemp,"TAXPYC",6)!=0) && (strncmp(ctemp,"TAXPYR",6)!=0) && (strncmp(ctemp,"SAXPYC",6)!=0) && (strncmp(ctemp,"IIJK",4)!=0) && (strncmp(ctemp,"TDOT",4)!=0) && (strncmp(ctemp,"SAXPYR",6)!=0) && (strncmp(ctemp,"SGEMM",5)!=0) && (strncmp(ctemp,"SGEMMS",6)!=0) && (strncmp(ctemp,"MIJK",4)!=0) &&(strncmp(ctemp,"NIJK",4)!=0) && (strncmp(ctemp,"SIJK",4)!=0) &&(strncmp(ctemp,"MXMA",4)!=0)) { Putout("The order you chose was not a valid choice.\n"); Putout("\nYour command was not carried out.\n"); *error=1; } else { sprintf(outline,"ORDER has been set to %s\n",ctemp); Putout(outline); strcpy(order,ctemp); } return; } /******************************************************************************/ void Getsho(char *str,int lval) /******************************************************************************/ /* GETSHO gets information from the user about what is to be printed out as results after a multiplication is carried out. */ { if ((strncmp(str,"ALL",3) == 0) || (strncmp(str,"all",3) == 0)) mshow=langshow=ashow=cshow=fshow=lshow=nshow=noshow=tshow=lval; else if ((strncmp(str,"MACHINE",7) == 0) || (strncmp(str,"machine",7) == 0)) mshow=lval; else if ((strncmp(str,"LANGUAGE",8) == 0) || (strncmp(str,"language",8) == 0)) langshow=lval; else if ((strncmp(str,"A(N,N)",6) == 0) || (strncmp(str,"a(n,n)",6) == 0)) ashow=lval; else if ((strncmp(str,"ORDER",5) == 0) || (strncmp(str,"order",5) == 0)) cshow=lval; else if ((strncmp(str,"MFLOPS",6) == 0) || (strncmp(str,"mflops",6) == 0)) fshow=lval; else if ((strncmp(str,"LDA",3) == 0) || (strncmp(str,"lda",3) == 0)) lshow=lval; else if ((str[0]=='N') || (str[0]=='n')) nshow=lval; else if ((strncmp(str,"OPS",3) == 0) || (strncmp(str,"ops",3) == 0)) noshow=lval; else if ((strncmp(str,"TIME",4) == 0) || (strncmp(str,"time",4) == 0)) tshow=lval; else { Putout("That is not a legal name! \n"); Putout("Legal names are ORDER, LDA, N, TIME, OPS, LANGUAGE, MACHINE, MFLOPS and A(N,N).\n"); } return; } /******************************************************************************/ void Hello() /******************************************************************************/ /* HELLO says hello to the user, printing the version, machine, and so on. */ { char out[80]; printf ( "\n" ); printf ( "MATMUL\n" ); printf ( " C version\n" ); printf ( " An interactive demonstration of the speed of"); Putout(" matrix multiplication.\n"); sprintf(out,"Version 1.0c, last compiled on %s. ",LASTMOD); Putout(out); sprintf(out,"This version is for a %s\n",machine[MACHINE]); Putout(out); Putout("\nIf you\'ve never used this program before, type EXPLAIN.\n\n"); return; } /******************************************************************************/ void Help() /******************************************************************************/ /* HELP prints a list of the available commands. */ { Putout("This is the list of legal commands.\n"); if(ifilee) Putout("E Explain what this program does.\n"); Putout("H Help. List the legal commands.\n"); Putout("LDA=value Assigns the leading dimension of arrays.\n"); Putout("M Multiply two matrices.\n"); Putout("N=value Assigns the size of the arrays.\n"); Putout("ORDER=name Chooses the algorithm for multiplication.\n"); Putout("P Prints out current values and results.\n"); Putout("Q Quit.\n"); Putout("NOSHOW=name Value of NAME should not be included in output.\n"); Putout(" NAME=ORDER, LDA, N, TIME, OPS or MFLOPS\n"); Putout("SHOW=name Value of NAME should be included in output.\n"); Putout(" NAME=ORDER, LDA, N, TIME, OPS or MFLOPS\n\n"); return; } /*****************************************************************************/ void r8_ijk ( ) /*****************************************************************************/ /* Purose: R8_IJK uses R8 arithmetic and IJK order. */ { double da[LENA][LENA]; double db[LENA][LENA]; double dc[LENA][LENA]; int i; int j; int k; for ( i = 0; i < n; i++ ) { for ( j = 0; j < n; j++ ) { da[i][j] = 0.0; db[i][j] = 1.0; dc[i][j] = 1.0; } } sec1 = cpu_time ( ); for ( i = 0; i < n; i++ ) { for ( j = 0; j < n; j++ ) { for ( k = 0; k < n; k++ ) { da[i][k] = da[i][k] + db[i][j] * dc[j][k]; } } } sec2 = cpu_time ( ); a[n-1][n-1] = ( float ) da[n-1][n-1]; return; } void NIJK() /****************************************************************************** NIJK multiplies A=B*C using index order IJK with integers instead of floating point numbers. ******************************************************************************/ { int i,j,k; long ia[LENA][LENA]; long ib[LENA][LENA],ic[LENA][LENA]; for(i=0;i 0)) || ((nlo > nhi) && (ninc < 0)) ) { n+=ninc; ido=1; if ( ((n < nhi) && (nhi <= nlo)) || ((n > nhi) && (nhi >= nlo)) ) { ido=0; n=nlo; } } else ido=2; return(ido); } if (nmult > 1) { if ( nlo < nhi) { n= n*nmult; ido=1; if ( ((n < nhi) && (nhi <= nlo)) || ((n > nhi) && (nhi >= nlo)) ) { ido=0; n=nlo; } } else ido = 2; return(ido); } ido=3; return(ido); } /******************************************************************************/ void Pdump() /******************************************************************************/ /* PDUMP writes the timing results to a data file. */ { int i; if(iplot >0 ) { ifiled=fopen(filed,"w"); if(ifiled) for(i=1;i<=iplot;i++) fprintf(ifiled,"%-5s %-3d %-3d %-9.6f %-11d %-9.6f %-6.3f\n", cplot[i],lplot[i],nplot[i],tplot[i],noplot[i],fplot[i],aplot[i]); } else ifiled=NULL; return; } /******************************************************************************/ void Printr() /******************************************************************************/ /* PRINTR prints out the current values of parameters that the user should be interested in, including: the algorithm, the leading dimension, the maximum allowable dimension, the actual size of arrays, the number of multiplications carried out. */ { char outline[80]; sprintf(outline,"The algorithm chosen is %s\n",order); Putout(outline); sprintf(outline,"The leading dimension of arrays, LDA, is %d\n",lda); Putout(outline); sprintf(outline,"The maximum legal choice for LDA is %d\n",LENA); Putout(outline); sprintf(outline,"The actual size of the arrays, N, is %d\n",n); Putout(outline); sprintf(outline,"A total of %d cases have been run.\n",iplot); Putout(outline); return; } void Putinp() /****************************************************************************** PUTINP prints the user's most recent input line to the command file and to the output file. ******************************************************************************/ { size_t i; i=strlen(command); if (i) { if (ifilei) fprintf(ifilei,"%s\n",command); if (ifileo) fprintf(ifileo,"%s\n",command); } else { fprintf(ifilei," \n"); fprintf(ifilei," \n"); } return; } void Putout(char *outstr) /****************************************************************************** PUTOUT prints a line of output to the user's screen, and to the output file. ******************************************************************************/ { size_t i; i=strlen(outstr); if (i) { printf("%s",outstr); if (ifileo) fprintf(ifileo,"%s",outstr); } else { printf("\n"); if (ifileo) fprintf(ifileo,"\n"); } return; } void Record() /****************************************************************************** RECORD stores the results for the latest multiplication into various arrays. ******************************************************************************/ { if(iplot < MAXPLT) { iplot++; aplot[iplot]=a[n-1][n-1]; strcpy(cplot[iplot],order); lplot[iplot]=lda; nplot[iplot]=n; #if MACHINE == VAX_VMS tot_seconds=sec2-sec1; tplot[iplot]=(float)tot_seconds/100.0; #elif ((MACHINE == IBM_PC) || (MACHINE == IBM_RS6)) tot_seconds=sec2-sec1; tplot[iplot]=(float)tot_seconds/CLOCKS_PER_SEC; #elif MACHINE == CRAY tot_seconds=sec2-sec1; tplot[iplot]=(float)tot_seconds*0.000000006; #else tot_seconds=sec2-sec1; tot_microseconds=micro2-micro1; tot_microseconds=tot_microseconds+(tot_seconds*1000000); tplot[iplot]=(float)((tot_microseconds/1000000.0)); #endif if(tplot[iplot] == 0.0)tplot[iplot]=1.0; noplot[iplot]=2*n*n*n; fplot[iplot]=0.000001 * (float)((float)noplot[iplot]/tplot[iplot]); } else { Putout("There is no more room to record results.\n"); Putout("This is not a serious error, but it does mean\n"); Putout("and any further results will not make it into\n"); Putout("the results file.\n"); } return; } void Result ( short lchop ) /****************************************************************************** RESULT prints out the results of all, or some, of the multiplications. ******************************************************************************/ { char out[81]; int i; if ( ilo <= ihi ) { /* Prepare the header string. Then chop out items that are not being shown. It's important to do this chopping in reverse order! */ memset(out,'\0',80); sprintf(out," %-8s %3s %3s %10s %8s %10s %8s %-12s %8s\n","ORDER","LDA","N","Time","Ops","MFLOPS","A(N,N)","MACHINE","LANGUAGE"); if (lchop) { if (!langshow) chop(out,70,78); if (!mshow) chop(out,57,69); if (!ashow) chop(out,48,56); if (!fshow) chop(out,37,47); if (!noshow) chop(out,28,36); if (!tshow) chop(out,17,27); if (!nshow) chop(out,13,16); if (!lshow) chop(out,9,12); if (!cshow) chop(out,0,8); Putout(out); } memset(out,'\0',80); /*Print out the values requested.*/ for(i=ilo;i<=min(ihi,iplot);i++) { sprintf(out," %-8s %3d %3d %10.6f %8d %10.6f %8.3f %-12s %8s\n",cplot[i],lplot[i],nplot[i],tplot[i],noplot[i],fplot[i],aplot[i],machine[MACHINE],"C "); if (lchop) { if (!langshow) chop(out,70,78); if (!mshow) chop(out,57,69); if (!ashow) chop(out,48,56); if (!fshow) chop(out,37,47); if (!noshow) chop(out,28,36); if (!tshow) chop(out,17,27); if (!nshow) chop(out,13,16); if (!lshow) chop(out,9,12); if (!cshow) chop(out,0,8); } if (lplot[i] != 0) Putout(out); } } return; } float tdot(int n,float *sx,int incx,float *sy,int incy ) /****************************************************************************** the C code for the TDOT routine ******************************************************************************/ /* PURPOSE Forms the dot product of a vector. INPUT n Number of elements to sum. sx Address of first element of x vector. incx Incrament for the x vector. sy Address of first element of y vector. incy incrament for the y vector. OUPUT sdot Dot product x and y. float returned due to `C' language features. */ { int i; float stemp=0.0e0; if( n<1 ) return( stemp ); if( incx == incy ) { if( incx == 1 ) { /* Both increments = 1 */ for( i=0; i0 ) { /* Equal, positive, non-unit increments. */ for( i=0; i0 ) { /* Equal, positive, non-unit increments. */ for(i=0; i