[pypy-svn] r18688 - pypy/dist/pypy/translator/locality
tismer at codespeak.net
tismer at codespeak.net
Sun Oct 16 15:25:13 CEST 2005
Author: tismer
Date: Sun Oct 16 15:25:11 2005
New Revision: 18688
Added:
pypy/dist/pypy/translator/locality/calltree.py (contents, props changed)
pypy/dist/pypy/translator/locality/projection.py (contents, props changed)
Modified:
pypy/dist/pypy/translator/locality/simulation.py
Log:
checking in, incomplete so far but quite far.
Added: pypy/dist/pypy/translator/locality/calltree.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/locality/calltree.py Sun Oct 16 15:25:11 2005
@@ -0,0 +1,66 @@
+"""
+
+CallTree
+
+An approach to do static call analysis in the PyPy backend
+to produce a somewhat locality-of-reference optimized
+ordering of the function objects in the generated source.
+
+In extent to that, it is planned to produce a non-optimized
+binary from instrumented source code, run some sample
+applications and optimize according to transition statistics.
+This step will only be done if the first approach shows any
+improvement.
+
+Sketch of the algorithm:
+------------------------
+In a first pass, we inspect all function nodes for direct_call
+opcodes and record the callees, if they are constants.
+(Variables will later be tried to find out by re-using the
+information in the translator).
+
+We then run a simulation of calls.
+See pypy/translator/locality/simulation.py.
+
+After that, a poly-dimensional model is computed and morphed
+into a one-dimensional ordering.
+See pypy/translator/locality/projection.py.
+"""
+
+from pypy.objspace.flow.model import Variable, Constant
+
+class CallTree:
+ def __init__(self, funcnodes):
+ self.nodes = funcnodes
+ self.graphs2nodes = self._build_graph2nodes()
+ self.calls = {}
+ for node in self.nodes:
+ self.calls[node] = self.find_callees(node)
+
+ def _build_graph2nodes(self):
+ dic = {}
+ for node in self.nodes:
+ dic[node.obj.graph] = node
+ return dic
+
+ def find_callees(self, node):
+ graph = node.obj.graph
+ res = []
+ for block in graph.iterblocks():
+ for op in block.operations:
+ if op.opname == 'direct_call':
+ fnarg = op.args[0]
+ if isinstance(fnarg, Constant):
+ fnptr = fnarg.value
+ fn = fnptr._obj
+ graph = fn.graph
+ try:
+ callednode = self.graphs2nodes[graph]
+ except KeyError:
+ print "No node found for graph %s" % graph.name
+ continue
+ else:
+ res.append(callednode)
+ else:
+ print "Node %s calls Variable %s" % (node, fnarg)
+ return res
Added: pypy/dist/pypy/translator/locality/projection.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/locality/projection.py Sun Oct 16 15:25:11 2005
@@ -0,0 +1,73 @@
+"""
+
+Turning function dependencies into linear order
+-----------------------------------------------
+
+The purpose of this module is to calculate a good linear
+ordering of functions, according to call transition
+statistics.
+
+Every node has some connections to other nodes, expressed
+in terms of transition frequencies. As a starting point,
+one could assign every node its own dimension. All transitions
+would therefore be orthogonal to each other. The resulting
+vector space would be quite huge.
+
+Instead, we use a different approach:
+
+For a node having t transitions, we order the transitions
+by decreasing frequencies. The initial position of each
+node is this t-dimensional vector.
+
+The distance between two nodes along a transition is the
+Euclidean distance of the intersecion of the nodes dimensions.
+The transition frequencies define a weight for each transition.
+The weighted distance between two nodes
+"""
+
+from pypy.translator.locality.simulation import DemoNode, DemoSim
+from math import sqrt
+
+class SpaceNode:
+ def __init__(self, node):
+ self.func = node.func
+ self.name = node.name
+
+ def setup(self, relations, weights):
+ self.relations = relations
+ self.weights = weights
+ self.position = weights[:] # just anything to start with
+
+ def distance(self, other):
+ # using the nice property of zip to give the minimum length
+ dist = 0.0
+ for x1, x2 in zip(self.position, other.position):
+ d = x2 - x1
+ dist += d * d
+ return sqrt(dist)
+
+ def lonelyness(self):
+ # get the sum of weighted distances
+ lonely = 0.0
+ for weight, relative in zip(self.weights, self.relations):
+ lonely += weight * self.distance(relative)
+ return lonely
+
+ def corrvector(self):
+ pass # XXX continue here
+
+class SpaceGraph:
+ def __init__(self, simgraph):
+ mapping = {}
+ for simnode in simgraph.nodes:
+ mapping[simnode] = SpaceNode(simnode)
+ self.nodes = [mapping[simnode] for simnode in simgraph.nodes]
+ for simnode in simgraph.nodes:
+ relations, weights = simnode.get_relations()
+ relations = [mapping[rel] for rel in relations]
+ node = mapping[simnode]
+ node.setup(relations, weights)
+
+if __name__ == '__main__':
+ from pypy.translator.locality.simulation import test
+ g = SpaceGraph(test())
Modified: pypy/dist/pypy/translator/locality/simulation.py
==============================================================================
--- pypy/dist/pypy/translator/locality/simulation.py (original)
+++ pypy/dist/pypy/translator/locality/simulation.py Sun Oct 16 15:25:11 2005
@@ -26,8 +26,20 @@
self.func = func
self.name = self._get_name(func)
self.callees = []
+ self._callers = None # computed
self.calls = 0
+ def __repr__(self):
+ return '(%s)' % self.name
+
+ def __cmp__(self, other):
+ if isinstance(other, self.__class__):
+ return cmp(self.name, other.name)
+ return cmp(id(self), id(other))
+
+ def __hash__(self):
+ return id(self)
+
def _get_name(self, func):
# to be overridden
return func.__name__
@@ -49,6 +61,28 @@
def simulate_call(self, weight=1):
self.calls += weight
+ # calls and returns are symmetric. We provide a callers
+ # interface that is computed on demand.
+
+ def _get_callers(self):
+ if not self.sim._callers_computed:
+ self.sim._compute_callers()
+ return self.callers
+ callers = property(_get_callers)
+
+ def get_relations(self):
+ # get callees and callers with frequency, ordered
+ # by decreasing frequency and then by name.
+ ret = []
+ for node in self.callees:
+ freq = self.sim.transitions[ (self, node) ]
+ ret.append( (-freq, node) )
+ for node in self.callers:
+ freq = self.sim.transitions[ (node, self) ]
+ ret.append( (-freq, node) )
+ ret.sort()
+ freqs, nodes = zip(*ret)
+ return nodes, [-freq for freq in freqs]
class DemoSim:
def __init__(self, funcnodes, nodefactory=DemoNode):
@@ -67,6 +101,7 @@
callee = name2node[name]
node.callees.append(callee)
self.transitions[ (node, callee) ] = 0
+ self._callers_computed = False
def _find_names_width(self):
n = 0
@@ -78,6 +113,7 @@
self.transitions[ (caller, callee) ] += weight
def run(self, reps=1, root=0):
+ self._callers_computed = False
self.repetitions_per_call = reps
root = self.nodes[root]
root.call()
@@ -113,6 +149,7 @@
# the transitions in a weighted manner.
# this allows us to handle recursions as well.
# first, stimulate nodes if no transitions are pending
+ self._callers_computed = False
if not self.pending:
if root is not None:
startnodes = [self.nodes[root]]
@@ -135,6 +172,17 @@
while self.pending:
self.simulate(call_prob)
+ def _compute_callers(self):
+ nodes = {}
+ for node in self.nodes:
+ nodes[node] = node
+ node.callers = []
+ returns = [ (callee, caller)
+ for caller, callee in self.transitions.keys()]
+ returns.sort()
+ for callee, caller in returns:
+ nodes[callee].callers.append(caller)
+
# sample functions for proof of correctness
def test(debug=False):
@@ -157,6 +205,7 @@
sim.sim_all(prob)
state2 = sim.get_state()
assert state1 == state2
+ return sim
if __name__ == '__main__':
test()
More information about the Pypy-commit
mailing list