1*09467b48Spatrick#!/usr/bin/env python 2*09467b48Spatrick 3*09467b48Spatrickfrom __future__ import print_function 4*09467b48Spatrick 5*09467b48Spatrickimport sys 6*09467b48Spatrickimport random 7*09467b48Spatrick 8*09467b48Spatrickclass TimingScriptGenerator: 9*09467b48Spatrick """Used to generate a bash script which will invoke the toy and time it""" 10*09467b48Spatrick def __init__(self, scriptname, outputname): 11*09467b48Spatrick self.timeFile = outputname 12*09467b48Spatrick self.shfile = open(scriptname, 'w') 13*09467b48Spatrick self.shfile.write("echo \"\" > %s\n" % self.timeFile) 14*09467b48Spatrick 15*09467b48Spatrick def writeTimingCall(self, filename, numFuncs, funcsCalled, totalCalls): 16*09467b48Spatrick """Echo some comments and invoke both versions of toy""" 17*09467b48Spatrick rootname = filename 18*09467b48Spatrick if '.' in filename: 19*09467b48Spatrick rootname = filename[:filename.rfind('.')] 20*09467b48Spatrick self.shfile.write("echo \"%s: Calls %d of %d functions, %d total\" >> %s\n" % (filename, funcsCalled, numFuncs, totalCalls, self.timeFile)) 21*09467b48Spatrick self.shfile.write("echo \"\" >> %s\n" % self.timeFile) 22*09467b48Spatrick self.shfile.write("echo \"With MCJIT (original)\" >> %s\n" % self.timeFile) 23*09467b48Spatrick self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"") 24*09467b48Spatrick self.shfile.write(" -o %s -a " % self.timeFile) 25*09467b48Spatrick self.shfile.write("./toy -suppress-prompts -use-mcjit=true -enable-lazy-compilation=false < %s > %s-mcjit.out 2> %s-mcjit.err\n" % (filename, rootname, rootname)) 26*09467b48Spatrick self.shfile.write("echo \"\" >> %s\n" % self.timeFile) 27*09467b48Spatrick self.shfile.write("echo \"With MCJIT (lazy)\" >> %s\n" % self.timeFile) 28*09467b48Spatrick self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"") 29*09467b48Spatrick self.shfile.write(" -o %s -a " % self.timeFile) 30*09467b48Spatrick self.shfile.write("./toy -suppress-prompts -use-mcjit=true -enable-lazy-compilation=true < %s > %s-mcjit-lazy.out 2> %s-mcjit-lazy.err\n" % (filename, rootname, rootname)) 31*09467b48Spatrick self.shfile.write("echo \"\" >> %s\n" % self.timeFile) 32*09467b48Spatrick self.shfile.write("echo \"With JIT\" >> %s\n" % self.timeFile) 33*09467b48Spatrick self.shfile.write("/usr/bin/time -f \"Command %C\\n\\tuser time: %U s\\n\\tsytem time: %S s\\n\\tmax set: %M kb\"") 34*09467b48Spatrick self.shfile.write(" -o %s -a " % self.timeFile) 35*09467b48Spatrick self.shfile.write("./toy -suppress-prompts -use-mcjit=false < %s > %s-jit.out 2> %s-jit.err\n" % (filename, rootname, rootname)) 36*09467b48Spatrick self.shfile.write("echo \"\" >> %s\n" % self.timeFile) 37*09467b48Spatrick self.shfile.write("echo \"\" >> %s\n" % self.timeFile) 38*09467b48Spatrick 39*09467b48Spatrickclass KScriptGenerator: 40*09467b48Spatrick """Used to generate random Kaleidoscope code""" 41*09467b48Spatrick def __init__(self, filename): 42*09467b48Spatrick self.kfile = open(filename, 'w') 43*09467b48Spatrick self.nextFuncNum = 1 44*09467b48Spatrick self.lastFuncNum = None 45*09467b48Spatrick self.callWeighting = 0.1 46*09467b48Spatrick # A mapping of calls within functions with no duplicates 47*09467b48Spatrick self.calledFunctionTable = {} 48*09467b48Spatrick # A list of function calls which will actually be executed 49*09467b48Spatrick self.calledFunctions = [] 50*09467b48Spatrick # A comprehensive mapping of calls within functions 51*09467b48Spatrick # used for computing the total number of calls 52*09467b48Spatrick self.comprehensiveCalledFunctionTable = {} 53*09467b48Spatrick self.totalCallsExecuted = 0 54*09467b48Spatrick 55*09467b48Spatrick def updateTotalCallCount(self, callee): 56*09467b48Spatrick # Count this call 57*09467b48Spatrick self.totalCallsExecuted += 1 58*09467b48Spatrick # Then count all the functions it calls 59*09467b48Spatrick if callee in self.comprehensiveCalledFunctionTable: 60*09467b48Spatrick for child in self.comprehensiveCalledFunctionTable[callee]: 61*09467b48Spatrick self.updateTotalCallCount(child) 62*09467b48Spatrick 63*09467b48Spatrick def updateFunctionCallMap(self, caller, callee): 64*09467b48Spatrick """Maintains a map of functions that are called from other functions""" 65*09467b48Spatrick if not caller in self.calledFunctionTable: 66*09467b48Spatrick self.calledFunctionTable[caller] = [] 67*09467b48Spatrick if not callee in self.calledFunctionTable[caller]: 68*09467b48Spatrick self.calledFunctionTable[caller].append(callee) 69*09467b48Spatrick if not caller in self.comprehensiveCalledFunctionTable: 70*09467b48Spatrick self.comprehensiveCalledFunctionTable[caller] = [] 71*09467b48Spatrick self.comprehensiveCalledFunctionTable[caller].append(callee) 72*09467b48Spatrick 73*09467b48Spatrick def updateCalledFunctionList(self, callee): 74*09467b48Spatrick """Maintains a list of functions that will actually be called""" 75*09467b48Spatrick # Update the total call count 76*09467b48Spatrick self.updateTotalCallCount(callee) 77*09467b48Spatrick # If this function is already in the list, don't do anything else 78*09467b48Spatrick if callee in self.calledFunctions: 79*09467b48Spatrick return 80*09467b48Spatrick # Add this function to the list of those that will be called. 81*09467b48Spatrick self.calledFunctions.append(callee) 82*09467b48Spatrick # If this function calls other functions, add them too 83*09467b48Spatrick if callee in self.calledFunctionTable: 84*09467b48Spatrick for subCallee in self.calledFunctionTable[callee]: 85*09467b48Spatrick self.updateCalledFunctionList(subCallee) 86*09467b48Spatrick 87*09467b48Spatrick def setCallWeighting(self, weight): 88*09467b48Spatrick """ Sets the probably of generating a function call""" 89*09467b48Spatrick self.callWeighting = weight 90*09467b48Spatrick 91*09467b48Spatrick def writeln(self, line): 92*09467b48Spatrick self.kfile.write(line + '\n') 93*09467b48Spatrick 94*09467b48Spatrick def writeComment(self, comment): 95*09467b48Spatrick self.writeln('# ' + comment) 96*09467b48Spatrick 97*09467b48Spatrick def writeEmptyLine(self): 98*09467b48Spatrick self.writeln("") 99*09467b48Spatrick 100*09467b48Spatrick def writePredefinedFunctions(self): 101*09467b48Spatrick self.writeComment("Define ':' for sequencing: as a low-precedence operator that ignores operands") 102*09467b48Spatrick self.writeComment("and just returns the RHS.") 103*09467b48Spatrick self.writeln("def binary : 1 (x y) y;") 104*09467b48Spatrick self.writeEmptyLine() 105*09467b48Spatrick self.writeComment("Helper functions defined within toy") 106*09467b48Spatrick self.writeln("extern putchard(x);") 107*09467b48Spatrick self.writeln("extern printd(d);") 108*09467b48Spatrick self.writeln("extern printlf();") 109*09467b48Spatrick self.writeEmptyLine() 110*09467b48Spatrick self.writeComment("Print the result of a function call") 111*09467b48Spatrick self.writeln("def printresult(N Result)") 112*09467b48Spatrick self.writeln(" # 'result('") 113*09467b48Spatrick self.writeln(" putchard(114) : putchard(101) : putchard(115) : putchard(117) : putchard(108) : putchard(116) : putchard(40) :") 114*09467b48Spatrick self.writeln(" printd(N) :"); 115*09467b48Spatrick self.writeln(" # ') = '") 116*09467b48Spatrick self.writeln(" putchard(41) : putchard(32) : putchard(61) : putchard(32) :") 117*09467b48Spatrick self.writeln(" printd(Result) :"); 118*09467b48Spatrick self.writeln(" printlf();") 119*09467b48Spatrick self.writeEmptyLine() 120*09467b48Spatrick 121*09467b48Spatrick def writeRandomOperation(self, LValue, LHS, RHS): 122*09467b48Spatrick shouldCallFunc = (self.lastFuncNum > 2 and random.random() < self.callWeighting) 123*09467b48Spatrick if shouldCallFunc: 124*09467b48Spatrick funcToCall = random.randrange(1, self.lastFuncNum - 1) 125*09467b48Spatrick self.updateFunctionCallMap(self.lastFuncNum, funcToCall) 126*09467b48Spatrick self.writeln(" %s = func%d(%s, %s) :" % (LValue, funcToCall, LHS, RHS)) 127*09467b48Spatrick else: 128*09467b48Spatrick possibleOperations = ["+", "-", "*", "/"] 129*09467b48Spatrick operation = random.choice(possibleOperations) 130*09467b48Spatrick if operation == "-": 131*09467b48Spatrick # Don't let our intermediate value become zero 132*09467b48Spatrick # This is complicated by the fact that '<' is our only comparison operator 133*09467b48Spatrick self.writeln(" if %s < %s then" % (LHS, RHS)) 134*09467b48Spatrick self.writeln(" %s = %s %s %s" % (LValue, LHS, operation, RHS)) 135*09467b48Spatrick self.writeln(" else if %s < %s then" % (RHS, LHS)) 136*09467b48Spatrick self.writeln(" %s = %s %s %s" % (LValue, LHS, operation, RHS)) 137*09467b48Spatrick self.writeln(" else") 138*09467b48Spatrick self.writeln(" %s = %s %s %f :" % (LValue, LHS, operation, random.uniform(1, 100))) 139*09467b48Spatrick else: 140*09467b48Spatrick self.writeln(" %s = %s %s %s :" % (LValue, LHS, operation, RHS)) 141*09467b48Spatrick 142*09467b48Spatrick def getNextFuncNum(self): 143*09467b48Spatrick result = self.nextFuncNum 144*09467b48Spatrick self.nextFuncNum += 1 145*09467b48Spatrick self.lastFuncNum = result 146*09467b48Spatrick return result 147*09467b48Spatrick 148*09467b48Spatrick def writeFunction(self, elements): 149*09467b48Spatrick funcNum = self.getNextFuncNum() 150*09467b48Spatrick self.writeComment("Auto-generated function number %d" % funcNum) 151*09467b48Spatrick self.writeln("def func%d(X Y)" % funcNum) 152*09467b48Spatrick self.writeln(" var temp1 = X,") 153*09467b48Spatrick self.writeln(" temp2 = Y,") 154*09467b48Spatrick self.writeln(" temp3 in") 155*09467b48Spatrick # Initialize the variable names to be rotated 156*09467b48Spatrick first = "temp3" 157*09467b48Spatrick second = "temp1" 158*09467b48Spatrick third = "temp2" 159*09467b48Spatrick # Write some random operations 160*09467b48Spatrick for i in range(elements): 161*09467b48Spatrick self.writeRandomOperation(first, second, third) 162*09467b48Spatrick # Rotate the variables 163*09467b48Spatrick temp = first 164*09467b48Spatrick first = second 165*09467b48Spatrick second = third 166*09467b48Spatrick third = temp 167*09467b48Spatrick self.writeln(" " + third + ";") 168*09467b48Spatrick self.writeEmptyLine() 169*09467b48Spatrick 170*09467b48Spatrick def writeFunctionCall(self): 171*09467b48Spatrick self.writeComment("Call the last function") 172*09467b48Spatrick arg1 = random.uniform(1, 100) 173*09467b48Spatrick arg2 = random.uniform(1, 100) 174*09467b48Spatrick self.writeln("printresult(%d, func%d(%f, %f) )" % (self.lastFuncNum, self.lastFuncNum, arg1, arg2)) 175*09467b48Spatrick self.writeEmptyLine() 176*09467b48Spatrick self.updateCalledFunctionList(self.lastFuncNum) 177*09467b48Spatrick 178*09467b48Spatrick def writeFinalFunctionCounts(self): 179*09467b48Spatrick self.writeComment("Called %d of %d functions" % (len(self.calledFunctions), self.lastFuncNum)) 180*09467b48Spatrick 181*09467b48Spatrickdef generateKScript(filename, numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting, timingScript): 182*09467b48Spatrick """ Generate a random Kaleidoscope script based on the given parameters """ 183*09467b48Spatrick print("Generating " + filename) 184*09467b48Spatrick print(" %d functions, %d elements per function, %d functions between execution" % 185*09467b48Spatrick (numFuncs, elementsPerFunc, funcsBetweenExec)) 186*09467b48Spatrick print(" Call weighting = %f" % callWeighting) 187*09467b48Spatrick script = KScriptGenerator(filename) 188*09467b48Spatrick script.setCallWeighting(callWeighting) 189*09467b48Spatrick script.writeComment("===========================================================================") 190*09467b48Spatrick script.writeComment("Auto-generated script") 191*09467b48Spatrick script.writeComment(" %d functions, %d elements per function, %d functions between execution" 192*09467b48Spatrick % (numFuncs, elementsPerFunc, funcsBetweenExec)) 193*09467b48Spatrick script.writeComment(" call weighting = %f" % callWeighting) 194*09467b48Spatrick script.writeComment("===========================================================================") 195*09467b48Spatrick script.writeEmptyLine() 196*09467b48Spatrick script.writePredefinedFunctions() 197*09467b48Spatrick funcsSinceLastExec = 0 198*09467b48Spatrick for i in range(numFuncs): 199*09467b48Spatrick script.writeFunction(elementsPerFunc) 200*09467b48Spatrick funcsSinceLastExec += 1 201*09467b48Spatrick if funcsSinceLastExec == funcsBetweenExec: 202*09467b48Spatrick script.writeFunctionCall() 203*09467b48Spatrick funcsSinceLastExec = 0 204*09467b48Spatrick # Always end with a function call 205*09467b48Spatrick if funcsSinceLastExec > 0: 206*09467b48Spatrick script.writeFunctionCall() 207*09467b48Spatrick script.writeEmptyLine() 208*09467b48Spatrick script.writeFinalFunctionCounts() 209*09467b48Spatrick funcsCalled = len(script.calledFunctions) 210*09467b48Spatrick print(" Called %d of %d functions, %d total" % (funcsCalled, numFuncs, script.totalCallsExecuted)) 211*09467b48Spatrick timingScript.writeTimingCall(filename, numFuncs, funcsCalled, script.totalCallsExecuted) 212*09467b48Spatrick 213*09467b48Spatrick# Execution begins here 214*09467b48Spatrickrandom.seed() 215*09467b48Spatrick 216*09467b48SpatricktimingScript = TimingScriptGenerator("time-toy.sh", "timing-data.txt") 217*09467b48Spatrick 218*09467b48SpatrickdataSets = [(5000, 3, 50, 0.50), (5000, 10, 100, 0.10), (5000, 10, 5, 0.10), (5000, 10, 1, 0.0), 219*09467b48Spatrick (1000, 3, 10, 0.50), (1000, 10, 100, 0.10), (1000, 10, 5, 0.10), (1000, 10, 1, 0.0), 220*09467b48Spatrick ( 200, 3, 2, 0.50), ( 200, 10, 40, 0.10), ( 200, 10, 2, 0.10), ( 200, 10, 1, 0.0)] 221*09467b48Spatrick 222*09467b48Spatrick# Generate the code 223*09467b48Spatrickfor (numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting) in dataSets: 224*09467b48Spatrick filename = "test-%d-%d-%d-%d.k" % (numFuncs, elementsPerFunc, funcsBetweenExec, int(callWeighting * 100)) 225*09467b48Spatrick generateKScript(filename, numFuncs, elementsPerFunc, funcsBetweenExec, callWeighting, timingScript) 226*09467b48Spatrickprint("All done!") 227