During last night's meeting a question was raised about benchmarks for the python language, or how fast it is relative to other computer languages. Since I am a computer language buff, I will attempt to give a more informative answer to this question, (to augment and correct my utterance of last evening).
Basically a benchmark is an implementation of a timing procedure, that determines how fast a specified program runs in a given computer language, or a software manufacturer's implementation of a given language. Various accounts have been written as to what should be the components of the ideal benchmark, (using the sieve of Eratosthenes, etc.). Of course, these, can give an idea as to how fast various languages perform. However, a benchmark should be a reflection of the type of programs, you are most interested in using.
If your applications will mostly be using disk file i/o, or large memory transfers, then a number crunching algorithm, as a benchmark, may not give you a relevant answer. Even, if you are focused on number crunching, the numerical algorithms used in calculating say, perspective views in a graphics program may be quite different from a program performing finite element analysis, or number theory conjectures.
(Urban Legend Gossip Note: a software manufacturer may fine tune or optimize their product to run more quickly with the well known benchmark algorithms.)
The above then becomes an argument for designing your own benchmark. By carefully noting what you want to perform with your programs, you can determine what kind of routines will be used. These are the routines you should use in your benchmark.
Having said the above, I will give a benchmark with results, as an example --knowing full well that that I can not please everyone-- the results will only be an indication of run times that may or may not be relevant to your application needs.
In my benchmark, I decided to test run times for: function (or subroutine) calls, and basic arithmetic. I decided in using a not too generally well known algorithm, but one having documented results.
The benchmark I chose, consists of an algorithm that calculates the length maxima, and the maxima of height maxima, of consecutive hailstone numbers from 1 to 100 000. For the definition and properties of hailstone numbers, as used here, refer to the Collatz conjecture. One of the earliest references to this with tabulated results is by:
Brian Hayes SCIENTIFIC AMERICAN (Computer Recreations) Jan. 1984, page 10.
No matter what language is used, all of my benchmark programs are written in the same format. This consists of a main program that writes an output table to a data file, the table entries are calculated by calling a single function or a subroutine.
The benchmark test suite consists of programs written in: C, Java, Python, and a hybrid (consisting of a main routine in Python with a C subroutine). Here are the results...
Running table.c (written in C), elapsed time is:
real 0m0.500s user 0m0.500s sys 0m0.000s
Running table.java (written in Java), elapsed time is:
real 0m15.413s user 0m14.930s sys 0m0.070s
Running table.py (written in Python), elapsed time is:.
real 1m57.656s user 1m57.620s sys 0m0.030s
Running combo.py (written in Python, with a subroutine in C), elapsed time is:
real 0m5.161s user 0m5.090s sys 0m0.070s
Here is the source code...
****************************** table.c *************************************** #include <stdio.h> #include <stdlib.h> #include <sys/types.h>
/* prototypes */ void hsTry(long int, long int *); void hsDisp(void);
/* globals */ static int maxTry =100000; static char fname1[] = "Hsc.data";
main() { hsDisp(); }
void hsDisp(void) { /* prints the longest path and largest height, (as encountered so far) */ typedef struct { char hst; long int hs; long int hsMax; } rcdHM;
int i =0, ecMPH =1; long int hs = 0, hsMaxPath =0, hsMaxHeight =0, hsPH[2] ={0, 0}; rcdHM *hsMPH; rcdHM *idxMPH ;
FILE *fil1;
hsMPH = (rcdHM *) calloc(ecMPH,sizeof(rcdHM)); if (!hsMPH) { printf("\n calloc error \n"); exit(1);} idxMPH =hsMPH;
while (hs < maxTry) { hs++; hsTry(hs, hsPH); if (hsPH[0] > hsMaxPath) { hsMaxPath =hsPH[0]; ecMPH++; hsMPH = (rcdHM *) realloc(hsMPH, ecMPH * sizeof(rcdHM)); if (hsMPH == NULL) { printf("realloc error"); exit(1);} else { idxMPH++; idxMPH->hst ='l'; idxMPH->hs =hs; idxMPH->hsMax =hsMaxPath; } } if (hsPH[1] > hsMaxHeight) { hsMaxHeight =hsPH[1]; ecMPH++; hsMPH = (rcdHM *) realloc(hsMPH, ecMPH * sizeof(rcdHM)); if (hsMPH == NULL) { printf("realloc error"); exit(1);} else { idxMPH++; idxMPH->hst ='h'; idxMPH->hs =hs; idxMPH->hsMax =hsMaxHeight; } } }
if ( !(fil1=fopen(fname1,"w")) ) exit(1); fprintf(fil1, "calculates hailstone numbers \n"); fprintf(fil1, " hs path maxima \n"); idxMPH =hsMPH; for(i=1;i <= ecMPH; i++,idxMPH++) if (idxMPH->hst=='l') {fprintf(fil1, " %9i %9i \n", idxMPH->hs, idxMPH->hsMax);}
idxMPH =hsMPH; fprintf(fil1, "\n hs height maxima \n"); for(i=1;i <= ecMPH; i++,idxMPH++) if (idxMPH->hst=='h') {fprintf(fil1, " %9i %9i \n", idxMPH->hs, idxMPH->hsMax);}
free(hsMPH); fclose(fil1); /* clean up */ }
void hsTry(long int hs, long int hsPH[]) { /* input positive integer, returns path length and maximum height */ /* PathLength = hsPH[0], Height = hsPH[1] */ hsPH[0] =0; if (hs <= 0) printf("out of bounds \n"); while(1) { if (hs == 1) break; else if (hs%2 == 0) hs =hs/2; else if (hs%2 == 1) { hs =(hs*3)+1; if (hs > hsPH[1]) hsPH[1] =hs; } hsPH[0]++; } }
****************************** table.java *************************************** import java.io.*; import java.lang.*; import java.text.*; import java.util.*; import java.lang.reflect.Array; import java.lang.Math.*;
public class Hsj { static int maxTry = 100000; static PrintWriter out = new PrintWriter(System.out, true);
public static long [] hsTry(long hs, long [] hsPH) { // input positive integer, returns path length and maximum height /* PathLength = hsPH[0], Height = hsPH[1] */ hsPH[0] =0; if (hs >= 0) { while (true) { if (hs == 1) break; else if (hs%2 == 0) hs/=2; else if (hs%2 == 1) { hs =(hs+hs+hs)+1; if (hs > hsPH[1]) hsPH[1] =hs; } hsPH[0]++; } } return hsPH; }
public static void hsDisp() throws IOException { // Prints the longest path and largest height, (as encountered so far) File outputFile = new File("Hsj.data"); FileWriter output = new FileWriter(outputFile);
Vector hsMP = new Vector(); Vector hsMH = new Vector();
long hs =0, hsMaxPath =0, hsMaxHeight =0;
long hsPH[] = new long [2];
while (hs < maxTry) { hs+=1; hsTry(hs, hsPH); if (hsPH[0] > hsMaxPath) { hsMaxPath =hsPH[0]; hsMP.add(new Long(hs)); hsMP.add(new Long(hsMaxPath)); } if (hsPH[1] > hsMaxHeight) { hsMaxHeight =hsPH[1]; hsMH.add(new Long(hs)); hsMH.add(new Long(hsMaxHeight)); } } output.write(" calculate hailstone numbers \n");
DecimalFormat hfmt = new DecimalFormat(" 000000000000"); output.write(" hs length maxima \n");
for (int i =0; i < hsMP.size()-1; i+=2) { output.write(hfmt.format(hsMP.elementAt(i)) +hfmt.format(hsMP.elementAt(i+1))+"\n"); }
output.write("\n hs height maxima \n"); for (int i=0; i < hsMH.size()-1; i+=2) { output.write(hfmt.format(hsMH.elementAt(i)) +hfmt.format(hsMH.elementAt(i+1))+"\n"); }
output.close(); }
public static void main (String args[]) { try{ hsDisp(); }catch(IOException event) { out.println("io error");} }
}
****************************** table.py *************************************** #! $(env_path)/env $(py_path)/python # above parameters need to be configured to your own system
# Basic Hailstone number properties, (refer to Collatz conjecture)
def hsTry(hs): 'Find path length and maximum height for a hailstone number.' length = 0 ; height = hs
while (hs>1): if hs%2 == 0: hs/=2 elif hs%2 == 1: hs = hs+hs+hs+1 if hs > height: height =hs length+=1 return [length, height]
def hsDisp(maxTry): '''Write to file, length maxima and maxima of height maxima, of a sequence of hailstone numbers up to maxTry.'''
hs =0; hsMaxLength =0; hsMaxHeight =0 hsPH =[0,0]; hsML =[]; hsMH =[];
while (hs < maxTry): hs+=1 hsPH =hsTry(hs) if (hsPH[0] > hsMaxLength): hsMaxLength =hsPH[0] hsML.append((hs,hsMaxLength)) if (hsPH[1] > hsMaxHeight): hsMaxHeight =hsPH[1] hsMH.append((hs,hsMaxHeight))
fil=open('Hsp.data','w') fil.write('calculate hailstone numbers \n\n') fil.write(" hs length maxima \n") for x,y in hsML: fil.write(' %9d %9d\n' % (x,y)) fil.write("\n hs height maxima \n") for x,y in hsMH: fil.write(' %9d %9d\n' % (x,y)) fil.close
hsDisp(100000)
****************************** combo.py *************************************** #! $(env_path)/env $(py_path)/python # above parameters need to be configured to your own system
# Basic Hailstone number properties, (refer to Collitz conjecture)
# Check for module include path, if not found include it. import sys if filter(lambda x: sys.path[x]=='/examples', range(0,len(sys.path)))==[] : (sys.path).append('/examples')
import Calc
def hsDisp(maxTry): '''write to file, length maxima and maxima of height maxima, of a sequence of hailstone numbers up to maxTry'''
hs =0; hsMaxLength =0; hsMaxHeight =0 hsPH =[0,0]; hsML =[]; hsMH =[];
while (hs < maxTry): hs+=1 hsPH =Calc.hsTry(hs) if (hsPH[0] > hsMaxLength): hsMaxLength =hsPH[0] hsML.append((hs,hsMaxLength)) if (hsPH[1] > hsMaxHeight): hsMaxHeight =hsPH[1] hsMH.append((hs,hsMaxHeight))
fil=open('table.data','w') fil.write('calculate hailstone numbers \n\n') fil.write(" hs length maxima \n") for x,y in hsML: fil.write(' %9d %9d\n' % (x,y)) fil.write("\n hs height maxima \n") for x,y in hsMH: fil.write(' %9d %9d\n' % (x,y)) fil.close
hsDisp(100000)
****************************** Calc.c (called C subroutine) ******************* #include "Python.h"
static void hextTry(long int, long int *); static PyObject *
hs_call(self, args) PyObject *self; PyObject *args; { long int hs, hsPH[2]={0,0}; if (!PyArg_ParseTuple(args, "l", &hs)) return NULL; hsPH[1]=hs; hextTry(hs, hsPH); return Py_BuildValue("[l,l]",hsPH[0],hsPH[1]); }
static PyMethodDef HextMethods[] = { {"hsTry", hs_call, METH_VARARGS, "Find path length and maximum height for a hailstone number." }, {NULL, NULL, 0, NULL} /* Sentinel (depends on python version) */ };
static void hextTry(long int hs, long int hsPH[]) { /* input positive integer, returns path length and maximum height */ /* PathLength = hsPH[0], Height = hsPH[1] */ hsPH[0] =0; if (hs <= 0) printf("out of bounds \n"); while(1) { if (hs == 1) break; else if (hs%2 == 0) hs =hs/2; else if (hs%2 == 1) { hs =(hs*3)+1; if (hs > hsPH[1]) hsPH[1] =hs; } hsPH[0]++; } }
void initHext() { (void) Py_InitModule("Hext",HextMethods); }
In conclusion, I determined that, Python is suited as a prototyping language. Not shown in the timing data, is how quickly it took me to develop complete running error free code in python. This was much less than for any of the other languages. That is, you can develop a scratch pad or prototype application using python, that can give a rough or partial estimate of results for a problem. If need be, the application can be rewritten in a faster language after some of the bugs and coding problems have been initially solved by using python. In the long run, much development time can be saved by using this approach.
-Ihor