From 054239df667facf46551dd1b69a34f397f48831a Mon Sep 17 00:00:00 2001
From: Axel Naumann <Axel.Naumann@cern.ch>
Date: Thu, 20 Jan 2011 17:27:59 +0000
Subject: [PATCH] Add new interpreter stress test. Should be run interpreted -
 and thus reruns itself interpreted if started as compiled code. Tests
 interpreted function calls (recursive and flat), STL dictionaries, reflection
 retrieval (on existing and non-existing names) and reflection-based calls
 (aka CallFunc) and wildly nested expressions and statements. Normalization of
 ROOTMARKS still needs to be fixed.

git-svn-id: http://root.cern.ch/svn/root/trunk@37819 27541ba8-7e3a-0410-8455-c3a389f83636
---
 test/Makefile              |  17 +-
 test/stressInterpreter.cxx | 461 +++++++++++++++++++++++++++++++++++++
 2 files changed, 474 insertions(+), 4 deletions(-)
 create mode 100644 test/stressInterpreter.cxx

diff --git a/test/Makefile b/test/Makefile
index 3772c856bd2..a4075da91cc 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -236,6 +236,10 @@ THREADSO      = threads.$(ObjSuf)
 THREADSS      = threads.$(SrcSuf)
 THREADS       = threads$(ExeSuf)
 
+STRESSINTERPO = stressInterpreter.$(ObjSuf)
+STRESSINTERPS = stressInterpreter.$(SrcSuf)
+STRESSINTERP  = stressInterpreter$(ExeSuf)
+
 STRESSITERO    = stressIterators.$(ObjSuf)
 STRESSITERS    = stressIterators.$(SrcSuf)
 STRESSITER     = stressIterators$(ExeSuf)
