Skip to content

tests: Add performance benchmarking test suite. #4858

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ jobs:
- make ${MAKEOPTS} -C ports/unix deplibs
- make ${MAKEOPTS} -C ports/unix
- make ${MAKEOPTS} -C ports/unix test
- (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=../ports/unix/micropython ./run-perfbench.py 1000 1000)

# unix nanbox
- stage: test
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
26 changes: 26 additions & 0 deletions tests/perf_bench/benchrun.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
def bm_run(N, M):
try:
from utime import ticks_us, ticks_diff
except ImportError:
import time
ticks_us = lambda: int(time.perf_counter() * 1000000)
ticks_diff = lambda a, b: a - b

# Pick sensible parameters given N, M
cur_nm = (0, 0)
param = None
for nm, p in bm_params.items():
if 10 * nm[0] <= 12 * N and nm[1] <= M and nm > cur_nm:
cur_nm = nm
param = p
if param is None:
print(-1, -1, 'no matching params')
return

# Run and time benchmark
run, result = bm_setup(param)
t0 = ticks_us()
run()
t1 = ticks_us()
norm, out = result()
print(ticks_diff(t1, t0), norm, out)
274 changes: 274 additions & 0 deletions tests/perf_bench/bm_chaos.py
Original file line number Diff line number Diff line change
@@ -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 changes: 67 additions & 0 deletions tests/perf_bench/bm_fannkuch.py
Original file line number Diff line number Diff line change
@@ -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
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy