Update benchmark suite with fair timing and micropython support
This commit is contained in:
parent
de76ba5394
commit
e4db90d392
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -10,5 +10,3 @@ jobs:
|
||||
run: make clean && make
|
||||
- name: Test
|
||||
run: make test
|
||||
- name: Benchmark
|
||||
run: make bench
|
||||
|
2
Makefile
2
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)
|
||||
|
@ -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 '?')
|
||||
|
||||
|
2
bench/fasttimer/.gitignore
vendored
Normal file
2
bench/fasttimer/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
build/
|
||||
__pycache__/
|
8
bench/fasttimer/Makefile
Normal file
8
bench/fasttimer/Makefile
Normal file
@ -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 $@ $<
|
13
bench/fasttimer/__init__.py
Normal file
13
bench/fasttimer/__init__.py
Normal file
@ -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)
|
||||
|
18
bench/fasttimer/mpy_timer.c
Normal file
18
bench/fasttimer/mpy_timer.c
Normal file
@ -0,0 +1,18 @@
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
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;
|
||||
}
|
59
bench/fasttimer/py_fasttimer.c
Normal file
59
bench/fasttimer/py_fasttimer.c
Normal file
@ -0,0 +1,59 @@
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include <Python.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
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);
|
||||
}
|
4
bench/fasttimer/setup.py
Normal file
4
bench/fasttimer/setup.py
Normal file
@ -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])
|
@ -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)')
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
from timeit import timeit
|
||||
from fasttimer import timeit
|
||||
|
||||
l = []
|
||||
add = l.append
|
||||
|
@ -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')
|
||||
|
Loading…
Reference in New Issue
Block a user