5 Python binding benchmarks
7 Displays the number of operations per second achieved under various scenarios
8 utilizing the Python bindings that wrap the AtomSpace using Cython. Some of the
9 tests compare the evaluation of nodes using the Python Scheme bindings which
10 take much longer than their comparable Cython bindings.
12 Results can be compared with the more comprehensive benchmarking code in
13 the opencog/benchmark directory.
15 Example results (excerpt only):
16 --- OpenCog Python Benchmark - 2015-03-12 15:44:02.614126 ---
18 -- Testing Node Adds --
24 Ops per second: 186,219
26 Add nodes - Cython (500K)
30 Ops per second: 179,115
32 Add fully connected nodes
36 Ops per second: 99,132
44 from opencog.atomspace
import AtomSpace, TruthValue, Handle, Atom, types
45 import opencog.scheme_wrapper
as scheme
46 from opencog.scheme_wrapper
import load_scm, scheme_eval, scheme_eval_h
47 from opencog.bindlink
import stub_bindlink, bindlink
49 __authors__ =
'Cosmo Harrigan, Curtis Faith'
55 if text.startswith(
'R|'):
56 return text[2:].splitlines()
57 return argparse.HelpFormatter._split_lines(self, text, width)
60 parser = argparse.ArgumentParser(description=
"OpenCog Python Benchmark",
61 formatter_class=SmartFormatter)
62 verbosity_group = parser.add_mutually_exclusive_group()
63 verbosity_group.add_argument(
"-v",
"--verbose", action=
"store_true", help=
"verbose output")
64 verbosity_group.add_argument(
"-c",
"--columns", action=
"store_true", default=
True, help=
"columnar output (default)")
66 test_group = parser.add_mutually_exclusive_group()
67 test_group.add_argument(
"-a",
"--all", default=
False, action=
"store_true", help=
"run all tests")
68 test_group.add_argument(
"-t",
"--test", type=str, default=
'spread',
69 choices=[
'spread',
'node',
'bindlink',
'traverse',
'scheme',
'get_vs_xget',
'predicates'],
70 help=
"R|Test to benchmark, where:\n"
71 " spread - a spread of tests across areas (default)\n"
72 " node - atomspace node operations \n"
73 " bindlink - all the bindlink forms\n"
74 " traverse - traversal of atomspace lists\n"
75 " scheme - scheme evaluation functions\n"
76 " get_vs_xget - compare get to xget functions\n"
77 " predicates - predicate retrieval functions")
78 parser.add_argument(
"-i",
"--iterations", metavar=
'N', type=int, default=10, help=
"iterations to average (default=10)")
79 args = parser.parse_args()
85 test_iterations = args.iterations
88 "opencog/atomspace/core_types.scm",
89 "opencog/scm/utilities.scm"
98 return ((finish - start) / n)
107 """Add n nodes in atomspace with python bindings"""
110 atomspace.add_node(types.ConceptNode, str(i), TruthValue(.5, .5))
111 return n, atomspace.get_atoms_by_type(types.ConceptNode)
114 """Add n nodes in atomspace with python bindings"""
117 atomspace.add_node(types.ConceptNode, str(i), TruthValue(.5, .5))
118 return n, atomspace.get_atoms_by_type(types.ConceptNode)
121 """Add n nodes in atomspace with python bindings"""
124 atomspace.add_node(types.ConceptNode, str(i), TruthValue(.5, .5))
125 return n, atomspace.get_atoms_by_type(types.ConceptNode)
128 """Add n nodes in atomspace with python bindings"""
131 atomspace.add_node(types.ConceptNode, str(i), TruthValue(.5, .5))
135 """Add n nodes and create a complete (fully-connected) graph in atomspace
136 and returns the number of items processed
139 offset = atomspace.add_node(types.ConceptNode,
"Starting handle offset")
140 offset = offset.h.value()
142 for i
in xrange(1, n+1):
143 atomspace.add_node(types.ConceptNode, str(i), TruthValue(.5, .5))
144 for j
in xrange(1, i):
145 atomspace.add_link(types.HebbianLink,
146 [Handle(i + offset), Handle(j + offset)],
150 return n + (n**2 - n) / 2
153 scheme.__init__(atomspace)
156 scheme.__init__(atomspace)
157 for scheme_file
in scheme_preload:
158 load_scm(atomspace, scheme_file)
163 (InheritanceLink (ConceptNode "Frog") (ConceptNode "animal"))
164 (InheritanceLink (ConceptNode "Zebra") (ConceptNode "animal"))
165 (InheritanceLink (ConceptNode "Deer") (ConceptNode "animal"))
166 (InheritanceLink (ConceptNode "Spaceship") (ConceptNode "machine"))
168 scheme_eval_h(atomspace, scheme_animals)
177 ;; The variable to be bound
178 (VariableNode "$var")
179 ;; The pattern to be searched for
181 (VariableNode "$var")
182 (ConceptNode "animal")
184 ;; The value to be returned.
185 (VariableNode "$var")
188 return scheme_eval_h(atomspace, bind_link_query)
198 ;; The variable to be bound
199 (VariableNode "$var")
200 ;; The pattern to be searched for
202 (VariableNode "$var")
203 (ConceptNode "animal")
206 ;; The value to be returned.
207 (VariableNode "$var")
211 scheme_eval_h(atomspace, scheme_query)
214 scheme.__init__(atomspace)
215 for scheme_file
in scheme_preload:
216 load_scm(atomspace, scheme_file)
219 scheme_dog_predicates = \
222 (PredicateNode "IsA")
225 (ConceptNode "mammal")
229 (PredicateNode "IsA")
232 (ConceptNode "animal")
236 (PredicateNode "Loves")
239 (ConceptNode "biscuits")
243 scheme_eval_h(atomspace, scheme_dog_predicates)
244 dog = atomspace.add_node(types.ConceptNode,
"dog")
245 isA = atomspace.add_node(types.PredicateNode,
"IsA")
251 """Add n nodes in atomspace with python bindings"""
254 atomspace.add_node(types.ConceptNode, str(i), TruthValue(.5, .5))
258 """Add n nodes in atomspace with python bindings"""
261 atomspace.add_node(types.ConceptNode, str(i), TruthValue(.5, .5))
265 """Add n nodes and create a complete (fully-connected) graph in atomspace
266 and returns the number of items processed
269 offset = atomspace.add_node(types.ConceptNode,
"Starting handle offset")
270 offset = offset.h.value()
272 for i
in xrange(1, n+1):
273 atomspace.add_node(types.ConceptNode, str(i), TruthValue(.5, .5))
274 for j
in xrange(1, i):
275 atomspace.add_link(types.HebbianLink,
276 [Handle(i + offset), Handle(j + offset)],
280 return n + (n**2 - n) / 2
285 atom_count, atom_list = prep_traverse_result
286 for atom
in atom_list:
293 atom_count, atom_list = prep_traverse_result
294 for atom
in atom_list:
296 print "test_resolve_traversal - atom didn't resolve"
303 atom_count = prep_result
304 atom_list = atomspace.get_atoms_by_type(types.ConceptNode)
305 for atom
in atom_list:
312 atom_count = prep_result
313 for atom
in atomspace.xget_atoms_by_type(types.ConceptNode):
320 atom_count = prep_result
321 atom_list = atomspace.get_atoms_by_type(types.HebbianLink)
323 for atom
in atom_list:
324 outgoing_list = atomspace.get_outgoing(atom.h)
325 for outgoing
in outgoing_list:
330 atom_count = prep_result
332 for atom
in atomspace.get_atoms_by_type(types.HebbianLink):
333 for outgoing
in atomspace.get_outgoing(atom.h):
338 atom_count = prep_result
340 for atom
in atomspace.xget_atoms_by_type(types.HebbianLink):
341 for outgoing
in atomspace.xget_outgoing(atom.h):
357 result =
bindlink(atomspace, prep_handle)
366 result = scheme_eval_h(atomspace,
'(+ 2 2)')
372 result = scheme_eval_h(atomspace,
'(cog-bind find-animals)')
376 """Add n nodes in atomspace using scheme"""
379 scheme =
'cog-new-node \'ConceptNode "' + str(i) + \
380 '" (cog-new-stv 0.5 0.5)'
381 scheme_eval_h(atomspace, scheme)
385 """Add n nodes in atomspace using scheme with syntactic sugar"""
388 scheme =
'\'ConceptNode "' + str(i) +
'" (cog-new-stv 0.5 0.5)'
389 scheme_eval_h(atomspace, scheme)
394 dog, isA = prep_result
397 result = atomspace.get_predicates(dog)
401 dog, isA = prep_result
404 result = atomspace.get_predicates_for(dog, isA)
408 scheme =
'(cog-get-pred (ConceptNode "dog") \'PredicateNode)'
411 scheme_eval_h(atomspace, scheme)
417 def do_test(prep, test, description, op_time_adjustment):
418 """Runs tests and prints the test name and performance"""
424 for i
in xrange(test_iterations):
426 atomspace = AtomSpace()
427 prep_result = prep(atomspace)
431 ops = test(atomspace, prep_result)
435 total_time += finish - start
438 average_time = total_time / test_iterations
439 average_ops = total_ops / test_iterations
440 adjusted_time = average_time - (ops * op_time_adjustment)
444 time_string =
"{0:.03f}".format(adjusted_time)
445 print " Total: {0: >12}s".format(time_string)
447 test_ops =
"{0:,d}".format(int(average_ops))
448 print " Ops: {0: >12}".format(test_ops)
450 op_time =
"{0:.03f}".format(adjusted_time * 1000000 / ops)
451 ops_sec =
"{0:,d}".format(int(ops / adjusted_time))
453 print "{0: <40} {1: >12}µs {2: >15}".format(description, op_time,
456 print " Op time: {0: >12}µs".format(op_time)
457 print " Ops/sec: {0: >12}".format(ops_sec)
465 ([
'all'],
None,
None,
"-- Testing Node Adds --"),
466 ([
'node',
'spread'], prep_none, test_add_nodes,
"Add nodes - Cython"),
467 ([
'node'], prep_none, test_add_nodes_large,
"Add nodes - Cython (500K)"),
468 ([
'node'], prep_none, test_add_connected,
"Add fully connected nodes"),
470 ([
'all'],
None,
None,
"-- Testing Atom Traversal --"),
471 ([
'traverse'], prep_traverse_100K, test_bare_traversal,
"Bare atom traversal 100K - by type"),
472 ([
'traverse'], prep_traverse_10K, test_resolve_traversal,
"Resolve Handle 10K - by type"),
473 ([
'traverse',
'spread'], prep_traverse_100K, test_resolve_traversal,
"Resolve Handle 100K - by type"),
474 ([
'traverse'], prep_traverse_1M, test_resolve_traversal,
"Resolve Handle 1M - by type"),
476 ([
'all'],
None,
None,
"-- Testing Get versus Yield-based Get --"),
477 ([
'get_vs_xget'], prep_get_100K, test_get_traverse,
"Get and Traverse 100K - by type"),
478 ([
'get_vs_xget'], prep_get_100K, test_xget_traverse,
"Yield Get and Traverse 100K - by type"),
479 ([
'get_vs_xget'], prep_get_outgoing, test_get_outgoing,
"Get Outgoing"),
480 ([
'get_vs_xget'], prep_get_outgoing, test_get_outgoing_no_list,
"Get Outgoing - no temporary list"),
481 ([
'get_vs_xget'], prep_get_outgoing, test_xget_outgoing,
"Yield Get Outgoing"),
483 ([
'all'],
None,
None,
"-- Testing Bind --"),
484 ([
'bindlink'], prep_none, test_stub_bindlink,
"Bind - stub_bindlink - Cython"),
485 ([
'bindlink',
'spread'], prep_bind_python, test_bind,
"Bind - bindlink - Cython"),
487 ([
'all'],
None,
None,
"-- Testing Scheme Eval --"),
488 ([
'scheme',
'spread'], prep_scheme, test_scheme_eval,
"Test scheme_eval_h(+ 2 2)"),
489 ([
'scheme'], prep_bind_scheme, test_bind_scheme,
"Bind - cog-bind - Scheme"),
490 ([
'scheme'], prep_scheme, test_add_nodes_scheme,
"Add nodes - cog-new-node - Scheme"),
491 ([
'scheme'], prep_scheme, test_add_nodes_sugar,
"Add nodes - ConceptNode sugar - Scheme"),
493 ([
'all'],
None,
None,
"-- Testing Get Predicates --"),
494 ([
'predicates',
'spread'], prep_predicates, test_get_predicates,
"Predicates - get_predicates"),
495 ([
'predicates'], prep_predicates, test_get_predicates_for,
"Predicates - get_predicates_for"),
496 ([
'predicates'], prep_predicates, test_get_predicates_scheme,
"Predicates - cog-get-pred - Scheme"),
501 print "--- OpenCog Python Benchmark - ", str(datetime.datetime.now()),
"---"
506 if args.verbose
or args.iterations != 10:
507 if args.iterations > 1:
508 print "Test times averaged over {0:d} iterations.".format(test_iterations)
510 print "Test times averaged over 1 iteration."
511 if not args.verbose
and args.iterations != 10:
515 print "Per-op loop adjustment: {0:.03f}µs.".format(op_time_adjustment * 1000000)
519 print "{0: <40} {1: >14} {2: >15}".format(
"Test",
"Time per op",
"Ops per second")
520 print "{0: <40} {1: >14} {2: >15}".format(
"----",
"-----------",
"--------------")
523 for run_list, prep, test, description
in tests:
524 if args.all
or args.test
in run_list:
525 do_test(prep, test, description, op_time_adjustment)
def test_get_outgoing_no_list
def test_add_nodes_scheme
def test_get_predicates_for
def test_get_predicates_scheme
Handle bindlink(AtomSpace *, const Handle &)
def test_resolve_traversal
Handle stub_bindlink(AtomSpace *, Handle)