tests/perf_bench: Add some benchmarks from python-performance.

From https://github.com/python/pyperformance commit
6690642ddeda46fc5ee6e97c3ef4b2f292348ab8
This commit is contained in:
Damien George
2019-06-26 14:24:13 +10:00
parent e92c9aa9c9
commit 127714c3af
6 changed files with 1182 additions and 0 deletions
+274
View File
@@ -0,0 +1,274 @@
# Source: https://github.com/python/pyperformance
# License: MIT
# create chaosgame-like fractals
# Copyright (C) 2005 Carl Friedrich Bolz
import math
import random
class GVector(object):
def __init__(self, x=0, y=0, z=0):
self.x = x
self.y = y
self.z = z
def Mag(self):
return math.sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2)
def dist(self, other):
return math.sqrt((self.x - other.x) ** 2
+ (self.y - other.y) ** 2
+ (self.z - other.z) ** 2)
def __add__(self, other):
if not isinstance(other, GVector):
raise ValueError("Can't add GVector to " + str(type(other)))
v = GVector(self.x + other.x, self.y + other.y, self.z + other.z)
return v
def __sub__(self, other):
return self + other * -1
def __mul__(self, other):
v = GVector(self.x * other, self.y * other, self.z * other)
return v
__rmul__ = __mul__
def linear_combination(self, other, l1, l2=None):
if l2 is None:
l2 = 1 - l1
v = GVector(self.x * l1 + other.x * l2,
self.y * l1 + other.y * l2,
self.z * l1 + other.z * l2)
return v
def __str__(self):
return "<%f, %f, %f>" % (self.x, self.y, self.z)
def __repr__(self):
return "GVector(%f, %f, %f)" % (self.x, self.y, self.z)
class Spline(object):
"""Class for representing B-Splines and NURBS of arbitrary degree"""
def __init__(self, points, degree, knots):
"""Creates a Spline.
points is a list of GVector, degree is the degree of the Spline.
"""
if len(points) > len(knots) - degree + 1:
raise ValueError("too many control points")
elif len(points) < len(knots) - degree + 1:
raise ValueError("not enough control points")
last = knots[0]
for cur in knots[1:]:
if cur < last:
raise ValueError("knots not strictly increasing")
last = cur
self.knots = knots
self.points = points
self.degree = degree
def GetDomain(self):
"""Returns the domain of the B-Spline"""
return (self.knots[self.degree - 1],
self.knots[len(self.knots) - self.degree])
def __call__(self, u):
"""Calculates a point of the B-Spline using de Boors Algorithm"""
dom = self.GetDomain()
if u < dom[0] or u > dom[1]:
raise ValueError("Function value not in domain")
if u == dom[0]:
return self.points[0]
if u == dom[1]:
return self.points[-1]
I = self.GetIndex(u)
d = [self.points[I - self.degree + 1 + ii]
for ii in range(self.degree + 1)]
U = self.knots
for ik in range(1, self.degree + 1):
for ii in range(I - self.degree + ik + 1, I + 2):
ua = U[ii + self.degree - ik]
ub = U[ii - 1]
co1 = (ua - u) / (ua - ub)
co2 = (u - ub) / (ua - ub)
index = ii - I + self.degree - ik - 1
d[index] = d[index].linear_combination(d[index + 1], co1, co2)
return d[0]
def GetIndex(self, u):
dom = self.GetDomain()
for ii in range(self.degree - 1, len(self.knots) - self.degree):
if u >= self.knots[ii] and u < self.knots[ii + 1]:
I = ii
break
else:
I = dom[1] - 1
return I
def __len__(self):
return len(self.points)
def __repr__(self):
return "Spline(%r, %r, %r)" % (self.points, self.degree, self.knots)
def write_ppm(im, w, h, filename):
with open(filename, "wb") as f:
f.write(b'P6\n%i %i\n255\n' % (w, h))
for j in range(h):
for i in range(w):
val = im[j * w + i]
c = val * 255
f.write(b'%c%c%c' % (c, c, c))
class Chaosgame(object):
def __init__(self, splines, thickness, subdivs):
self.splines = splines
self.thickness = thickness
self.minx = min([p.x for spl in splines for p in spl.points])
self.miny = min([p.y for spl in splines for p in spl.points])
self.maxx = max([p.x for spl in splines for p in spl.points])
self.maxy = max([p.y for spl in splines for p in spl.points])
self.height = self.maxy - self.miny
self.width = self.maxx - self.minx
self.num_trafos = []
maxlength = thickness * self.width / self.height
for spl in splines:
length = 0
curr = spl(0)
for i in range(1, subdivs + 1):
last = curr
t = 1 / subdivs * i
curr = spl(t)
length += curr.dist(last)
self.num_trafos.append(max(1, int(length / maxlength * 1.5)))
self.num_total = sum(self.num_trafos)
def get_random_trafo(self):
r = random.randrange(int(self.num_total) + 1)
l = 0
for i in range(len(self.num_trafos)):
if r >= l and r < l + self.num_trafos[i]:
return i, random.randrange(self.num_trafos[i])
l += self.num_trafos[i]
return len(self.num_trafos) - 1, random.randrange(self.num_trafos[-1])
def transform_point(self, point, trafo=None):
x = (point.x - self.minx) / self.width
y = (point.y - self.miny) / self.height
if trafo is None:
trafo = self.get_random_trafo()
start, end = self.splines[trafo[0]].GetDomain()
length = end - start
seg_length = length / self.num_trafos[trafo[0]]
t = start + seg_length * trafo[1] + seg_length * x
basepoint = self.splines[trafo[0]](t)
if t + 1 / 50000 > end:
neighbour = self.splines[trafo[0]](t - 1 / 50000)
derivative = neighbour - basepoint
else:
neighbour = self.splines[trafo[0]](t + 1 / 50000)
derivative = basepoint - neighbour
if derivative.Mag() != 0:
basepoint.x += derivative.y / derivative.Mag() * (y - 0.5) * \
self.thickness
basepoint.y += -derivative.x / derivative.Mag() * (y - 0.5) * \
self.thickness
else:
# can happen, especially with single precision float
pass
self.truncate(basepoint)
return basepoint
def truncate(self, point):
if point.x >= self.maxx:
point.x = self.maxx
if point.y >= self.maxy:
point.y = self.maxy
if point.x < self.minx:
point.x = self.minx
if point.y < self.miny:
point.y = self.miny
def create_image_chaos(self, w, h, iterations, rng_seed):
# Always use the same sequence of random numbers
# to get reproductible benchmark
random.seed(rng_seed)
im = bytearray(w * h)
point = GVector((self.maxx + self.minx) / 2,
(self.maxy + self.miny) / 2, 0)
for _ in range(iterations):
point = self.transform_point(point)
x = (point.x - self.minx) / self.width * w
y = (point.y - self.miny) / self.height * h
x = int(x)
y = int(y)
if x == w:
x -= 1
if y == h:
y -= 1
im[(h - y - 1) * w + x] = 1
return im
###########################################################################
# Benchmark interface
bm_params = {
(100, 50): (0.25, 100, 50, 50, 50, 1234),
(1000, 1000): (0.25, 200, 400, 400, 1000, 1234),
(5000, 1000): (0.25, 400, 500, 500, 7000, 1234),
}
def bm_setup(params):
splines = [
Spline([
GVector(1.597, 3.304, 0.0),
GVector(1.576, 4.123, 0.0),
GVector(1.313, 5.288, 0.0),
GVector(1.619, 5.330, 0.0),
GVector(2.890, 5.503, 0.0),
GVector(2.373, 4.382, 0.0),
GVector(1.662, 4.360, 0.0)],
3, [0, 0, 0, 1, 1, 1, 2, 2, 2]),
Spline([
GVector(2.805, 4.017, 0.0),
GVector(2.551, 3.525, 0.0),
GVector(1.979, 2.620, 0.0),
GVector(1.979, 2.620, 0.0)],
3, [0, 0, 0, 1, 1, 1]),
Spline([
GVector(2.002, 4.011, 0.0),
GVector(2.335, 3.313, 0.0),
GVector(2.367, 3.233, 0.0),
GVector(2.367, 3.233, 0.0)],
3, [0, 0, 0, 1, 1, 1])
]
chaos = Chaosgame(splines, params[0], params[1])
image = None
def run():
nonlocal image
_, _, width, height, iter, rng_seed = params
image = chaos.create_image_chaos(width, height, iter, rng_seed)
def result():
norm = params[4]
# Images are not the same when floating point behaviour is different,
# so return percentage of pixels that are set (rounded to int).
#write_ppm(image, params[2], params[3], 'out-.ppm')
pix = int(100 * sum(image) / len(image))
return norm, pix
return run, result
+67
View File
@@ -0,0 +1,67 @@
# Source: https://github.com/python/pyperformance
# License: MIT
# The Computer Language Benchmarks Game
# http://benchmarksgame.alioth.debian.org/
# Contributed by Sokolov Yura, modified by Tupteq.
def fannkuch(n):
count = list(range(1, n + 1))
max_flips = 0
m = n - 1
r = n
check = 0
perm1 = list(range(n))
perm = list(range(n))
perm1_ins = perm1.insert
perm1_pop = perm1.pop
while 1:
if check < 30:
check += 1
while r != 1:
count[r - 1] = r
r -= 1
if perm1[0] != 0 and perm1[m] != m:
perm = perm1[:]
flips_count = 0
k = perm[0]
while k:
perm[:k + 1] = perm[k::-1]
flips_count += 1
k = perm[0]
if flips_count > max_flips:
max_flips = flips_count
while r != n:
perm1_ins(r, perm1_pop(0))
count[r] -= 1
if count[r] > 0:
break
r += 1
else:
return max_flips
###########################################################################
# Benchmark interface
bm_params = {
(50, 10): (5,),
(100, 10): (6,),
(500, 10): (7,),
(1000, 10): (8,),
(5000, 10): (9,),
}
def bm_setup(params):
state = None
def run():
nonlocal state
state = fannkuch(params[0])
def result():
return params[0], state
return run, result
+70
View File
@@ -0,0 +1,70 @@
# Source: https://github.com/python/pyperformance
# License: MIT
# Artificial, floating point-heavy benchmark originally used by Factor.
from math import sin, cos, sqrt
class Point(object):
__slots__ = ('x', 'y', 'z')
def __init__(self, i):
self.x = x = sin(i)
self.y = cos(i) * 3
self.z = (x * x) / 2
def __repr__(self):
return "<Point: x=%s, y=%s, z=%s>" % (self.x, self.y, self.z)
def normalize(self):
x = self.x
y = self.y
z = self.z
norm = sqrt(x * x + y * y + z * z)
self.x /= norm
self.y /= norm
self.z /= norm
def maximize(self, other):
self.x = self.x if self.x > other.x else other.x
self.y = self.y if self.y > other.y else other.y
self.z = self.z if self.z > other.z else other.z
return self
def maximize(points):
next = points[0]
for p in points[1:]:
next = next.maximize(p)
return next
def benchmark(n):
points = [None] * n
for i in range(n):
points[i] = Point(i)
for p in points:
p.normalize()
return maximize(points)
###########################################################################
# Benchmark interface
bm_params = {
(50, 25): (1, 150),
(100, 100): (1, 250),
(1000, 1000): (10, 1500),
(5000, 1000): (20, 3000),
}
def bm_setup(params):
state = None
def run():
nonlocal state
for _ in range(params[0]):
state = benchmark(params[1])
def result():
return params[0] * params[1], 'Point(%.4f, %.4f, %.4f)' % (state.x, state.y, state.z)
return run, result
File diff suppressed because it is too large Load Diff
+62
View File
@@ -0,0 +1,62 @@
# Source: https://github.com/python/pyperformance
# License: MIT
# Simple, brute-force N-Queens solver.
# author: collinwinter@google.com (Collin Winter)
# n_queens function: Copyright 2009 Raymond Hettinger
# Pure-Python implementation of itertools.permutations().
def permutations(iterable, r=None):
"""permutations(range(3), 2) --> (0,1) (0,2) (1,0) (1,2) (2,0) (2,1)"""
pool = tuple(iterable)
n = len(pool)
if r is None:
r = n
indices = list(range(n))
cycles = list(range(n - r + 1, n + 1))[::-1]
yield tuple(pool[i] for i in indices[:r])
while n:
for i in reversed(range(r)):
cycles[i] -= 1
if cycles[i] == 0:
indices[i:] = indices[i + 1:] + indices[i:i + 1]
cycles[i] = n - i
else:
j = cycles[i]
indices[i], indices[-j] = indices[-j], indices[i]
yield tuple(pool[i] for i in indices[:r])
break
else:
return
# From http://code.activestate.com/recipes/576647/
def n_queens(queen_count):
"""N-Queens solver.
Args: queen_count: the number of queens to solve for, same as board size.
Yields: Solutions to the problem, each yielded value is a N-tuple.
"""
cols = range(queen_count)
for vec in permutations(cols):
if (queen_count == len(set(vec[i] + i for i in cols))
== len(set(vec[i] - i for i in cols))):
yield vec
###########################################################################
# Benchmark interface
bm_params = {
(50, 25): (1, 5),
(100, 25): (1, 6),
(1000, 100): (1, 7),
(5000, 100): (1, 8),
}
def bm_setup(params):
res = None
def run():
nonlocal res
for _ in range(params[0]):
res = len(list(n_queens(params[1])))
def result():
return params[0] * 10 ** (params[1] - 3), res
return run, result
+62
View File
@@ -0,0 +1,62 @@
# Source: https://github.com/python/pyperformance
# License: MIT
# Calculating some of the digits of π.
# This benchmark stresses big integer arithmetic.
# Adapted from code on: http://benchmarksgame.alioth.debian.org/
def compose(a, b):
aq, ar, as_, at = a
bq, br, bs, bt = b
return (aq * bq,
aq * br + ar * bt,
as_ * bq + at * bs,
as_ * br + at * bt)
def extract(z, j):
q, r, s, t = z
return (q * j + r) // (s * j + t)
def gen_pi_digits(n):
z = (1, 0, 0, 1)
k = 1
digs = []
for _ in range(n):
y = extract(z, 3)
while y != extract(z, 4):
z = compose(z, (k, 4 * k + 2, 0, 2 * k + 1))
k += 1
y = extract(z, 3)
z = compose((10, -10 * y, 0, 1), z)
digs.append(y)
return digs
###########################################################################
# Benchmark interface
bm_params = {
(50, 25): (1, 35),
(100, 100): (1, 65),
(1000, 1000): (2, 250),
(5000, 1000): (3, 350),
}
def bm_setup(params):
state = None
def run():
nonlocal state
nloop, ndig = params
ndig = params[1]
for _ in range(nloop):
state = None # free previous result
state = gen_pi_digits(ndig)
def result():
return params[0] * params[1], ''.join(str(d) for d in state)
return run, result