diff --git a/benchmarks/bench.py b/benchmarks/bench.py index 2ebe18a..a3bcc6b 100644 --- a/benchmarks/bench.py +++ b/benchmarks/bench.py @@ -1,56 +1,165 @@ import sys import os +from xml.dom.minidom import getDOMImplementation +from timeit import Timer +import subprocess + sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'build', 'default', 'benchmarks')) -from timeit import Timer TIMES = 10000000 +TIMES1 = TIMES/4 + +import testapi_pybindgen +import testapi_boost +import testapi_swig - -def bench(mod): - def bench3(): - return mod.Multiplier() - print "test3 (call class constructor with no arguments):", Timer(bench3).timeit(TIMES) - - def bench4(): - return mod.Multiplier(3.0) - print "test4 (call class constructor with double):", Timer(bench4).timeit(TIMES) - - obj = mod.Multiplier(3.0) - def bench5(): - return obj.GetFactor() - print "test4 (call method with no arguments):", Timer(bench5).timeit(TIMES) - - obj = mod.Multiplier(3.0) - def bench6(): - return obj.Multiply(5.0) - print "test4 (call method with double):", Timer(bench6).timeit(TIMES) - - def bench1(): +def bench(mod, dom, elem): + def bench(): return mod.func1() - print "test1 (call function with no arguments):", Timer(bench1).timeit(TIMES) + tst = elem.appendChild(dom.createElement('test')) + tst.setAttribute("description", "call function with no arguments") + tst.setAttribute("time", repr(Timer(bench).timeit(TIMES))) + print "%s (%s): %s" % (tst.tagName, tst.getAttribute('description'), tst.getAttribute('time')) - def bench2(): + def bench(): return mod.func2(1.0, 2.0, 3.0) - print "test2 (call function taking 3 doubles):", Timer(bench2).timeit(TIMES) + tst = elem.appendChild(dom.createElement('test')) + tst.setAttribute("description", "call function taking 3 doubles") + tst.setAttribute("time", repr(Timer(bench).timeit(TIMES))) + print "%s (%s): %s" % (tst.tagName, tst.getAttribute('description'), tst.getAttribute('time')) + + def bench(): + return mod.Multiplier() + tst = elem.appendChild(dom.createElement('test')) + tst.setAttribute("description", "call class constructor with no arguments") + tst.setAttribute("time", repr(Timer(bench).timeit(TIMES))) + print "%s (%s): %s" % (tst.tagName, tst.getAttribute('description'), tst.getAttribute('time')) + + def bench(): + return mod.Multiplier(3.0) + tst = elem.appendChild(dom.createElement('test')) + tst.setAttribute("description", "call class constructor with double") + tst.setAttribute("time", repr(Timer(bench).timeit(TIMES))) + print "%s (%s): %s" % (tst.tagName, tst.getAttribute('description'), tst.getAttribute('time')) + + obj = mod.Multiplier(3.0) + def bench(): + return obj.GetFactor() + tst = elem.appendChild(dom.createElement('test')) + tst.setAttribute("description", "call simple method") + tst.setAttribute("time", repr(Timer(bench).timeit(TIMES))) + print "%s (%s): %s" % (tst.tagName, tst.getAttribute('description'), tst.getAttribute('time')) + + obj = mod.Multiplier(3.0) + def bench(): + return obj.SetFactor() + tst = elem.appendChild(dom.createElement('test')) + tst.setAttribute("description", "call overloaded method 1") + tst.setAttribute("time", repr(Timer(bench).timeit(TIMES))) + print "%s (%s): %s" % (tst.tagName, tst.getAttribute('description'), tst.getAttribute('time')) + + obj = mod.Multiplier(3.0) + def bench(): + return obj.SetFactor(1.0) + tst = elem.appendChild(dom.createElement('test')) + tst.setAttribute("description", "call overloaded method 2") + tst.setAttribute("time", repr(Timer(bench).timeit(TIMES))) + print "%s (%s): %s" % (tst.tagName, tst.getAttribute('description'), tst.getAttribute('time')) + + obj = mod.Multiplier(3.0) + def bench(): + return obj.Multiply(5.0) + tst = elem.appendChild(dom.createElement('test')) + tst.setAttribute("description", "call non-overridden virtual method with double") + tst.setAttribute("time", repr(Timer(bench).timeit(TIMES))) + print "%s (%s): %s" % (tst.tagName, tst.getAttribute('description'), tst.getAttribute('time')) + + if mod is testapi_pybindgen: + class M(mod.Multiplier): + def _Multiply(self, value): + #print "Multiply", value, " return ", super(M, self)._Multiply(value) + return super(M, self)._Multiply(value) + elif mod is testapi_boost: + class M(mod.Multiplier): + def Multiply(self, value): + #print "Multiply", value, " return ", super(M, self).Multiply(value) + return super(M, self).Multiply(value) + elif mod is testapi_swig: + class M(mod.Multiplier): + def Multiply(self, value): + #print "Multiply", value, " return ", super(M, self).Multiply(value) + return super(M, self).Multiply(value) + else: + raise NotImplementedError + obj = M(2.0) + def bench(): + return obj.Multiply(5.0) + + tst = elem.appendChild(dom.createElement('test')) + tst.setAttribute("description", "call python-overridden virtual method from Python") + tst.setAttribute("time", repr(Timer(bench).timeit(TIMES1))) + print "%s (%s): %s" % (tst.tagName, tst.getAttribute('description'), tst.getAttribute('time')) + + def bench(): + return mod.call_virtual_from_cpp(obj, 5.0) + tst = elem.appendChild(dom.createElement('test')) + tst.setAttribute("description", "call python-overridden virtual method from C++") + tst.setAttribute("time", repr(Timer(bench).timeit(TIMES1))) + print "%s (%s): %s" % (tst.tagName, tst.getAttribute('description'), tst.getAttribute('time')) def main(): - import testapi_pybindgen - import testapi_boost - import testapi_swig + impl = getDOMImplementation() + dom = impl.createDocument(None, "pybindgen-benchmarks", None) + top = dom.documentElement + + env = top.appendChild(dom.createElement('environment')) + env.appendChild(dom.createElement('compiler')).appendChild(dom.createTextNode( + subprocess.Popen(["g++", "-v"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0])) + env.appendChild(dom.createElement('python')).appendChild(dom.createTextNode( + sys.version)) + env.appendChild(dom.createElement('swig')).appendChild(dom.createTextNode( + subprocess.Popen(["swig", "-version"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0])) + env.appendChild(dom.createElement('boost_python')).appendChild(dom.createTextNode( + subprocess.Popen(["dpkg", "-s", "libboost-python-dev"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0])) + env.appendChild(dom.createElement('pybindgen')).appendChild(dom.createTextNode( + subprocess.Popen(["bzr", "revno"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0])) + + env.appendChild(dom.createElement('cpu')).appendChild(dom.createTextNode( + file("/proc/cpuinfo").read())) + + if len(sys.argv) == 3: + env.appendChild(dom.createElement('CXXFLAGS')).appendChild(dom.createTextNode(sys.argv[2])) + + + res = top.appendChild(dom.createElement('results')) + print "pybindgen results:" - bench(testapi_pybindgen) + pbg = res.appendChild(dom.createElement('pybindgen')) + pbg.setAttribute("module-file-size", repr(os.stat("build/default/benchmarks/testapi_pybindgen.so").st_size)) + bench(testapi_pybindgen, dom, pbg) print "boost_python results:" - bench(testapi_boost) + bp = res.appendChild(dom.createElement('boost_python')) + bp.setAttribute("module-file-size", repr(os.stat("build/default/benchmarks/testapi_boost.so").st_size)) + bench(testapi_boost, dom, bp) print "swig results:" - bench(testapi_swig) + sw = res.appendChild(dom.createElement('swig')) + sw.setAttribute("module-file-size", repr(os.stat("build/default/benchmarks/_testapi_swig.so").st_size)) + sw.setAttribute("module-python-file-size", repr(os.stat("build/default/benchmarks/testapi_swig.py").st_size)) + bench(testapi_swig, dom, sw) + + if len(sys.argv) == 3: + f = open(sys.argv[1], "wb") + dom.writexml(f, "", " ", "\n") + f.close() + if __name__ == '__main__': diff --git a/benchmarks/testapi-pybindgen.py b/benchmarks/testapi-pybindgen.py index faa434b..b8e5ee1 100644 --- a/benchmarks/testapi-pybindgen.py +++ b/benchmarks/testapi-pybindgen.py @@ -21,7 +21,11 @@ def my_module_gen(out_file): Multiplier.add_constructor([param('double', 'factor')]) Multiplier.add_method('GetFactor', 'double', [], is_const=True) + Multiplier.add_method('SetFactor', 'void', [param('double', 'f')], is_const=True) + Multiplier.add_method('SetFactor', 'void', [], is_const=True) Multiplier.add_method('Multiply', 'double', [param('double', 'value')], is_virtual=True, is_const=True) + + mod.add_function('call_virtual_from_cpp', 'double', [param('Multiplier const *', 'obj'), param('double', 'value')]) mod.generate(FileCodeSink(out_file)) diff --git a/benchmarks/testapi.cc b/benchmarks/testapi.cc index 4909cdc..8f290a5 100644 --- a/benchmarks/testapi.cc +++ b/benchmarks/testapi.cc @@ -21,12 +21,25 @@ Multiplier::Multiplier (double factor) { } +Multiplier::~Multiplier () +{ +} double Multiplier::GetFactor () const { return m_factor; } +void Multiplier::SetFactor (double f) +{ + m_factor = f; +} + +void Multiplier::SetFactor () +{ + m_factor = 1.0; +} + double Multiplier::Multiply (double value) const { @@ -34,3 +47,9 @@ Multiplier::Multiply (double value) const } +double +call_virtual_from_cpp (Multiplier const *obj, double value) +{ + return obj->Multiply (value); +} + diff --git a/benchmarks/testapi.h b/benchmarks/testapi.h index 94722b0..0982f6d 100644 --- a/benchmarks/testapi.h +++ b/benchmarks/testapi.h @@ -7,6 +7,7 @@ void func1 (void); double func2 (double x, double y, double z); + class Multiplier { double m_factor; @@ -14,10 +15,14 @@ class Multiplier public: Multiplier (); Multiplier (double factor); + virtual ~Multiplier (); + void SetFactor (double f); + void SetFactor (void); double GetFactor () const; virtual double Multiply (double value) const; }; +double call_virtual_from_cpp (Multiplier const *obj, double value); #endif diff --git a/benchmarks/testapi_boost.cc b/benchmarks/testapi_boost.cc index 4793032..5b1b1de 100644 --- a/benchmarks/testapi_boost.cc +++ b/benchmarks/testapi_boost.cc @@ -3,16 +3,44 @@ #include +using namespace boost::python; + +void (Multiplier::*factor1) () = &Multiplier::SetFactor; +void (Multiplier::*factor2) (double) = &Multiplier::SetFactor; + +struct MultiplierWrap : Multiplier, wrapper +{ + MultiplierWrap () : Multiplier () + { + } + MultiplierWrap (double factor) : Multiplier (factor) + { + } + + double Multiply (double value) const + { + return this->get_override("Multiply") (value); + } + + double default_Multiply (double value) const + { + return this->Multiplier::Multiply (value); + } + +}; + BOOST_PYTHON_MODULE(testapi_boost) { - using namespace boost::python; def("func1", func1); def("func2", func2); + def("call_virtual_from_cpp", call_virtual_from_cpp); - class_("Multiplier") + class_("Multiplier") .def(init()) - .def("GetFactor", &Multiplier::GetFactor) - .def("Multiply", &Multiplier::Multiply) + .def("GetFactor", &Multiplier::GetFactor) + .def("SetFactor", factor1) + .def("SetFactor", factor2) + .def("Multiply", &Multiplier::Multiply, &MultiplierWrap::default_Multiply) ; } diff --git a/benchmarks/testapi_swig.i b/benchmarks/testapi_swig.i index 81c0b87..2bbd23c 100644 --- a/benchmarks/testapi_swig.i +++ b/benchmarks/testapi_swig.i @@ -1,6 +1,8 @@ /* example.i */ -%module testapi_swig +%module(directors="1") testapi_swig +%feature("director"); + %{ #include "testapi.h" @@ -16,8 +18,13 @@ class Multiplier public: Multiplier (); + virtual ~Multiplier (); Multiplier (double factor); - + void SetFactor (double f); + void SetFactor (void); double GetFactor () const; virtual double Multiply (double value) const; }; + +extern double call_virtual_from_cpp (Multiplier const *obj, double value); + diff --git a/wscript b/wscript index ff0ecec..4a6d64e 100644 --- a/wscript +++ b/wscript @@ -309,7 +309,8 @@ def bench(bld): env = g_bld.env print "Running benchmarks..." - retval = subprocess.Popen([env['PYTHON'], '-O', 'benchmarks/bench.py']).wait() + retval = subprocess.Popen([env['PYTHON'], '-O', 'benchmarks/bench.py', + "build/default/benchmarks/results.xml", ' '.join(env['CXXFLAGS_PYEXT'] + env['CXXFLAGS'])]).wait() check_context = Build.BuildContext