diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d82b032..2d7eb873 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,7 +54,14 @@ include(PytestBenchmark) #---Add ROOT include direcories and used compilation flags include_directories(${ROOT_INCLUDE_DIRS}) -add_definitions(${ROOT_CXX_FLAGS}) +string(REPLACE " " ";" ROOT_C_FLAGS_LIST ${ROOT_C_FLAGS}) +string(REPLACE " " ";" ROOT_CXX_FLAGS_LIST ${ROOT_CXX_FLAGS}) +foreach(C_COMPILE_FLAG ${ROOT_C_FLAGS_LIST}) + add_compile_options($<$:${C_COMPILE_FLAG}>) +endforeach() +foreach(CXX_COMPILE_FLAG ${ROOT_CXX_FLAGS_LIST}) + add_compile_options($<$:${CXX_COMPILE_FLAG}>) +endforeach() #---Include rootbench options include(RootBenchOptions) @@ -75,6 +82,15 @@ if(rootbench-datafiles) endif(rootbench-datafiles) set(RB_DATASETDIR ${PROJECT_BINARY_DIR}/rootbench-datafiles) +#---Define some constants for Constants.h.in --------------------------------------------------- +if(WIN32) + set(RB_ON_WIN32 1) + set(RB_ON_UNIX 0) +else(WIN32) + set(RB_ON_WIN32 0) + set(RB_ON_UNIX 1) +endif() + #---Enable and setup CTest. include(RootBenchCTest) diff --git a/cmake/modules/AddRootBench.cmake b/cmake/modules/AddRootBench.cmake index 2b0f6144..d1fe9546 100644 --- a/cmake/modules/AddRootBench.cmake +++ b/cmake/modules/AddRootBench.cmake @@ -43,7 +43,10 @@ function(RB_ADD_GBENCHMARK benchmark) # FIXME: For better coherence we could restrict the libraries the test suite could link # against. For example, tests in Core should link only against libCore. This could be tricky # to implement because some ROOT components create more than one library. - target_link_libraries(${benchmark} ${ARG_LIBRARIES} gbenchmark RBSupport rt) + target_link_libraries(${benchmark} PUBLIC ${ARG_LIBRARIES} gbenchmark RBSupport) + if (NOT APPLE) + target_link_libraries(${benchmark} PUBLIC rt) + endif() #ROOT_PATH_TO_STRING(mangled_name ${benchmark} PATH_SEPARATOR_REPLACEMENT "-") #ROOT_ADD_TEST(gbench${mangled_name} # COMMAND ${benchmark} @@ -76,6 +79,29 @@ function(RB_ADD_GBENCHMARK benchmark) FIXTURES_REQUIRED "setup-${benchmark};download-${benchmark}-datafiles") endfunction(RB_ADD_GBENCHMARK) +#---------------------------------------------------------------------------- +# function RB_ADD_GBENCHMARK( source1 source2... LIBRARIES libs) +#---------------------------------------------------------------------------- +function(RB_ADD_MEM_GBENCHMARK benchmark) + RB_ADD_GBENCHMARK(${benchmark} ${ARGN}) + + set (benchmark_env) + if(APPLE) + target_link_libraries(${benchmark} PUBLIC + -Wl,-bind_at_load -Wl,-undefined -Wl,dynamic_lookup + ) + set (benchmark_env "DYLD_FORCE_FLAT_NAMESPACE=1" "DYLD_INSERT_LIBRARIES=$") + elseif(NOT MSVC) + target_link_libraries(${benchmark} PUBLIC + -Wl,--unresolved-symbols=ignore-in-object-files + ) + set (benchmark_env "LD_PRELOAD=$") + endif() + if (benchmark_env) + set_property(TEST rootbench-${benchmark} APPEND PROPERTY ENVIRONMENT ${benchmark_env}) + endif(benchmark_env) + +endfunction(RB_ADD_MEM_GBENCHMARK) #---------------------------------------------------------------------------- # function RB_ADD_PYTESTBENCHMARK( filename) @@ -126,13 +152,19 @@ endfunction() function(RB_ADD_LIBRARY library) cmake_parse_arguments(ARG "" "" "LIBRARIES;DEPENDENCIES" ${ARGN}) set(sources ${ARG_UNPARSED_ARGUMENTS}) - include_directories(BEFORE ${ROOTBENCH_SOURCE_DIR}/include) add_library(${library} STATIC ${sources}) - if (ARG_LIBRARIES OR ARG_DEPENDENCIES) - target_link_libraries(${library} ${ARG_LIBRARIES} ${ARG_DEPENDENCIES}) - endif() + target_include_directories(${library} BEFORE PUBLIC ${ROOTBENCH_SOURCE_DIR}/include) + target_link_libraries(${library} PUBLIC ${ARG_LIBRARIES} ${ARG_DEPENDENCIES} gbenchmark) endfunction(RB_ADD_LIBRARY) +#---------------------------------------------------------------------------- +# function RB_ADD_C_LIBRARY( source1 source2... LIBRARIES libs) +#---------------------------------------------------------------------------- +function(RB_ADD_C_LIBRARY library) + RB_ADD_LIBRARY(${library} ${ARGN}) + set_target_properties(${library} PROPERTIES LINKER_LANGUAGE C) +endfunction(RB_ADD_C_LIBRARY) + #---------------------------------------------------------------------------- # function RB_ADD_TOOL( source1 source2... LIBRARIES libs) #---------------------------------------------------------------------------- diff --git a/include/rootbench/Constants.h.in b/include/rootbench/Constants.h.in index afcdae42..f381e2dc 100644 --- a/include/rootbench/Constants.h.in +++ b/include/rootbench/Constants.h.in @@ -1,3 +1,6 @@ namespace RB { static constexpr const char* kDatasetDirectory = "@RB_DATASETDIR@"; } + +#cmakedefine RB_ON_WIN32 ${RB_ON_WIN32} +#cmakedefine RB_ON_UNIX ${RB_ON_UNIX} diff --git a/include/rootbench/Instrumentation/Memory.h b/include/rootbench/Instrumentation/Memory.h new file mode 100644 index 00000000..576ce07e --- /dev/null +++ b/include/rootbench/Instrumentation/Memory.h @@ -0,0 +1,24 @@ +/// \file Memory.h +/// +/// The file contains interfaces of the C memory instrumentation library. +/// +/// \author Vassil Vassilev +/// +/// \date Oct, 2020 +/// + +#ifndef RB_MEMORY_H +#define RB_MEMORY_H + +#include + +struct malloc_stats { + size_t num_malloc_calls; + size_t max_bytes_used; +}; + +void reset_malloc_stats(); +const struct malloc_stats get_current_malloc_stats(); + + +#endif // RB_MEMORY_H diff --git a/include/rootbench/RBConfig.h b/include/rootbench/RBConfig.h index 108e94e0..c841d322 100644 --- a/include/rootbench/RBConfig.h +++ b/include/rootbench/RBConfig.h @@ -2,12 +2,14 @@ #ifndef RB_CONFIG_H #define RB_CONFIG_H -#include - -#include "rootbench/ErrorHandling.h" #include // RB::kDatasetDirectory + +#include "rootbench/Support/ErrorHandling.h" + #include +#include + namespace RB { /// Returns a path to temporary file system (preferably in-memory). The path /// is set in the RB_TEMP_FS environment variable. @@ -28,16 +30,37 @@ namespace RB { rb_abort("Please set the ROOTSYS env variable!"); } + constexpr const char* GetPreloadEnvVar() { +#ifdef __APPLE__ + // FIXME: Remove the need of DYLD_FORCE_FLAT_NAMESPACE by using interposing. + return "DYLD_INSERT_LIBRARIES"; +#elif defined(RB_ON_UNIX) + return "LD_PRELOAD"; +#else +# error Unsupported Platform; +#endif + } + + /// Checks if we have set up the LD_PRELOAD mechanism for binary + /// instrumentation + inline bool IsLdPreloadEnabled() { + if (char* rootsys = std::getenv(GetPreloadEnvVar())) + return true; + return false; + } + /// Return the absolute path to the directory where data will be downloaded inline std::string GetDataDir() { - return RB::kDatasetDirectory; + return RB::kDatasetDirectory; } /// Like assert, but it does not disappear if -DNDEBUG - inline void Ensure(bool b) + inline void Ensure(bool b, const std::string& reason = "") { - if (!b) - std::abort(); + if (!b) { + printf("Aborting with reason: '%s'\n", reason.c_str()); + std::abort(); + } } } diff --git a/include/rootbench/ErrorHandling.h b/include/rootbench/Support/ErrorHandling.h similarity index 100% rename from include/rootbench/ErrorHandling.h rename to include/rootbench/Support/ErrorHandling.h diff --git a/include/rootbench/Support/MemoryManager.h b/include/rootbench/Support/MemoryManager.h new file mode 100644 index 00000000..27264cf7 --- /dev/null +++ b/include/rootbench/Support/MemoryManager.h @@ -0,0 +1,41 @@ +/// \file MemoryManager.h +/// +/// The file contains the implementation of benchmark memory tracking. +/// +/// \author Vassil Vassilev +/// +/// \date Oct, 2020 +/// + +#ifndef RB_MEMORY_MANAGER_H +#define RB_MEMORY_MANAGER_H + +#include "benchmark/benchmark.h" + +#include + +namespace RB { + /// Records number of allocations per iteration and the maximum bites used + /// for a benchmark. + struct MemoryManager : public benchmark::MemoryManager { + size_t cur_num_allocs = 0; + size_t cur_max_bytes_used = 0; + void Start() override; + void Stop(Result* result) override; + }; +} // end RB + +namespace { + static auto mm = std::unique_ptr(new RB::MemoryManager()); + static struct InstrumentationRegistrer { + InstrumentationRegistrer() { + benchmark::RegisterMemoryManager(mm.get()); + } + ~InstrumentationRegistrer() { + benchmark::RegisterMemoryManager(nullptr); + } + } __mem_mgr_register; +} + + +#endif // RB_MEMORY_MANAGER_H diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 174fa9cd..dccc9646 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,4 +1,3 @@ -RB_ADD_LIBRARY(RBSupport - ErrorHandling.cxx -) -target_include_directories(RBSupport PUBLIC ${PROJECT_BINARY_DIR}/include ${PROJECT_SOURCE_DIR}/include) +#---Add the support libraries. +add_subdirectory(Instrumentation) +add_subdirectory(Support) diff --git a/lib/Instrumentation/CMakeLists.txt b/lib/Instrumentation/CMakeLists.txt new file mode 100644 index 00000000..248a127c --- /dev/null +++ b/lib/Instrumentation/CMakeLists.txt @@ -0,0 +1,4 @@ +RB_ADD_C_LIBRARY(RBInstrumentation SHARED + Memory.c + LIBRARIES dl + ) diff --git a/lib/Instrumentation/Memory.c b/lib/Instrumentation/Memory.c new file mode 100644 index 00000000..9f84c435 --- /dev/null +++ b/lib/Instrumentation/Memory.c @@ -0,0 +1,47 @@ +/// \file Memory.c +/// +/// The file contains code of the C memory instrumentation library. +/// +/// \author Vassil Vassilev +/// +/// \date Oct, 2020 +/// + + +#include "rootbench/Instrumentation/Memory.h" + +#define _GNU_SOURCE +#include +#include +#undef _GNU_SOURCE + +static void* (*real_malloc)(size_t) = NULL; + +static void mtrace_init(void) { + real_malloc = dlsym(RTLD_NEXT, "malloc"); + if (!real_malloc) { + fprintf(stderr, "Error in `dlsym`: %s\n", dlerror()); + } +} + +static struct malloc_stats cur_malloc_stats; +void reset_malloc_stats() { + cur_malloc_stats.num_malloc_calls = 0; + cur_malloc_stats.max_bytes_used = 0; +} + +const struct malloc_stats get_current_malloc_stats() { + return cur_malloc_stats; +} + +void *malloc(size_t size) { + if(!real_malloc) + mtrace_init(); + + void *p = NULL; + p = real_malloc(size); + cur_malloc_stats.num_malloc_calls++; + cur_malloc_stats.max_bytes_used += size; + + return p; +} diff --git a/lib/Support/CMakeLists.txt b/lib/Support/CMakeLists.txt new file mode 100644 index 00000000..1a61ae09 --- /dev/null +++ b/lib/Support/CMakeLists.txt @@ -0,0 +1,8 @@ +RB_ADD_LIBRARY(RBSupport + ErrorHandling.cxx + MemoryManager.cxx +) +target_include_directories(RBSupport PUBLIC + ${PROJECT_BINARY_DIR}/include + ${PROJECT_SOURCE_DIR}/include + ${GBENCHMARK_INCLUDE_DIR}) diff --git a/lib/ErrorHandling.cxx b/lib/Support/ErrorHandling.cxx similarity index 94% rename from lib/ErrorHandling.cxx rename to lib/Support/ErrorHandling.cxx index 3037f2c2..b5835bf5 100644 --- a/lib/ErrorHandling.cxx +++ b/lib/Support/ErrorHandling.cxx @@ -1,6 +1,6 @@ ///\file Contains utilities to handle error situations. -#include "rootbench/ErrorHandling.h" +#include "rootbench/Support/ErrorHandling.h" #include diff --git a/lib/Support/MemoryManager.cxx b/lib/Support/MemoryManager.cxx new file mode 100644 index 00000000..5976a2cc --- /dev/null +++ b/lib/Support/MemoryManager.cxx @@ -0,0 +1,44 @@ +/// \file MemoryManager.h +/// +/// The file contains interfaces for benchmark memory tracking. +/// +/// \author Vassil Vassilev +/// +/// \date Oct, 2020 +/// + +#include + +#include + +extern "C" { + #include +} + +#include + +// FIXME: Remove the need of DYLD_FORCE_FLAT_NAMESPACE by using interposing. +static void EnsureFlatNamespaceOSX() { +#ifdef __APPLE__ + RB::Ensure(std::getenv("DYLD_FORCE_FLAT_NAMESPACE"), + "DYLD_FORCE_FLAT_NAMESPACE not set."); +#endif // __APPLE__ +} + +namespace RB { + void MemoryManager::Start() { + Ensure(IsLdPreloadEnabled(), std::string(GetPreloadEnvVar()) + " not set."); + EnsureFlatNamespaceOSX(); + cur_num_allocs = 0; + cur_max_bytes_used = 0; + reset_malloc_stats(); + } + + void MemoryManager::Stop(Result* result) { + Ensure(IsLdPreloadEnabled(), std::string(GetPreloadEnvVar()) + " not set."); + EnsureFlatNamespaceOSX(); + const malloc_stats& stats = get_current_malloc_stats(); + result->num_allocs = stats.num_malloc_calls; + result->max_bytes_used = stats.max_bytes_used; + } +} // end RB diff --git a/root/interpreter/CMakeLists.txt b/root/interpreter/CMakeLists.txt index f0ffa6be..095d1269 100644 --- a/root/interpreter/CMakeLists.txt +++ b/root/interpreter/CMakeLists.txt @@ -4,4 +4,8 @@ RB_ADD_GBENCHMARK(InterpreterBenchmarks RB_ADD_GBENCHMARK(InterpreterBenchmarksNoPCH RunInterpreterNoPCH.cxx - LABEL short) \ No newline at end of file + LABEL short) + +RB_ADD_MEM_GBENCHMARK(InterpreterLookupHelperBenchmarks + LookupHelper.cxx LIBRARIES Core + LABEL short) diff --git a/root/interpreter/LookupHelper.cxx b/root/interpreter/LookupHelper.cxx new file mode 100644 index 00000000..43d50a9c --- /dev/null +++ b/root/interpreter/LookupHelper.cxx @@ -0,0 +1,24 @@ +#include + +#include + +#include + + +static void BM_LookupHelper_Leak(benchmark::State &state) { + gInterpreter->Declare(R"CODE( +#ifndef BM_LookupHelper_Leak_Guard +#define BM_LookupHelper_Leak_Guard + template class __gmp_expr; + typedef struct{ } __mpz_struct; + typedef __gmp_expr<__mpz_struct[1],__mpz_struct[1]> mpz_class; +#endif // BM_LookupHelper_Leak_Guard + )CODE"); + + for (auto _ : state) + gInterpreter->ClassInfo_IsEnum("std::vector::value_type"); +} + + +BENCHMARK(BM_LookupHelper_Leak); +BENCHMARK_MAIN();