From e4db90d39298b7fe97c268fd5f14fd379132ada9 Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Fri, 16 Apr 2021 19:58:05 +0900 Subject: [PATCH] Update benchmark suite with fair timing and micropython support --- .github/workflows/build.yml | 2 -- Makefile | 2 ++ bench/bench.py | 4 +-- bench/fasttimer/.gitignore | 2 ++ bench/fasttimer/Makefile | 8 +++++ bench/fasttimer/__init__.py | 13 ++++++++ bench/fasttimer/mpy_timer.c | 18 +++++++++++ bench/fasttimer/py_fasttimer.c | 59 ++++++++++++++++++++++++++++++++++ bench/fasttimer/setup.py | 4 +++ bench/fib.py | 2 +- bench/list.py | 2 +- bench/maketree.py | 2 +- 12 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 bench/fasttimer/.gitignore create mode 100644 bench/fasttimer/Makefile create mode 100644 bench/fasttimer/__init__.py create mode 100644 bench/fasttimer/mpy_timer.c create mode 100644 bench/fasttimer/py_fasttimer.c create mode 100644 bench/fasttimer/setup.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0ce8f9d..9ff7a28 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,5 +10,3 @@ jobs: run: make clean && make - name: Test run: make test - - name: Benchmark - run: make bench diff --git a/Makefile b/Makefile index 2bca4c9..15e9a49 100644 --- a/Makefile +++ b/Makefile @@ -155,6 +155,8 @@ bench: @for i in bench/*.krk; do ./kuroko "$$i"; done @echo "CPython:" @for i in bench/*.py; do python3 "$$i"; done + @echo "Micropython:" + @for i in bench/*.py; do micropython -X heapsize=128M "$$i"; done # Really should be up to you to set, not us... multiarch ?= $(shell gcc -print-multiarch) diff --git a/bench/bench.py b/bench/bench.py index 440041a..9fd8edc 100644 --- a/bench/bench.py +++ b/bench/bench.py @@ -120,10 +120,10 @@ def write_instancevar(): a.x = 1; a.x = 1; a.x = 1; a.x = 1; a.x = 1 if __name__=='__main__': - from timeit import timeit + from fasttimer import timeit for f in [read_local, read_nonlocal, read_global, read_builtin, read_classvar, read_instancevar, read_unboundmethod, read_boundmethod, write_local, write_nonlocal, write_global, write_classvar, write_instancevar]: - print(timeit(f,number=1000000), f.__qualname__) + print(timeit(f,number=1000000), f.__qualname__ if hasattr(f,'__qualname__') else f.__name__ if hasattr(f,'__name__') else '?') diff --git a/bench/fasttimer/.gitignore b/bench/fasttimer/.gitignore new file mode 100644 index 0000000..3d43339 --- /dev/null +++ b/bench/fasttimer/.gitignore @@ -0,0 +1,2 @@ +build/ +__pycache__/ diff --git a/bench/fasttimer/Makefile b/bench/fasttimer/Makefile new file mode 100644 index 0000000..4d39312 --- /dev/null +++ b/bench/fasttimer/Makefile @@ -0,0 +1,8 @@ +all: _fasttimer.so _mpytimer.so + +_fasttimer.so: py_fasttimer.c + python3 setup.py build + cp build/*/*.so ./$@ + +_mpytimer.so: mpy_timer.c + $(CC) -shared -o $@ $< diff --git a/bench/fasttimer/__init__.py b/bench/fasttimer/__init__.py new file mode 100644 index 0000000..4d9882f --- /dev/null +++ b/bench/fasttimer/__init__.py @@ -0,0 +1,13 @@ +try: + from ._fasttimer import timeit +except: + import ffi + path = __file__.split('/')[:-1] + if not path: path = '.' + else: path = '/'.join(path) + lib = ffi.open(path + '/_mpytimer.so') + _timeit = lib.func('d','timeit','Ci') + def timeit(callback,number=1000000): + cb = ffi.callback('v',callback,'') + return _timeit(cb,number) + diff --git a/bench/fasttimer/mpy_timer.c b/bench/fasttimer/mpy_timer.c new file mode 100644 index 0000000..4cef2a5 --- /dev/null +++ b/bench/fasttimer/mpy_timer.c @@ -0,0 +1,18 @@ +#include +#include + +double +timeit(void (*callback)(void), int times) +{ + struct timeval tv_before, tv_after; + gettimeofday(&tv_before,NULL); + for (int t = 0; t < times; ++t) { + callback(); + } + gettimeofday(&tv_after,NULL); + + double before = (double)tv_before.tv_sec + (double)tv_before.tv_usec / 1000000.0; + double after = (double)tv_after.tv_sec + (double)tv_after.tv_usec / 1000000.0; + + return after-before; +} diff --git a/bench/fasttimer/py_fasttimer.c b/bench/fasttimer/py_fasttimer.c new file mode 100644 index 0000000..69e8a19 --- /dev/null +++ b/bench/fasttimer/py_fasttimer.c @@ -0,0 +1,59 @@ +#define PY_SSIZE_T_CLEAN +#include +#include +#include + +static PyObject * +fasttimer_timeit(PyObject * self, PyObject * args, PyObject* kwargs) +{ + PyObject * callable; + int times = 1000000; + + static char * keywords[] = {"callable","number",NULL}; + + if (!PyArg_ParseTupleAndKeywords(args,kwargs,"O|i:timeit", + keywords, &callable, ×)) { + PyErr_SetString(PyExc_TypeError, "bad arguments?"); + return NULL; + } + + if (!PyCallable_Check(callable)) { + PyErr_SetString(PyExc_TypeError, "expected callable"); + return NULL; + } + + + Py_XINCREF(callable); + struct timeval tv_before, tv_after; + gettimeofday(&tv_before,NULL); + for (int t = 0; t < times; ++t) { + /* Call it here */ + PyObject_CallObject(callable, NULL); + } + gettimeofday(&tv_after,NULL); + Py_XDECREF(callable); + + double before = (double)tv_before.tv_sec + (double)tv_before.tv_usec / 1000000.0; + double after = (double)tv_after.tv_sec + (double)tv_after.tv_usec / 1000000.0; + + return PyFloat_FromDouble(after-before); +} + +static PyMethodDef FasttimerMethods[] = { + {"timeit", (PyCFunction)(void(*)(void))fasttimer_timeit, METH_VARARGS | METH_KEYWORDS, "Call a callable in a tight loop."}, + {NULL,NULL,0,NULL}, +}; + +static struct PyModuleDef fasttimermodule = { + PyModuleDef_HEAD_INIT, + "fasttimer", + NULL, + -1, + FasttimerMethods +}; + +PyMODINIT_FUNC +PyInit__fasttimer(void) +{ + return PyModule_Create(&fasttimermodule); +} diff --git a/bench/fasttimer/setup.py b/bench/fasttimer/setup.py new file mode 100644 index 0000000..a42ad4a --- /dev/null +++ b/bench/fasttimer/setup.py @@ -0,0 +1,4 @@ +from distutils.core import setup, Extension + +fasttimer = Extension('_fasttimer',sources=['py_fasttimer.c']) +setup(name="_fasttimer",version='1.0',description='fast timer',ext_modules=[fasttimer]) diff --git a/bench/fib.py b/bench/fib.py index 8bef2ff..9a61a55 100644 --- a/bench/fib.py +++ b/bench/fib.py @@ -5,6 +5,6 @@ def __main__(): fib(30) if __name__ == '__main__': - from timeit import timeit + from fasttimer import timeit print(timeit(__main__,number=1),'fib(30)') diff --git a/bench/list.py b/bench/list.py index 501a280..8d497ed 100644 --- a/bench/list.py +++ b/bench/list.py @@ -1,4 +1,4 @@ -from timeit import timeit +from fasttimer import timeit l = [] add = l.append diff --git a/bench/maketree.py b/bench/maketree.py index a6a7cea..2c5ed71 100644 --- a/bench/maketree.py +++ b/bench/maketree.py @@ -10,5 +10,5 @@ def makeTree(depth): return Node(n1,n2) if __name__ == '__main__': - from timeit import timeit + from fasttimer import timeit print(timeit(lambda: makeTree(16), number=10), 'makeTree')