Home | History | Annotate | Download | only in complete
      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