OpenCog Framework  Branch: master, revision 6f0b7fc776b08468cf1b74aa9db028f387b4f0c0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
AtomSpaceBenchmark.cc
Go to the documentation of this file.
1 
4 #include <ctime>
5 #include <iostream>
6 #include <fstream>
7 #include <sys/time.h>
8 #include <sys/resource.h>
9 
10 #include <boost/tuple/tuple_io.hpp>
11 
12 #include <opencog/util/oc_assert.h>
13 #include <opencog/util/random.h>
14 
20 #include <opencog/atomspace/TLB.h>
24 
25 #include "AtomSpaceBenchmark.h"
26 
27 const char* VERSION_STRING = "Version 1.0.1";
28 
29 namespace opencog {
30 
31 using namespace boost;
32 using std::cout;
33 using std::cerr;
34 using std::flush;
35 using std::endl;
36 using std::clock;
37 using std::time;
38 
39 #define DIVIDER_LINE "------------------------------"
40 #define PROGRESS_BAR_LENGTH 10
41 
43 {
44  percentLinks = 0.2;
45 
46  // Create an atomspace with a quarter-million atoms
47  // This is quasi-realistic for an atomspace doing language processing.
48  // Maybe a bit on the small side ...
49  atomCount = (1 << 18);
50  defaultNodeType = CONCEPT_NODE;
51  chanceOfNonDefaultNode = 0.4f;
52  defaultLinkType = INHERITANCE_LINK;
53  chanceOfNonDefaultLink = 0.4f;
54  linkSize_mean = 2.0f;
55  prg = new std::poisson_distribution<unsigned>(linkSize_mean);
56 
57  counter = 0;
58  showTypeSizes = false;
59  Nreps = 100000;
60  Nloops = 1;
61  memoize = false;
62  compile = false;
63  sizeIncrease = 0;
64  saveToFile = false;
65  saveInterval = 1;
66  buildTestData = false;
67  chanceUseDefaultTV = 0.8f;
68  doStats = false;
69  testKind = BENCH_AS;
70 
71  randomseed = (unsigned long) time(NULL);
72 
73 
74  asp = NULL;
75  atab = NULL;
76  rng = NULL;
77 }
78 
80 {
81  delete prg;
82  delete rng;
83 }
84 
85 // This is wrong, because it fails to also count the amount of RAM
86 // used by the AtomTable to store indexes.
88 {
89  size_t total = 0;
91  {
92  switch (h->getTruthValue()->getType()) {
93  case SIMPLE_TRUTH_VALUE:
94  total += sizeof(SimpleTruthValue);
95  break;
96  case COUNT_TRUTH_VALUE:
97  total += sizeof(CountTruthValue);
98  break;
100  total += sizeof(IndefiniteTruthValue);
101  break;
102  default:
103  break;
104  }
105  }
106 
108  {
109  total += sizeof(AttentionValue);
110  }
111 
112  if (asp->is_node(h))
113  {
114  NodePtr n(NodeCast(h));
115  total = sizeof(Node);
116  total += n->getName().capacity();
117  }
118  else
119  {
120  LinkPtr l(LinkCast(h));
121  total = sizeof(Link);
122  total += l->getOutgoingSet().capacity() * sizeof(Handle);
123  for (Handle ho: l->getOutgoingSet())
124  {
125  total += estimateOfAtomSize(ho);
126  }
127  }
128 
129  return total;
130 
131 }
132 
134 {
135  // getrusage is the best option it seems...
136  // on linux /proc/pid/status and other files may have more detail
137  struct rusage *s = (struct rusage *) malloc(sizeof(struct rusage));
138  getrusage(RUSAGE_SELF,s);
139  long rss = s->ru_maxrss;
140  free(s);
141  return rss;
142 }
143 
145 {
146  // Note that these are just the type size, it doesn't include the size of
147  // data/classes that these might point to.
148  //cout << "CLOCKS_PER_SEC = " << CLOCKS_PER_SEC << endl;
149  cout << "==sizeof() on various classes==" << endl;
150  cout << "FIXME: the report below is only for the sizes of the C++ objects\n"
151  << "themselves. In addition, every atom consumes from 5 to 15 times\n"
152  << "the sizeof(Handle) in the AtomTable indexes. This depends on the\n"
153  << "atom type; Links store more than twice the the sizeof(Handle) per\n"
154  << "outgoing atoms.\n";
155  cout << "Type = " << sizeof(Type) << endl;
156  cout << "Handle = " << sizeof(Handle) << endl;
157  cout << "Atom = " << sizeof(Atom) << endl;
158  cout << "Node = " << sizeof(Node) << endl;
159  cout << "Link = " << sizeof(Link) << endl;
160  cout << "SimpleTruthValue = " << sizeof(SimpleTruthValue) << endl;
161  cout << "CountTruthValue = " << sizeof(CountTruthValue) << endl;
162  cout << "IndefiniteTruthValue = " << sizeof(IndefiniteTruthValue) << endl;
163  cout << "AttentionValue = " << sizeof(AttentionValue) << endl;
164  cout << "IncomingSet = " << sizeof(IncomingSet) << endl;
165  cout << "AtomSignal = " << sizeof(AtomSignal) << endl;
166  cout << "AtomPairSignal = " << sizeof(AtomPairSignal) << endl;
167  cout << DIVIDER_LINE << endl;
168 
169 #define ND(T,S) ({Handle n(createNode(T,S)); n;})
170 #define LK(T,A,B) ({Handle l(createLink(T,A,B)); l;})
171  Handle h = ND(CONCEPT_NODE, "this is a test");
172  cout << "ConceptNode \"this is a test\" = "
173  << estimateOfAtomSize(h) << endl;
174 
175  HandleSeq empty;
176  h = Handle(createLink(LIST_LINK, empty));
177  cout << "Empty ListLink = " << estimateOfAtomSize(h) << endl;
178 
179  Handle na = ND(CONCEPT_NODE, "first atom");
180  Handle nb = ND(CONCEPT_NODE, "second atom");
181  Handle ll = LK(LIST_LINK, na, nb);
182  cout << "ListLink with two ConceptNodes = "
183  << estimateOfAtomSize(ll) << endl;
184 
185  Handle np = ND(PREDICATE_NODE, "some predicate");
186  Handle el = LK(EVALUATION_LINK, np, ll);
187  cout << "EvaluationLink with two ConceptNodes = "
188  << estimateOfAtomSize(el) << endl;
189 }
190 
193  cout << "Methods that can be tested:" << endl;
194  cout << " addNode" << endl;
195  cout << " addLink" << endl;
196  cout << " removeAtom" << endl;
197  cout << " getType" << endl;
198  cout << " getTruthValue" << endl;
199  cout << " setTruthValue" << endl;
200  #ifdef ZMQ_EXPERIMENT
201  cout << " getTruthValueZMQ" << endl;
202 #endif
203  cout << " getHandlesByType" << endl;
204  cout << " getOutgoingSet" << endl;
205  cout << " getIncomingSet" << endl;
206 }
207 
208 void AtomSpaceBenchmark::setMethod(std::string methodToTest)
209 {
210  bool foundMethod = false;
211 
212  if (methodToTest == "all" || methodToTest == "noop") {
213  methodsToTest.push_back( &AtomSpaceBenchmark::bm_noop);
214  methodNames.push_back( "noop");
215  foundMethod = true;
216  }
217 
218  if (methodToTest == "all" || methodToTest == "addNode") {
219  methodsToTest.push_back( &AtomSpaceBenchmark::bm_addNode);
220  methodNames.push_back( "addNode");
221  foundMethod = true;
222  }
223 
224  if (methodToTest == "all" || methodToTest == "addLink") {
225  methodsToTest.push_back( &AtomSpaceBenchmark::bm_addLink);
226  methodNames.push_back( "addLink");
227  foundMethod = true;
228  }
229 
230  if (methodToTest == "all" || methodToTest == "removeAtom") {
231  methodsToTest.push_back( &AtomSpaceBenchmark::bm_rmAtom);
232  methodNames.push_back( "removeAtom");
233  foundMethod = true;
234  }
235 
236  if (methodToTest == "all" || methodToTest == "getType") {
237  methodsToTest.push_back( &AtomSpaceBenchmark::bm_getType);
238  methodNames.push_back( "getType");
239  foundMethod = true;
240  }
241 
242  if (methodToTest == "all" || methodToTest == "getTruthValue") {
243  methodsToTest.push_back( &AtomSpaceBenchmark::bm_getTruthValue);
244  methodNames.push_back( "getTruthValue");
245  foundMethod = true;
246  }
247 
248 #ifdef ZMQ_EXPERIMENT
249  if (methodToTest == "all" || methodToTest == "getTruthValueZMQ") {
250  methodsToTest.push_back( &AtomSpaceBenchmark::bm_getTruthValueZmq);
251  methodNames.push_back( "getTruthValueZMQ");
252  foundMethod = true;
253  }
254 #endif
255 
256  if (methodToTest == "all" || methodToTest == "setTruthValue") {
257  methodsToTest.push_back( &AtomSpaceBenchmark::bm_setTruthValue);
258  methodNames.push_back( "setTruthValue");
259  foundMethod = true;
260  }
261 
262  if (methodToTest == "all" || methodToTest == "getHandlesByType") {
263  methodsToTest.push_back( &AtomSpaceBenchmark::bm_getHandlesByType);
264  methodNames.push_back( "getHandlesByType");
265  foundMethod = true;
266  }
267 
268  if (methodToTest == "all" || methodToTest == "getOutgoingSet") {
269  methodsToTest.push_back( &AtomSpaceBenchmark::bm_getOutgoingSet);
270  methodNames.push_back( "getOutgoingSet");
271  foundMethod = true;
272  }
273 
274  if (methodToTest == "all" || methodToTest == "getIncomingSet") {
275  methodsToTest.push_back( &AtomSpaceBenchmark::bm_getIncomingSet);
276  methodNames.push_back( "getIncomingSet");
277  foundMethod = true;
278  }
279 
280  if (!foundMethod) {
281  std::cerr << "Error: specified a bad test name: " << methodToTest << std::endl;
282  exit(1);
283  }
284 
285 }
286 
287 #define CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember))
288 void AtomSpaceBenchmark::doBenchmark(const std::string& methodName,
289  BMFn methodToCall)
290 {
291  clock_t sumAsyncTime = 0;
292  long rssStart;
293  std::vector<record_t> records;
294  cout << "Benchmarking ";
295  switch (testKind) {
296  case BENCH_AS: cout << "AtomSpace's "; break;
297  case BENCH_TABLE: cout << "AtomTable's "; break;
298 #if HAVE_GUILE
299  case BENCH_SCM: cout << "Scheme's ";
300  if (memoize) cout << "memoized ";
301  else if (compile) cout << "compiled ";
302  else cout << "interpreted ";
303  break;
304 #endif /* HAVE_GUILE */
305 #if HAVE_CYTHON
306  case BENCH_PYTHON: cout << "Python's "; break;
307 #endif /* HAVE_CYTHON */
308  }
309  cout << methodName << " method " << (Nreps*Nloops) << " times ";
310  std::ofstream myfile;
311  if (saveToFile)
312  {
313  myfile.open ((methodName + "_benchmark.csv").c_str());
314  }
315  int diff = (Nreps / PROGRESS_BAR_LENGTH);
316  if (!diff) diff = 1;
317  int counter=0;
318  rssStart = getMemUsage();
319  long rssFromIncrease = 0;
320  timeval tim;
321  gettimeofday(&tim, NULL);
322  double t1 = tim.tv_sec + (tim.tv_usec/1000000.0);
323  for (unsigned int i=0; i < Nreps; i++)
324  {
325  if (sizeIncrease)
326  {
327  long rssBeforeIncrease = getMemUsage();
328  buildAtomSpace(sizeIncrease, percentLinks, false);
329  // Try to negate the memory increase due to adding atoms
330  rssFromIncrease += (getMemUsage() - rssBeforeIncrease);
331  }
332  size_t atomspaceSize;
333  if (testKind == BENCH_TABLE)
334  atomspaceSize = atab->getSize();
335  else
336  atomspaceSize = asp->get_size();
337  timepair_t timeTaken = CALL_MEMBER_FN(*this, methodToCall)();
338  sumAsyncTime += get<0>(timeTaken);
339  counter++;
340  if (saveInterval && counter % saveInterval == 0)
341  {
342  // Only save datapoints every saveInterval calls
343  record_t dataPoint(atomspaceSize,get<0>(timeTaken),getMemUsage()-rssStart-rssFromIncrease);
344  // Only save datapoints if we have to calculate the stats
345  // afterwards, otherwise it affects memory usage
346  if (doStats) {
347  if (get<0>(timeTaken) < 0) cout << "ftumf" << endl;
348  records.push_back(dataPoint);
349  }
350  // otherwise, we might write directly to a file
351  if (saveToFile) recordToFile(myfile,dataPoint);
352  }
353  if (i % diff == 0) cerr << "." << flush;
354  }
355  Handle rh = getRandomHandle();
356  gettimeofday(&tim, NULL);
357  double t2=tim.tv_sec+(tim.tv_usec/1000000.0);
358  printf("\n%.6lf seconds elapsed (%.2f per second)\n", t2-t1, 1.0f/((t2-t1)/Nreps));
359  // rssEnd = getMemUsage();
360  cout << "Sum clock() time for all requests: " << sumAsyncTime << " (" <<
361  (float) sumAsyncTime / CLOCKS_PER_SEC << " seconds, "<<
362  1.0f/(((float)sumAsyncTime/CLOCKS_PER_SEC) / (Nreps*Nloops)) << " requests per second)" << endl;
363  //cout << "Memory (max RSS) change after benchmark: " <<
364  // (rssEnd - rssStart - rssFromIncrease) / 1024 << "kb" << endl;
365 
366  if (saveInterval && doStats)
367  {
368  // Only calculate stats if we've actually been saving datapoints
369  // the option to calculate them is enabled
371  t.print();
372  }
373  cout << DIVIDER_LINE << endl;
374  if (saveToFile) { myfile.close(); }
375 }
376 
378 {
379  cout << "OpenCog Atomspace Benchmark - " << VERSION_STRING << "\n";
380  cout << "\nRandom generator: MT19937\n";
381  cout << "Random seed: " << randomseed << "\n\n";
382 
383  // Initialize the random number generator with the seed which might
384  // have been passed in on the command line.
385  if (rng)
386  delete rng;
387  rng = new opencog::MT19937RandGen(randomseed);
388 
389  // Make sure we are using the correct link mean!
390  if (prg) delete prg;
391  prg = new std::poisson_distribution<unsigned>(linkSize_mean);
392 
393  // num threads does nothing at the moment;
394  if (showTypeSizes) printTypeSizes();
395 
396  for (unsigned int i = 0; i < methodNames.size(); i++) {
397  UUID_begin = TLB::getMaxUUID();
398  UUID_end = TLB::getMaxUUID();
399  if (testKind == BENCH_TABLE) {
400  atab = new AtomTable();
401  }
402  else {
403 #if HAVE_CYTHONX
404  cogs = new CogServer();
405  if (pymo == NULL) pymo = new PythonModule(*cogs);
406  pymo->init();
407  asp = &cogs->getAtomSpace();
408  pyev = &PythonEval::instance();
409 
410  // And now ... create a Python instance of the atomspace.
411  // Pass in the raw C++ atomspace address into cython.
412  // Kind-of tacky, but I don't see any better way.
413  // (We must do this because otherwise, the benchmark would
414  // run on a different atomspace, than the one containing
415  // all the atoms. And that would give bad results.
416  std::ostringstream dss;
417  dss << "from opencog.atomspace import AtomSpace, types, Handle, TruthValue\n"
418  << "aspace = AtomSpace(" << asp << ")\n";
419  pyev->eval(dss.str());
420 #else
421  asp = new AtomSpace();
422 #endif
423 #if HAVE_GUILE
424  scm = new SchemeEval(asp);
425 #endif
426  }
427  numberOfTypes = classserver().getNumberOfClasses();
428 
429  if (buildTestData) buildAtomSpace(atomCount, percentLinks, false);
430  UUID_end = TLB::getMaxUUID();
431 
432  doBenchmark(methodNames[i], methodsToTest[i]);
433 
434  if (testKind == BENCH_TABLE)
435  delete atab;
436  else {
437 #if HAVE_GUILE
438  delete scm;
439 #endif
440 #if not HAVE_CYTHON
441  delete asp;
442 #endif
443  }
444  }
445 
446  //cout << estimateOfAtomSize(Handle(2)) << endl;
447  //cout << estimateOfAtomSize(Handle(1020)) << endl;
448 }
449 
450 std::string
452 {
453 #ifdef HAVE_GUILE
454  if (memoize)
455  {
456  std::ostringstream dss;
457  dss << "(define (mk) " << exp << ")\n";
458  scm->eval(dss.str());
459  return "(mk)\n";
460  }
461  if (compile)
462  {
463  std::ostringstream dss;
464  dss << "(compile '(define (mk) " << exp
465  << ") #:env (current-module))\n";
466  scm->eval(dss.str());
467  return "(mk)\n";
468  }
469 #endif /* HAVE_GUILE */
470 #if HAVE_CYTHON
471  if (memoize)
472  {
473  std::ostringstream dss;
474  dss << "def mk():\n" << exp << "\n\n";
475  pyev->eval(dss.str());
476  return "mk()\n\n";
477  }
478 #endif /* HAVE_CYTHON */
479 
480  return exp;
481 }
482 
484 {
485  OC_ASSERT(t < numberOfTypes);
486  Type candidateType;
487 
488  // Loop until we get a type that is a subclass of t, skipping TYPE_NODE
489  // since that type can't handle randomly generated names. Also skip
490  // BIND_LINK and other validated types since the validation will fail.
491  do {
492  candidateType = ATOM + rng->randint(numberOfTypes-1);
493  } while (!classserver().isA(candidateType, t) or
494  !classserver().isA(candidateType, FREE_LINK) or
495  !classserver().isA(candidateType, LAMBDA_LINK) or
496  candidateType == NUMBER_NODE or
497  candidateType == TYPE_NODE);
498 
499  return candidateType;
500 }
501 
502 clock_t AtomSpaceBenchmark::makeRandomNode(const std::string& csi)
503 {
504 #ifdef FIXME_LATER
505  // Some faction of the time, we create atoms with non-default
506  // truth values. XXX implement this for making of links too...
507  bool useDefaultTV = (rng->randfloat() < chanceUseDefaultTV);
509 
510  if (not useDefaultTV) {
511  float strength = rng->randfloat();
512  float conf = rng->randfloat();
513  stv = SimpleTruthValue(strength, conf);
514  }
515 #endif
516 
517  double p = rng->randdouble();
518  Type t = defaultNodeType;
519  if (p < chanceOfNonDefaultNode)
520  t = randomType(NODE);
521 
522  std::string scp(csi);
523  if (csi.size() == 0) {
524  std::ostringstream oss;
525  counter++;
526  if (NUMBER_NODE == t)
527  oss << counter; // number nodes must actually be numbers.
528  else
529  oss << "node " << counter;
530  scp = oss.str();
531  }
532 
533  switch (testKind) {
534  case BENCH_TABLE: {
535  clock_t t_begin = clock();
536  atab->add(createNode(t, scp), false);
537  return clock() - t_begin;
538  }
539  case BENCH_AS: {
540  clock_t t_begin = clock();
541  asp->add_node(t, scp);
542  return clock() - t_begin;
543  }
544 #if HAVE_GUILE
545  case BENCH_SCM: {
546  std::ostringstream ss;
547  for (unsigned int i=0; i<Nloops; i++) {
548  ss << "(cog-new-node '"
549  << classserver().getTypeName(t)
550  << " \"" << scp << "\")\n";
551 
552  p = rng->randdouble();
553  t = defaultNodeType;
554  if (p < chanceOfNonDefaultNode)
555  t = randomType(NODE);
556 
557  scp = csi;
558  if (csi.size() == 0) {
559  std::ostringstream oss;
560  counter++;
561  if (NUMBER_NODE == t)
562  oss << counter; // number nodes must actually be numbers.
563  else
564  oss << "node " << counter;
565  scp = oss.str();
566  }
567  }
568  std::string gs = memoize_or_compile(ss.str());
569 
570  clock_t t_begin = clock();
571  scm->eval_h(gs);
572  return clock() - t_begin;
573  }
574 #endif /* HAVE_GUILE */
575 
576 #if HAVE_CYTHON
577  case BENCH_PYTHON: {
578  std::ostringstream dss;
579  for (unsigned int i=0; i<Nloops; i++) {
580  if (memoize) dss << " "; // indentation
581  dss << "aspace.add_node (" << t << ", \"" << scp << "\")\n";
582 
583  p = rng->randdouble();
584  t = defaultNodeType;
585  if (p < chanceOfNonDefaultNode)
586  t = randomType(NODE);
587 
588  scp = csi;
589  if (csi.size() == 0) {
590  std::ostringstream oss;
591  counter++;
592  if (NUMBER_NODE == t)
593  oss << counter; // number nodes must actually be numbers.
594  else
595  oss << "node " << counter;
596  scp = oss.str();
597  }
598  }
599  std::string ps = memoize_or_compile(dss.str());
600  clock_t t_begin = clock();
601  pyev->eval(ps);
602  return clock() - t_begin;
603  }
604 #endif /* HAVE_CYTHON */
605  }
606 
607  return 0;
608 }
609 
611 {
612  Type t = defaultLinkType;
613  double p = rng->randdouble();
614  if (p < chanceOfNonDefaultLink) t = randomType(LINK);
615 
616  size_t arity = (*prg)(randgen);
617  if (arity == 0) { ++arity; };
618 
619  // AtomSpace will throw if the context link has bad arity
620  if (CONTEXT_LINK == t) arity = 2;
621 
622  HandleSeq outgoing;
623  for (size_t j=0; j < arity; j++) {
624  Handle h(getRandomHandle());
625  outgoing.push_back(h);
626  }
627 
628  switch (testKind) {
629 #if HAVE_CYTHON
630  case BENCH_PYTHON: {
631  OC_ASSERT(1 == Nloops, "Looping not supported for python");
632  std::ostringstream dss;
633  dss << "aspace.add_link (" << t << ", [";
634  for (size_t j=0; j < arity; j++) {
635  dss << "Handle( " << outgoing[j].value() << ")";
636  if (j < arity-1) dss << ", ";
637  }
638  dss << " ] )\n";
639  std::string ps = dss.str();
640  clock_t t_begin = clock();
641  pyev->eval(ps);
642  return clock() - t_begin;
643  }
644 #endif /* HAVE_CYTHON */
645 
646 #if HAVE_GUILE
647  case BENCH_SCM: {
648  // This is somewhat more cumbersome and slower than what
649  // anyone would actually do in scheme, because handles are
650  // never handled in this way, but wtf, not much choice here.
651  // I guess its quasi-realistic as a stand-in for other work
652  // that might be done anyway...
653  std::ostringstream ss;
654  for (unsigned int i=0; i<Nloops; i++) {
655  if (25 < arity) arity = 25;
656  for (size_t j = 0; j < arity; j++) {
657  char c = 'a' + j;
658  ss << "(define h" << c
659  << " (cog-atom " << outgoing[j].value() << "))\n";
660  }
661  ss << "(cog-new-link '"
662  << classserver().getTypeName(t);
663  for (size_t j = 0; j < arity; j++) {
664  char c = 'a' + j;
665  ss << " h" << c;
666  }
667  ss << ")\n";
668 
669  t = defaultLinkType;
670  p = rng->randdouble();
671  if (p < chanceOfNonDefaultLink) t = randomType(LINK);
672 
673  arity = (*prg)(randgen);
674  if (arity == 0) { ++arity; };
675 
676  // AtomSpace will throw if the context link has bad arity
677  if (CONTEXT_LINK == t) arity = 2;
678 
679  outgoing.clear();
680  for (size_t j=0; j < arity; j++) {
681  outgoing.push_back(getRandomHandle());
682  }
683  }
684  std::string gs = memoize_or_compile(ss.str());
685 
686  clock_t t_begin = clock();
687  scm->eval_h(gs);
688  return clock() - t_begin;
689  }
690 #endif /* HAVE_GUILE */
691  case BENCH_TABLE: {
692  clock_t tAddLinkStart = clock();
693  atab->add(createLink(t, outgoing), false);
694  return clock() - tAddLinkStart;
695  }
696  case BENCH_AS: {
697  clock_t tAddLinkStart = clock();
698  asp->add_link(t, outgoing);
699  return clock() - tAddLinkStart;
700  }}
701  return 0;
702 }
703 
704 void AtomSpaceBenchmark::buildAtomSpace(long atomspaceSize, float _percentLinks, bool display)
705 {
706  BenchType saveKind = testKind;
707 #if HAVE_CYTHON
708  if (testKind == BENCH_PYTHON)
709  testKind = BENCH_AS;
710 #endif /* HAVE_CYTHON */
711 #if HAVE_GUILE
712  if (testKind == BENCH_SCM)
713  testKind = BENCH_AS;
714 #endif /* HAVE_GUILE */
715 
716  clock_t tStart = clock();
717  if (display) {
718  cout << "Building atomspace with " << atomspaceSize << " atoms (" <<
719  _percentLinks*100.0 << "\% links)" << endl;
720  }
721 
722  // Add nodes
723  long nodeCount = atomspaceSize * (1.0f - _percentLinks);
724  int i;
725  if (display) cout << "Adding " << nodeCount << " nodes ";
726  int diff = nodeCount / PROGRESS_BAR_LENGTH;
727  if (!diff) diff = 1;
728  for (i=0; i<nodeCount; i++) {
729  makeRandomNode("");
730  if (display && i % diff == 0) cerr << "." << flush;
731  }
732  UUID_end = TLB::getMaxUUID();
733 
734  // Add links
735  if (display) cout << endl << "Adding " << atomspaceSize - nodeCount << " links " << flush;
736  diff = ((atomspaceSize - nodeCount) / PROGRESS_BAR_LENGTH);
737  if (!diff) diff = 1;
738  for (; i < atomspaceSize; i++) {
739  makeRandomLink();
740  if (display && (i-nodeCount) % diff == 0) { cerr << "." << flush; }
741  }
742 
743  if (display) {
744  cout << endl;
745  printf("Built atomspace, execution time: %.2fs\n",
746  (double)(clock() - tStart)/CLOCKS_PER_SEC);
747  cout << DIVIDER_LINE << endl;
748  }
749 
750  UUID_end = TLB::getMaxUUID();
751  testKind = saveKind;
752 }
753 
755 {
756  // Benchmark clock overhead.
757  clock_t t_begin;
758  clock_t time_taken;
759  t_begin = clock();
760  time_taken = clock() - t_begin;
761  return timepair_t(time_taken,0);
762 }
763 
765 {
766  //cout << "Benchmarking AtomSpace::addNode" << endl;
767  return timepair_t(makeRandomNode(""),0);
768 }
769 
771 {
772  //cout << "Benchmarking AtomSpace::addLink" << endl;
773  return timepair_t(makeRandomLink(),0);
774 }
775 
777 {
778  Handle h = getRandomHandle();
779 
780  // Can't remove something that has incoming links, so find something
781  // that doesn't.
782  while (0 < h->getIncomingSetSize()) {
783  h = getRandomHandle();
784  }
785  clock_t t_begin;
786  clock_t time_taken;
787  switch (testKind) {
788 #if HAVE_CYTHON
789  case BENCH_PYTHON: {
790  OC_ASSERT(1 == Nloops, "Looping not supported for python");
791  std::ostringstream dss;
792  for (unsigned int i=0; i<Nloops; i++) {
793  dss << "aspace.remove(Handle(" << h.value() << "))\n";
794  h = getRandomHandle();
795  // XXX FIXME --- this may have trouble finding anything if
796  // Nloops is bigger than the number of links in the atomspace !
797  while (0 < h->getIncomingSetSize()) {
798  h = getRandomHandle();
799  }
800  }
801  std::string ps = dss.str();
802  clock_t t_begin = clock();
803  pyev->eval(ps);
804  return clock() - t_begin;
805  }
806 #endif /* HAVE_CYTHON */
807 #if HAVE_GUILE
808  case BENCH_SCM: {
809  std::ostringstream ss;
810  for (unsigned int i=0; i<Nloops; i++) {
811  ss << "(cog-delete-recursive (cog-atom " << h.value() << "))\n";
812  h = getRandomHandle();
813  // XXX FIXME --- this may have trouble finding anything if
814  // Nloops is bigger than the number of links in the atomspace !
815  while (0 < h->getIncomingSetSize()) {
816  h = getRandomHandle();
817  }
818  }
819  std::string gs = memoize_or_compile(ss.str());
820 
821  clock_t t_begin = clock();
822  scm->eval(gs);
823  time_taken = clock() - t_begin;
824  return timepair_t(time_taken,0);
825  }
826 #endif /* HAVE_GUILE */
827  case BENCH_TABLE: {
828  t_begin = clock();
829  atab->extract(h);
830  time_taken = clock() - t_begin;
831  return timepair_t(time_taken,0);
832  }
833  case BENCH_AS: {
834  t_begin = clock();
835  asp->remove_atom(h);
836  time_taken = clock() - t_begin;
837  return timepair_t(time_taken,0);
838  }}
839  return timepair_t(0,0);
840 }
841 
843 {
844  Handle h(UUID_begin + rng->randint(UUID_end-1-UUID_begin));
845  // operator->() can return NULL when there's no atom for the uuid,
846  // because the atom was deleted in a previous pass! Dohh!
847  while (NULL == h.operator->()) {
848  h = getRandomHandle();
849  }
850  return h;
851 }
852 
854 {
855  Handle h = getRandomHandle();
856  clock_t t_begin;
857  clock_t time_taken;
858  switch (testKind) {
859 #if HAVE_CYTHON
860  case BENCH_PYTHON: {
861  OC_ASSERT(1 == Nloops, "Looping not supported for python");
862  std::ostringstream dss;
863  for (unsigned int i=0; i<Nloops; i++) {
864  dss << "aspace.get_type(Handle(" << h.value() << "))\n";
865  h = getRandomHandle();
866  }
867  std::string ps = dss.str();
868  clock_t t_begin = clock();
869  pyev->eval(ps);
870  return clock() - t_begin;
871  }
872 #endif /* HAVE_CYTHON */
873 #if HAVE_GUILE
874  case BENCH_SCM: {
875  std::ostringstream ss;
876  for (unsigned int i=0; i<Nloops; i++) {
877  ss << "(cog-type (cog-atom " << h.value() << "))\n";
878  h = getRandomHandle();
879  }
880  std::string gs = memoize_or_compile(ss.str());
881 
882  clock_t t_begin = clock();
883  scm->eval(gs);
884  time_taken = clock() - t_begin;
885  return timepair_t(time_taken,0);
886  }
887 #endif /* HAVE_GUILE */
888  case BENCH_TABLE: {
889  t_begin = clock();
890  h->getType();
891  time_taken = clock() - t_begin;
892  return timepair_t(time_taken,0);
893  }
894  case BENCH_AS: {
895  t_begin = clock();
896  asp->get_type(h);
897  time_taken = clock() - t_begin;
898  return timepair_t(time_taken,0);
899  }}
900  return timepair_t(0,0);
901 }
902 
904 {
905  Handle h = getRandomHandle();
906  clock_t t_begin;
907  clock_t time_taken;
908  switch (testKind) {
909 #if HAVE_CYTHON
910  case BENCH_PYTHON: {
911  OC_ASSERT(1 == Nloops, "Looping not supported for python");
912  std::ostringstream dss;
913  dss << "aspace.get_tv(Handle(" << h.value() << "))\n";
914  std::string ps = dss.str();
915  clock_t t_begin = clock();
916  pyev->eval(ps);
917  return clock() - t_begin;
918  }
919 #endif /* HAVE_CYTHON */
920 #if HAVE_GUILE
921  case BENCH_SCM: {
922  std::ostringstream ss;
923  for (unsigned int i=0; i<Nloops; i++) {
924  ss << "(cog-tv (cog-atom " << h.value() << "))\n";
925  h = getRandomHandle();
926  }
927  std::string gs = memoize_or_compile(ss.str());
928 
929  clock_t t_begin = clock();
930  scm->eval(gs);
931  time_taken = clock() - t_begin;
932  return timepair_t(time_taken,0);
933  }
934 #endif /* HAVE_GUILE */
935  case BENCH_TABLE: {
936  t_begin = clock();
937  h->getTruthValue();
938  time_taken = clock() - t_begin;
939  return timepair_t(time_taken,0);
940  }
941  case BENCH_AS: {
942  t_begin = clock();
943  asp->get_TV(h);
944  time_taken = clock() - t_begin;
945  return timepair_t(time_taken,0);
946  }}
947  return timepair_t(0,0);
948 }
949 
950 #ifdef ZMQ_EXPERIMENT
951 timepair_t AtomSpaceBenchmark::bm_getTruthValueZmq()
952 {
953  Handle h = getRandomHandle();
954  clock_t t_begin = clock();
955  asp->getTVZmq(h);
956  return clock() - t_begin;
957 }
958 #endif
959 
961 {
962  Handle h = getRandomHandle();
963 
964  float strength = rng->randfloat();
965  float conf = rng->randfloat();
966 
967  clock_t t_begin;
968  clock_t time_taken;
969  switch (testKind) {
970 #if HAVE_CYTHON
971  case BENCH_PYTHON: {
972  OC_ASSERT(1 == Nloops, "Looping not supported for python");
973  std::ostringstream dss;
974  dss << "aspace.set_tv(Handle(" << h.value()
975  << "), TruthValue(" << strength << ", " << conf << "))\n";
976  std::string ps = dss.str();
977  clock_t t_begin = clock();
978  pyev->eval(ps);
979  return clock() - t_begin;
980  }
981 #endif /* HAVE_CYTHON */
982 #if HAVE_GUILE
983  case BENCH_SCM: {
984  std::ostringstream ss;
985  for (unsigned int i=0; i<Nloops; i++) {
986  ss << "(cog-set-tv! (cog-atom " << h.value() << ")"
987  << " (cog-new-stv " << strength << " " << conf << ")"
988  << ")\n";
989 
990  h = getRandomHandle();
991  strength = rng->randfloat();
992  conf = rng->randfloat();
993  }
994  std::string gs = memoize_or_compile(ss.str());
995 
996  clock_t t_begin = clock();
997  scm->eval(gs);
998  time_taken = clock() - t_begin;
999  return timepair_t(time_taken,0);
1000  }
1001 #endif /* HAVE_GUILE */
1002  case BENCH_TABLE: {
1003  t_begin = clock();
1004  TruthValuePtr stv(SimpleTruthValue::createTV(strength, conf));
1005  h->setTruthValue(stv);
1006  time_taken = clock() - t_begin;
1007  return timepair_t(time_taken,0);
1008  }
1009  case BENCH_AS: {
1010  t_begin = clock();
1011  TruthValuePtr stv(SimpleTruthValue::createTV(strength, conf));
1012  asp->set_TV(h,stv);
1013  time_taken = clock() - t_begin;
1014  return timepair_t(time_taken,0);
1015  }}
1016  return timepair_t(0,0);
1017 }
1018 
1020 {
1021  Type t = randomType(ATOM);
1022  switch (testKind) {
1023 #if HAVE_CYTHON
1024  case BENCH_PYTHON: {
1025  OC_ASSERT(1 == Nloops, "Looping not supported for python");
1026  std::ostringstream dss;
1027  dss << "aspace.get_atoms_by_type(" << t << ", True)\n";
1028  std::string ps = dss.str();
1029  clock_t t_begin = clock();
1030  pyev->eval(ps);
1031  return clock() - t_begin;
1032  }
1033 #endif /* HAVE_CYTHON */
1034 #if HAVE_GUILE
1035  case BENCH_SCM: {
1036  // Currently not expose in the SCM API
1037  return timepair_t(0,0);
1038  }
1039 #endif /* HAVE_GUILE */
1040  case BENCH_TABLE: {
1041  clock_t t_begin = clock();
1042  HandleSeq results;
1043  asp->get_handles_by_type(results, t, true);
1044  clock_t time_taken = clock() - t_begin;
1045  return timepair_t(time_taken,0);
1046  }
1047  case BENCH_AS: {
1048  HandleSeq results;
1049  clock_t t_begin = clock();
1050  asp->get_handles_by_type(back_inserter(results), t, true);
1051  clock_t time_taken = clock() - t_begin;
1052  return timepair_t(time_taken,0);
1053  }}
1054  return timepair_t(0,0);
1055 }
1056 
1058 {
1059  Handle h = getRandomHandle();
1060  clock_t t_begin;
1061  clock_t time_taken;
1062  switch (testKind) {
1063 #if HAVE_CYTHON
1064  case BENCH_PYTHON: {
1065  OC_ASSERT(1 == Nloops, "Looping not supported for python");
1066  std::ostringstream dss;
1067  dss << "aspace.get_outgoing(Handle(" << h.value() << "))\n";
1068  std::string ps = dss.str();
1069  clock_t t_begin = clock();
1070  pyev->eval(ps);
1071  return clock() - t_begin;
1072  }
1073 #endif /* HAVE_CYTHON */
1074 #if HAVE_GUILE
1075  case BENCH_SCM: {
1076  std::ostringstream ss;
1077  for (unsigned int i=0; i<Nloops; i++) {
1078  ss << "(cog-outgoing-set (cog-atom " << h.value() << "))\n";
1079  h = getRandomHandle();
1080  }
1081  std::string gs = memoize_or_compile(ss.str());
1082 
1083  clock_t t_begin = clock();
1084  scm->eval(gs);
1085  time_taken = clock() - t_begin;
1086  return timepair_t(time_taken,0);
1087  }
1088 #endif /* HAVE_GUILE */
1089  case BENCH_TABLE: {
1090  t_begin = clock();
1091  LinkPtr l(atab->getLink(h));
1092  if (l) l->getOutgoingSet();
1093  time_taken = clock() - t_begin;
1094  return timepair_t(time_taken,0);
1095  }
1096  case BENCH_AS: {
1097  t_begin = clock();
1098  asp->get_outgoing(h);
1099  time_taken = clock() - t_begin;
1100  return timepair_t(time_taken,0);
1101  }}
1102  return timepair_t(0,0);
1103 }
1104 
1106 {
1107  Handle h = getRandomHandle();
1108  clock_t t_begin;
1109  clock_t time_taken;
1110  switch (testKind) {
1111 #if HAVE_CYTHON
1112  case BENCH_PYTHON: {
1113  OC_ASSERT(1 == Nloops, "Looping not supported for python");
1114  std::ostringstream dss;
1115  dss << "aspace.get_incoming(Handle(" << h.value() << "))\n";
1116  std::string ps = dss.str();
1117  clock_t t_begin = clock();
1118  pyev->eval(ps);
1119  return clock() - t_begin;
1120  }
1121 #endif /* HAVE_CYTHON */
1122 #if HAVE_GUILE
1123  case BENCH_SCM: {
1124  std::ostringstream ss;
1125  for (unsigned int i=0; i<Nloops; i++) {
1126  ss << "(cog-incoming-set (cog-atom " << h.value() << "))\n";
1127  h = getRandomHandle();
1128  }
1129  std::string gs = memoize_or_compile(ss.str());
1130 
1131  clock_t t_begin = clock();
1132  scm->eval(gs);
1133  time_taken = clock() - t_begin;
1134  return timepair_t(time_taken,0);
1135  }
1136 #endif /* HAVE_GUILE */
1137  case BENCH_TABLE: {
1138  t_begin = clock();
1139  h->getIncomingSet();
1140  time_taken = clock() - t_begin;
1141  return timepair_t(time_taken,0);
1142  }
1143  case BENCH_AS: {
1144  t_begin = clock();
1145  asp->get_incoming(h);
1146  time_taken = clock() - t_begin;
1147  return timepair_t(time_taken,0);
1148  }}
1149  return timepair_t(0,0);
1150 }
1151 
1153  const std::vector<record_t>& records)
1154 {
1155  double sum = 0;
1156  t_min = 1 << 30;
1157  t_max = 0;
1158  for (record_t record : records) {
1159  sum += get<1>(record);
1160  if (get<1>(record) > t_max) t_max = get<1>(record);
1161  if (get<1>(record) < t_min) t_min = get<1>(record);
1162  }
1163  t_total = sum;
1164  t_N = records.size();
1165  t_mean = sum / t_N;
1166  sum = 0.0;
1167  for (record_t record : records) {
1168  clock_t value = (get<1>(record) - t_mean);
1169  sum += (value*value);
1170  }
1171  t_std = sqrt(sum/(t_N-1));
1172 }
1173 
1175 {
1176  cout << "Per operation stats, in CPU clock ticks: " << endl;
1177  cout << " N: " << t_N << endl;
1178  cout << " mean: " << t_mean << endl;
1179  cout << " min: " << t_min << endl;
1180  cout << " max: " << t_max << endl;
1181  cout << " std: " << t_std << endl;
1182 }
1183 
1184 void AtomSpaceBenchmark::recordToFile(std::ofstream& myfile, record_t record) const
1185 {
1186  myfile << tuples::set_open(' ');
1187  myfile << tuples::set_close(' ');
1188  myfile << tuples::set_delimiter(',');
1189  myfile << record;
1190  myfile << "," << (float) get<1>(record) / CLOCKS_PER_SEC << endl;
1191 }
1192 
1193 }
1194 
AttentionValuePtr getAttentionValue()
Definition: Atom.cc:146
#define createLink
Definition: Link.h:269
a TruthValue that stores a mean and the number of observations (strength and confidence) ...
#define PROGRESS_BAR_LENGTH
#define LK(T, A, B)
IncomingSet getIncomingSet()
Definition: Atom.cc:321
std::string memoize_or_compile(std::string)
std::vector< Handle > HandleSeq
a list of handles
Definition: Handle.h:246
std::shared_ptr< TruthValue > TruthValuePtr
Definition: TruthValue.h:85
static UUID getMaxUUID(void)
Definition: TLB.h:93
#define DIVIDER_LINE
void recordToFile(std::ofstream &file, const record_t record) const
void buildAtomSpace(long atomspaceSize=(1<< 16), float percentLinks=0.1, bool display=true)
boost::signals2::signal< void(AtomPtr, LinkPtr)> AtomPairSignal
Definition: Atom.h:58
Type getType() const
Definition: Atom.h:197
std::shared_ptr< Link > LinkPtr
Definition: Atom.h:53
boost::tuple< size_t, clock_t, long > record_t
void setTruthValue(TruthValuePtr)
Sets the TruthValue object of the atom.
Definition: Atom.cc:81
ClassServer & classserver(ClassServerFactory *=ClassServer::createInstance)
Definition: ClassServer.cc:159
static NodePtr NodeCast(const Handle &h)
Definition: Node.h:113
static PythonEval & instance(AtomSpace *atomspace=NULL)
Definition: PythonEval.cc:416
const char * VERSION_STRING
void setMethod(std::string method)
static TruthValuePtr createTV(strength_t mean, count_t count)
static TruthValuePtr DEFAULT_TV()
Definition: TruthValue.cc:52
const std::string & getTypeName(Type type)
Definition: ClassServer.cc:148
#define createNode
Definition: Node.h:119
static LinkPtr LinkCast(const Handle &h)
Definition: Link.h:263
boost::signals2::signal< void(const Handle &)> AtomSignal
Definition: AtomTable.h:62
std::vector< LinkPtr > IncomingSet
Definition: Atom.h:55
void startBenchmark(int numThreads=1)
static AttentionValuePtr DEFAULT_AV()
to be used as default attention value
std::shared_ptr< Node > NodePtr
Definition: Node.h:112
UUID value(void) const
Definition: Handle.h:85
TruthValuePtr getTruthValue()
Definition: Atom.cc:104
clock_t makeRandomNode(const std::string &s)
TimeStats(const std::vector< record_t > &records)
#define ND(T, S)
unsigned short Type
type of Atoms, represented as short integer (16 bits)
Definition: types.h:40
void doBenchmark(const std::string &methodName, BMFn methodToCall)
size_t getIncomingSetSize()
Get the size of the incoming set.
Definition: Atom.cc:310
#define CALL_MEMBER_FN(object, ptrToMember)
a TruthValue that stores a mean, a confidence and the number of observations
boost::tuple< clock_t, clock_t > timepair_t