[pypy-commit] benchmarks default: Add a benchmark performing inserts/finds/removes on a skip-list. We currently

Raemi noreply at buildbot.pypy.org
Wed Apr 9 14:57:56 CEST 2014


Author: Remi Meier <remi.meier at inf.ethz.ch>
Branch: 
Changeset: r247:1100457d9161
Date: 2014-04-09 14:58 +0200
http://bitbucket.org/pypy/benchmarks/changeset/1100457d9161/

Log:	Add a benchmark performing inserts/finds/removes on a skip-list. We
	currently get slower with more threads which we should try to avoid

diff --git a/multithread/skiplist/skiplist.py b/multithread/skiplist/skiplist.py
new file mode 100644
--- /dev/null
+++ b/multithread/skiplist/skiplist.py
@@ -0,0 +1,138 @@
+# https://github.com/kunigami/blog-examples/tree/master/2012-09-23-skip-list
+
+from common.abstract_threading import atomic, Future
+import time, threading
+
+import random
+
+thread_local = threading.local()
+
+class SkipNode:
+    """A node from a skip list"""
+    def __init__(self, height = 0, elem = None):
+        self.elem = elem
+        self.next = [None]*height
+
+class SkipList:
+    def __init__(self):
+        self.head = SkipNode()
+        self.len = 0
+        self.maxHeight = 0
+
+    def __len__(self):
+        return self.len
+
+    def find(self, elem, update = None):
+        if update == None:
+            update = self.updateList(elem)
+        if len(update) > 0:
+            candidate = update[0].next[0]
+            if candidate != None and candidate.elem == elem:
+                return candidate
+        return None
+
+    def contains(self, elem, update = None):
+        return self.find(elem, update) != None
+
+    def randomHeight(self):
+        height = 1
+        while thread_local.rnd.randint(1, 2) != 1:
+            height += 1
+        return height
+
+    def updateList(self, elem):
+        update = [None] * self.maxHeight
+        x = self.head
+        for i in reversed(xrange(self.maxHeight)):
+            while x.next[i] != None and x.next[i].elem < elem:
+                x = x.next[i]
+            update[i] = x
+        return update
+
+    def insert(self, elem):
+        node = SkipNode(self.randomHeight(), elem)
+
+        # conflicts with every find():
+        self.maxHeight = max(self.maxHeight, len(node.next))
+
+        while len(self.head.next) < len(node.next):
+            self.head.next.append(None)
+
+        update = self.updateList(elem)
+        if self.find(elem, update) == None:
+            for i in xrange(len(node.next)):
+                node.next[i] = update[i].next[i]
+                update[i].next[i] = node
+            self.len += 1
+
+    def remove(self, elem):
+        update = self.updateList(elem)
+        x = self.find(elem, update)
+        if x != None:
+            for i in reversed(range(len(x.next))):
+                update[i].next[i] = x.next[i]
+                if self.head.next[i] == None:
+                    self.maxHeight -= 1
+            self.len -= 1
+
+    def printList(self):
+        for i in range(len(self.head.next)-1, -1, -1):
+            x = self.head
+            while x.next[i] != None:
+                print x.next[i].elem,
+                x = x.next[i]
+            print ''
+
+
+
+OPS = [SkipList.find] * 98 + [SkipList.insert, SkipList.remove]
+
+
+def task(id, slist, ops):
+    print "start task with %s ops" % ops
+    r = random.Random()
+    r.seed(id)
+    thread_local.rnd = r
+
+    for _ in xrange(ops):
+        op = r.choice(OPS)
+        elem = r.randint(1, 10000)
+        with atomic:
+            op(slist, elem)
+
+    print "task ended"
+
+
+def chunks(l, n):
+    """ Yield successive n-sized chunks from l. """
+    for i in xrange(0, len(l), n):
+        yield l[i:i+n]
+
+
+
+def run(threads=2, operations=2000000):
+    threads = int(threads)
+    operations = int(operations)
+
+    thread_local.rnd = random
+
+    slist = SkipList()
+    for _ in xrange(1000):
+        slist.insert(random.randint(1, 1000))
+
+    c_len = operations // threads
+    fs = []
+    for i in xrange(threads):
+        fs.append(Future(task, i, slist, c_len))
+    for f in fs:
+        f()
+
+    # print "list:"
+    # slist.printList()
+
+
+
+
+
+if __name__ == '__main__':
+    run()
diff --git a/multithread/threadworms/threadworms.py b/multithread/threadworms/threadworms.py
--- a/multithread/threadworms/threadworms.py
+++ b/multithread/threadworms/threadworms.py
@@ -56,7 +56,7 @@
 
 
     def run(self):
-        for _ in xrange(NUM_STEPS):
+        for _ in range(NUM_STEPS):
             if self.rnd.randint(0, 100) < 20: # 20% to change direction
                 self.direction = self.rnd.choice((UP, DOWN, LEFT, RIGHT))
 
@@ -127,7 +127,7 @@
 def run(worms=2, steps=10000000):
     global DISPLAYSURF, NUM_WORMS, NUM_STEPS, GRID
     NUM_WORMS = int(worms)
-    NUM_STEPS = int(steps) / NUM_WORMS
+    NUM_STEPS = int(steps) // NUM_WORMS
 
     GRID = []
     for x in range(CELLS_WIDE):


More information about the pypy-commit mailing list