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
)
13 def writeTimingCall(self
, filename
, numFuncs
, funcsCalled
, totalCalls
):
14 """Echo some comments and invoke both versions of toy"""
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
)
37 class KScriptGenerator
:
38 """Used to generate random Kaleidoscope code"""
39 def __init__(self
, filename
):
40 self
.kfile
= open(filename
, 'w')
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
53 def updateTotalCallCount(self
, callee
):
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
)
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
)
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
:
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
)
85 def setCallWeighting(self
, weight
):
86 """ Sets the probably of generating a function call"""
87 self
.callWeighting
= weight
89 def writeln(self
, line
):
90 self
.kfile
.write(line
+ '\n')
92 def writeComment(self
, comment
):
93 self
.writeln('# ' + comment
)
95 def writeEmptyLine(self
):
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()
119 def writeRandomOperation(self
, LValue
, LHS
, RHS
):
120 shouldCallFunc
= (self
.lastFuncNum
> 2 and random
.random() < self
.callWeighting
)
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
))
126 possibleOperations
= ["+", "-", "*", "/"]
127 operation
= random
.choice(possibleOperations
)
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)))
138 self
.writeln(" %s = %s %s %s :" % (LValue
, LHS
, operation
, RHS
))
140 def getNextFuncNum(self
):
141 result
= self
.nextFuncNum
142 self
.nextFuncNum
+= 1
143 self
.lastFuncNum
= result
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
157 # Write some random operations
158 for i
in range(elements
):
159 self
.writeRandomOperation(first
, second
, third
)
160 # Rotate the variables
165 self
.writeln(" " + third
+ ";")
166 self
.writeEmptyLine()
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
)
176 def writeFinalFunctionCounts(self
):
177 self
.writeComment("Called %d of %d functions" % (len(self
.calledFunctions
), self
.lastFuncNum
))
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
)
211 # Execution begins here
214 timingScript
= TimingScriptGenerator("time-toy.sh", "timing-data.txt")
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)]
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
)