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