@@ -253,8 +257,8 @@ OBJS          = $(EVENTO) $(MAINEVENTO) $(EVENTMTO) $(HWORLDO) $(HSIMPLEO) $(MIN
                 $(CTORTUREO) $(QPRANDOMO) $(THREADSO) $(STRESSVECO) \
                 $(STRESSMATHO) $(STRESSFITO) $(STRESSHISTOFITO) $(STRESSHEPIXO) \
                 $(STRESSENTRYLISTO) $(STRESSROOFITO) $(STRESSPROOFO) \
-                $(STRESSMATHMOREO) $(STRESSTMVAO) $(STRESSITERO) $(STRESSHISTO) \
-                $(STRESSGUIO)
+                $(STRESSMATHMOREO) $(STRESSTMVAO) $(STRESSINTERPO) $(STRESSITERO) \
+                $(STRESSHISTO) $(STRESSGUIO)
 
 PROGRAMS      = $(EVENT) $(EVENTMTSO) $(HWORLD) $(HSIMPLE) $(MINEXAM) $(TSTRING) \
                 $(TCOLLEX) $(TCOLLBM) $(VVECTOR) $(VMATRIX) $(VLAZY) \
@@ -263,8 +267,8 @@ PROGRAMS      = $(EVENT) $(EVENTMTSO) $(HWORLD) $(HSIMPLE) $(MINEXAM) $(TSTRING)
                 $(TESTBITS) $(CTORTURE) $(QPRANDOM) $(THREADS) $(STRESSSP) \
                 $(STRESSVEC) $(STRESSFIT) $(STRESSHISTOFIT) $(STRESSHEPIX) \
                 $(STRESSENTRYLIST) $(STRESSROOFIT) $(STRESSPROOF) $(STRESSMATH) \
-                $(STRESSMATHMORE) $(STRESSTMVA) $(STRESSITER) $(STRESSHIST) \
-                $(STRESSGUI)
+                $(STRESSMATHMORE) $(STRESSTMVA) $(STRESSINTERP)  $(STRESSITER) \
+                $(STRESSHIST) $(STRESSGUI)
 
 
 OBJS         += $(GUITESTO) $(GUIVIEWERO) $(TETRISO)
@@ -713,6 +717,11 @@ endif
 endif
 endif
 
+$(STRESSINTERP): $(STRESSINTERPO) 
+		$(LD) $(LDFLAGS) $^ $(LIBS) $(OutPutOpt)$@
+		$(MT_EXE)
+		@echo "$@ done"
+
 $(STRESSITER):	$(STRESSITERO) 
 		$(LD) $(LDFLAGS) $^ $(LIBS) $(OutPutOpt)$@
 		$(MT_EXE)
diff --git a/test/stressInterpreter.cxx b/test/stressInterpreter.cxx
new file mode 100644
index 00000000000..3af59ea6293
--- /dev/null
+++ b/test/stressInterpreter.cxx
@@ -0,0 +1,461 @@
+// @(#)root/test:$Id$
+// Author: Axel Naumann, 2011-01-11
+
+/////////////////////////////////////////////////////////////////
+//
+// Stress test (functionality and timing) for C++ interpreter.
+//
+/////////////////////////////////////////////////////////////////
+
+#include <stdlib.h>
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <string>
+
+#include "TSystem.h"
+#include "TBenchmark.h"
+#include "TInterpreter.h"
+#include "TROOT.h"
+#include "TApplication.h"
+
+/////////////////////////////////////////////////////////////////
+// Utility classes / functions
+
+class Base {};
+
+class Klass: public Base {
+public:
+   Klass(): fKlass(this) {}
+   ~Klass() { fKlass = 0; }
+   Klass* get() const { return fKlass; }
+   static const int first_klf = 30;
+   static const int last_klf = 130;
+   int f30(double d) const {return d + 30;}
+   int f31(double d) const {return d + 31;}
+   int f32(double d) const {return d + 32;}
+   int f33(double d) const {return d + 33;}
+   int f34(double d) const {return d + 34;}
+   int f35(double d) const {return d + 35;}
+   int f36(double d) const {return d + 36;}
+   int f37(double d) const {return d + 37;}
+   int f38(double d) const {return d + 38;}
+   int f39(double d) const {return d + 39;}
+   int f40(double d) const {return d + 40;}
+   int f41(double d) const {return d + 41;}
+   int f42(double d) const {return d + 42;}
+   int f43(double d) const {return d + 43;}
+   int f44(double d) const {return d + 44;}
+   int f45(double d) const {return d + 45;}
+   int f46(double d) const {return d + 46;}
+   int f47(double d) const {return d + 47;}
+   int f48(double d) const {return d + 48;}
+   int f49(double d) const {return d + 49;}
+   int f50(double d) const {return d + 50;}
+   int f51(double d) const {return d + 51;}
+   int f52(double d) const {return d + 52;}
+   int f53(double d) const {return d + 53;}
+   int f54(double d) const {return d + 54;}
+   int f55(double d) const {return d + 55;}
+   int f56(double d) const {return d + 56;}
+   int f57(double d) const {return d + 57;}
+   int f58(double d) const {return d + 58;}
+   int f59(double d) const {return d + 59;}
+   int f60(double d) const {return d + 60;}
+   int f61(double d) const {return d + 61;}
+   int f62(double d) const {return d + 62;}
+   int f63(double d) const {return d + 63;}
+   int f64(double d) const {return d + 64;}
+   int f65(double d) const {return d + 65;}
+   int f66(double d) const {return d + 66;}
+   int f67(double d) const {return d + 67;}
+   int f68(double d) const {return d + 68;}
+   int f69(double d) const {return d + 69;}
+   int f70(double d) const {return d + 70;}
+   int f71(double d) const {return d + 71;}
+   int f72(double d) const {return d + 72;}
+   int f73(double d) const {return d + 73;}
+   int f74(double d) const {return d + 74;}
+   int f75(double d) const {return d + 75;}
+   int f76(double d) const {return d + 76;}
+   int f77(double d) const {return d + 77;}
+   int f78(double d) const {return d + 78;}
+   int f79(double d) const {return d + 79;}
+   int f80(double d) const {return d + 80;}
+   int f81(double d) const {return d + 81;}
+   int f82(double d) const {return d + 82;}
+   int f83(double d) const {return d + 83;}
+   int f84(double d) const {return d + 84;}
+   int f85(double d) const {return d + 85;}
+   int f86(double d) const {return d + 86;}
+   int f87(double d) const {return d + 87;}
+   int f88(double d) const {return d + 88;}
+   int f89(double d) const {return d + 89;}
+   int f90(double d) const {return d + 90;}
+   int f91(double d) const {return d + 91;}
+   int f92(double d) const {return d + 92;}
+   int f93(double d) const {return d + 93;}
+   int f94(double d) const {return d + 94;}
+   int f95(double d) const {return d + 95;}
+   int f96(double d) const {return d + 96;}
+   int f97(double d) const {return d + 97;}
+   int f98(double d) const {return d + 98;}
+   int f99(double d) const {return d + 99;}
+   int f100(double d) const {return d + 100;}
+   int f101(double d) const {return d + 101;}
+   int f102(double d) const {return d + 102;}
+   int f103(double d) const {return d + 103;}
+   int f104(double d) const {return d + 104;}
+   int f105(double d) const {return d + 105;}
+   int f106(double d) const {return d + 106;}
+   int f107(double d) const {return d + 107;}
+   int f108(double d) const {return d + 108;}
+   int f109(double d) const {return d + 109;}
+   int f110(double d) const {return d + 110;}
+   int f111(double d) const {return d + 111;}
+   int f112(double d) const {return d + 112;}
+   int f113(double d) const {return d + 113;}
+   int f114(double d) const {return d + 114;}
+   int f115(double d) const {return d + 115;}
+   int f116(double d) const {return d + 116;}
+   int f117(double d) const {return d + 117;}
+   int f118(double d) const {return d + 118;}
+   int f119(double d) const {return d + 119;}
+   int f120(double d) const {return d + 120;}
+   int f121(double d) const {return d + 121;}
+   int f122(double d) const {return d + 122;}
+   int f123(double d) const {return d + 123;}
+   int f124(double d) const {return d + 124;}
+   int f125(double d) const {return d + 125;}
+   int f126(double d) const {return d + 126;}
+   int f127(double d) const {return d + 127;}
+   int f128(double d) const {return d + 128;}
+   int f129(double d) const {return d + 129;}
+   int f130(double d) const {return d + 130;}
+   
+private:
+   Klass* fKlass;
+};
+
+unsigned long func(Long64_t& a, double b, const Klass& c) {
+   if (--a > b) return func(a, b, c);
+   return (unsigned long) c.get();
+}
+
+class InterpreterStress {
+public:
+   InterpreterStress(const char* binary): fNtimes(10), fBinary(binary) {
+      fNames.push_back("FuncCall");
+      fNames.push_back("STLDict");
+      fNames.push_back("Reflection");
+      fNames.push_back("NestedStatements");
+   }
+
+   bool run(Int_t ntimes = 10, const char* runTests = 0);
+
+   bool stressFuncCall();
+
+   void prepareSTLDict();
+   bool stressSTLDict();
+
+   bool stressReflection();
+
+   bool stressNestedStatements();
+
+   std::vector<std::string> fNames;
+
+private:
+   void runPreps() {
+      prepareSTLDict();
+   }
+
+   Int_t fNtimes;
+   TString fBinary;
+};
+
+/////////////////////////////////////////////////////////////////
+// Test function call performance
+
+bool InterpreterStress::stressFuncCall() {
+
+   // This is fast.
+   int ntimes = fNtimes * 100000;
+
+   Klass c;
+   unsigned long res[2];
+   for (int i = 0; i < ntimes / 100; ++i) {
+      // Call recursively:
+      Long64_t a = 100;
+      res[0] = func(a, 0., c);
+   }
+
+   // Call non-recursively:
+   for (Long64_t a = ntimes; a > 0;) {
+      res[1] = func(a, a - 1, c);
+   }
+   if (res[0] != (unsigned long)&c) return false;
+   if (res[0] != res[1]) return false;
+   return true;
+}
+
+
+/////////////////////////////////////////////////////////////////
+// Test custom STL dictionary / calls
+void InterpreterStress::prepareSTLDict() {
+   // Remove AutoDict
+   void* dir = gSystem->OpenDirectory(gSystem->pwd());
+   const char* name = 0;
+   while ((name = gSystem->GetDirEntry(dir))) {
+      if (!strncmp(name, "AutoDict_", 9)) {
+         gSystem->Unlink(name);
+      }
+   }
+   gSystem->FreeDirectory(dir);
+}
+bool InterpreterStress::stressSTLDict() {
+   using namespace std;
+
+   bool allres = true;
+   for (Int_t i = 1; i < fNtimes; ++i) {
+      int res = 3;
+      TInterpreter::EErrorCode interpError = TInterpreter::kNoError;
+      TString cmd = TString::Format("#include <vector>\nclass MyClass;\ntypedef MyClass* Klass%d_t;\nstd::vector<Klass%d_t> v%d;\nvoid stressInterpreter_tmp%d() {\n   v%d.push_back((Klass%d_t)0x12);\n   *((int*)0x%lx) = 0;}", i, i, i, i, i, i, (unsigned long) &res);
+      TString tmpfilename = TString::Format("stressInterpreter_tmp%d.C", i);
+      {
+         std::ofstream otmp(tmpfilename.Data());
+         otmp << cmd << endl;
+      }
+      gInterpreter->ProcessLine(TString(".X ") + tmpfilename, &interpError);
+      gSystem->Unlink(tmpfilename);
+      allres &= (interpError == TInterpreter::kNoError);
+      allres &= (res == 0);
+   }
+   return allres;
+}
+
+/////////////////////////////////////////////////////////////////
+// Test reflection query, reflection-based function call
+bool InterpreterStress::stressReflection() {
+
+   // This is fast
+   int ntimes = fNtimes * 800;
+
+#if !defined(__CINT__) && !defined(__CLING__)
+   TString macro(fBinary);
+   macro += ".cxx";
+   gInterpreter->LoadMacro(macro);
+#endif
+   int numfuncs = Klass::last_klf - Klass::first_klf + 1;
+   bool success = true;
+   for (Int_t i = 0; i < ntimes; ++i) {
+      int funcnum = i % (Long64_t)(1.2 * numfuncs);
+      TString fname = TString::Format("f%d", funcnum);
+      ClassInfo_t* k = gInterpreter->ClassInfo_Factory("Klass");
+      bool hasMethod = gInterpreter->ClassInfo_HasMethod(k, fname.Data());
+      if (hasMethod != (funcnum >= Klass::first_klf && funcnum <= Klass::last_klf)) {
+         success = false;
+      }
+      if (!hasMethod) {
+         gInterpreter->ClassInfo_Delete(k);
+         continue;
+      }
+
+      MethodInfo_t* mk = gInterpreter->CallFunc_Factory();
+      Long_t offset = -1;
+      gInterpreter->CallFunc_SetFuncProto(mk, k, fname, "double", &offset);
+      if (!gInterpreter->CallFunc_IsValid(mk)) {
+         success = false;
+         gInterpreter->CallFunc_Delete(mk);
+         gInterpreter->ClassInfo_Delete(k);
+         continue;
+      }
+      if (offset != 0) {
+         success = false;
+         gInterpreter->CallFunc_Delete(mk);
+         gInterpreter->ClassInfo_Delete(k);
+         continue;
+      }
+
+      gInterpreter->CallFunc_SetArg(mk, -funcnum * 2 + 0.2);
+
+      void* obj = gInterpreter->ClassInfo_New(k);
+      if (!obj) {
+         success = false;
+         gInterpreter->CallFunc_Delete(mk);
+         gInterpreter->ClassInfo_Delete(k);
+         continue;
+      }
+
+      double ret = gInterpreter->CallFunc_ExecDouble(mk, obj);
+      if (ret != funcnum + (-funcnum * 2 + 0.2)) {
+         success = false;
+         gInterpreter->CallFunc_Delete(mk);
+         gInterpreter->ClassInfo_Delete(k);
+         continue;
+      }
+
+      gInterpreter->ClassInfo_Delete(k, obj);
+
+      gInterpreter->CallFunc_Delete(mk);
+      gInterpreter->ClassInfo_Delete(k);
+   }
+
+   return success;
+}
+
+/////////////////////////////////////////////////////////////////
+// Test nested compound statements (if, switch, for,...)
+bool InterpreterStress::stressNestedStatements() {
+   bool success = true;
+   int ntimes = fNtimes * 4;
+   for (int i = 0; i < ntimes; ++i) {
+      for (int pattern = 0; pattern < 0xff; ++pattern) {
+         for (int bit = 0; bit < sizeof(Long64_t)*7; ++bit) {
+            ULong64_t v = 1; // always > 0
+            switch (pattern & 0xf) {
+            case 1: v += 1;
+            case 3: v += 3;
+            case 5: v += 5;
+            case 7: v += 7;
+            case 9: v += 9;
+            case 11: v += 11;
+            case 13: v += 13;
+            case 15: v += 15;
+            default:
+               v += (pattern & 0xf);
+            }
+            v = v << bit;
+
+            if (bit < 32) {
+               if (v > (1ll << 48)) success = false;
+            } else {
+               if (pattern) {
+                  if (!v || v < 1) success = false;
+                  else {
+                     if (bit > 0 && v == pattern && pattern > 0) {
+                        if (success)
+                           success = false;
+                     }
+                  }
+               } else {
+                  if (success) success = true;
+               }
+            }
+            while (v) v = v >> 1;
+            if (v) success = false;
+         }
+      }
+   }
+   return success;
+}
+
+/////////////////////////////////////////////////////////////////
+// Driver
+
+bool InterpreterStress::run(Int_t ntimes /*= 10*/, const char* runTests /*= 0*/) {
+   using namespace std;
+   static const char* benchmark = "stressInterpreter";
+
+   fNtimes = ntimes;
+
+   runPreps();
+
+   gBenchmark->Start(benchmark);
+   cout << "****************************************************************************" <<endl;
+   cout << "*  Starting  stress INTERPRETER                                            *" <<endl;
+   cout << "****************************************************************************" <<endl;
+   bool success = true;
+   for (int itest = 0; itest < fNames.size(); ++itest) {
+      if (runTests && runTests[0]) {
+         // only run test if it was selected
+         if (!strstr(fNames[itest].c_str(), runTests)) continue;
+      }
+      bool res = false;
+      switch (itest) {
+      case 0: res = stressFuncCall(); break;
+      case 1: res = stressSTLDict(); break;
+      case 2: res = stressReflection(); break;
+      case 3: res = stressNestedStatements(); break;
+      }
+      success &= res;
+      printf("%s %s%s\n", fNames[itest].c_str(), TString('.', 77 - fNames[itest].length() - 8).Data(), (res ? "... OK" : " FAILED"));
+   }
+
+   // Summary:
+   gBenchmark->Stop(benchmark);
+   Double_t reftime100 = 600; //pcbrun compiled
+   Double_t ct = gBenchmark->GetCpuTime(benchmark);
+   const Double_t rootmarks = 800*reftime100*ntimes/(100*ct);
+   printf("****************************************************************************\n");
+
+   gBenchmark->Print(benchmark);
+   printf("****************************************************************************\n");
+   printf("*  ROOTMARKS =%6.1f   *  Root%-8s  %d/%d\n",rootmarks,gROOT->GetVersion(),
+         gROOT->GetVersionDate(),gROOT->GetVersionTime());
+   printf("****************************************************************************\n");
+
+   return success;
+}
+
+bool stressInterpreter(Int_t ntimes = 10, const char* runTests = 0, const char* binary = "") {
+   InterpreterStress stress(binary);
+   return stress.run(ntimes, runTests);
+}
+
+#if !defined(__CINT__) && !defined(__CLING__)
+// If compiled: interpret! (by default)
+
+int main(int argc, char **argv)
+{
+   Int_t ntimes = 2;
+   bool runInterpreted = true;
+   TString runTests;
+
+   for (int iarg = 1; iarg < argc; ++iarg) {
+      const char* arg = argv[iarg];
+      if (arg[0] == '-') {
+         if (!strcmp(arg, "--help")) {
+            printf("Interpreter speed test\n");
+            printf("Run as: %s [--help] [-c] [--test=...] [num]\n", gSystem->BaseName(argv[0]));
+            printf("  --help: print this help\n");
+            printf("  -c: run compiled\n");
+            TString alltests;
+            InterpreterStress tmp("");
+            for (int i = 0; i < tmp.fNames.size(); ++i) alltests += TString(" ") + tmp.fNames[i];
+            printf("  --test=...: run only given test, one of%s\n", alltests.Data());
+            printf("  num: run for num iterations (default: %d)\n", ntimes);
+            return 0;
+         }
+         if (!strcmp(arg, "-c")) {
+            runInterpreted = false;
+            continue;
+         }
+         if (!strncmp(arg, "--test=", 7)) {
+            runTests = arg + 7;
+            continue;
+         }
+      } else {
+         ntimes = atoi(argv[1]);
+      }
+   }
+
+   TApplication theApp("App", &argc, argv);
+   gROOT->SetBatch();
+
+   TString exe(argv[0]);
+   if (exe.EndsWith(".exe")) exe.Remove(exe.Length() - 4, 4);
+
+   gBenchmark = new TBenchmark();
+
+   if (runInterpreted) {
+      TString cmd = TString::Format(".L %s.cxx", exe.Data());
+      gInterpreter->ProcessLine(cmd);
+      exe = gSystem->BaseName(exe);
+      cmd = TString::Format("%s(%d, \"%s\", \"%s\")", exe.Data(), ntimes, runTests.Data(), exe.Data());
+      if (!gInterpreter->ProcessLine(cmd)) return 1;
+   } else {
+      if (!stressInterpreter(ntimes, runTests, exe)) return 1;
+   }
+   return 0;
+}
+#endif
-- 
GitLab