diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8c466bbe..276a0ec8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -101,6 +101,13 @@ test_guidelines: - docker run -e CCACHE_DIR=/ccache -v /mnt/ccache:/ccache netgen_${CI_BUILD_REF_NAME}:${UBUNTU_VERSION} bash /root/src/netgen/tests/build_guidelines.sh when: always +# check if it compiles without spdlog +test_noSpdlog: + <<: *ubuntu + stage: test + script: + - docker run -e CCACHE_DIR=/ccache -v /mnt/ccache:/ccache netgen_${CI_BUILD_REF_NAME}:${UBUNTU_VERSION} bash /root/src/netgen/tests/build_nospdlog.sh + cleanup_ubuntu: stage: cleanup tags: diff --git a/CMakeLists.txt b/CMakeLists.txt index 14931c4d..bf7980e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,13 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING INTERNAL) endif(NOT CMAKE_BUILD_TYPE) -cmake_minimum_required(VERSION 3.1.3) +if(WIN32) + # we are linking to object libraries on Windows + cmake_minimum_required(VERSION 3.12) +else(WIN32) + cmake_minimum_required(VERSION 3.1.3) +endif(WIN32) + if(NOT WIN32) option( USE_NATIVE_ARCH "build which -march=native" ON) endif(NOT WIN32) @@ -19,6 +25,9 @@ option( USE_CCACHE "use ccache") option( USE_INTERNAL_TCL "Compile tcl files into the code and don't install them" ON) option( ENABLE_UNIT_TESTS "Enable Catch unit tests") option( ENABLE_CPP_CORE_GUIDELINES_CHECK "Enable cpp core guideline checks on ngcore" OFF) +option( USE_SPDLOG "Enable spd log logging" OFF) +option( DEBUG_LOG "Enable more debug output (may increase computation time) - only works with USE_SPDLOG=ON" OFF) +option( CHECK_RANGE "Check array range access, automatically enabled if built in debug mode" OFF) option( USE_SUPERBUILD "use ccache" ON) @@ -258,6 +267,7 @@ endif (USE_GUI) ####################################################################### if (USE_PYTHON) + add_subdirectory(external_dependencies/pybind11) add_definitions(-DNG_PYTHON) find_path(PYBIND_INCLUDE_DIR pybind11/pybind11.h HINTS ${PYTHON_INCLUDE_DIR}) if( PYBIND_INCLUDE_DIR ) @@ -350,6 +360,11 @@ endif(ENABLE_UNIT_TESTS) ####################################################################### +if(USE_SPDLOG) + include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/external_projects/spdlog.cmake) + include_directories(${SPDLOG_INCLUDE_DIR}) +endif(USE_SPDLOG) + if(ENABLE_CPP_CORE_GUIDELINES_CHECK) find_program( CLANG_TIDY_EXE diff --git a/cmake/SuperBuild.cmake b/cmake/SuperBuild.cmake index 36ea3fcb..646de088 100644 --- a/cmake/SuperBuild.cmake +++ b/cmake/SuperBuild.cmake @@ -142,6 +142,9 @@ set_vars( NETGEN_CMAKE_ARGS CMAKE_INSTALL_PREFIX ENABLE_UNIT_TESTS ENABLE_CPP_CORE_GUIDELINES_CHECK + USE_SPDLOG + DEBUG_LOG + CHECK_RANGE ) # propagate all variables set on the command line using cmake -DFOO=BAR diff --git a/cmake/external_projects/spdlog.cmake b/cmake/external_projects/spdlog.cmake new file mode 100644 index 00000000..d932ebef --- /dev/null +++ b/cmake/external_projects/spdlog.cmake @@ -0,0 +1,18 @@ +include(ExternalProject) +find_program(GIT_EXECUTABLE git) + +ExternalProject_Add( + project_spdlog + PREFIX ${CMAKE_BINARY_DIR}/spdlog + GIT_REPOSITORY https://github.com/gabime/spdlog.git + GIT_TAG v1.2.1 + TIMEOUT 01 + UPDATE_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD ON + ) + +ExternalProject_Get_Property(project_spdlog source_dir) +set(SPDLOG_INCLUDE_DIR ${source_dir}/include) diff --git a/libsrc/core/.clang-tidy b/libsrc/core/.clang-tidy index 290188fb..5742a3d8 100644 --- a/libsrc/core/.clang-tidy +++ b/libsrc/core/.clang-tidy @@ -1,5 +1,5 @@ -Checks: '*,-clang-analyzer-alpha.*,-*braces-around-statements,-fuchsia-*,-google-runtime-references,-readability-implicit-bool-conversion,-google-explicit-constructor,-hicpp-explicit-conversions,-google-runtime-int,-llvm-header-guard' +Checks: '*,-clang-analyzer-alpha.*,-*braces-around-statements,-fuchsia-*,-google-runtime-references,-readability-implicit-bool-conversion,-google-explicit-constructor,-hicpp-explicit-conversions,-google-runtime-int,-llvm-header-guard,-modernize-pass-by-value' CheckOptions: - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor value: 1 -WarningsAsErrors: '*' \ No newline at end of file +WarningsAsErrors: '*' diff --git a/libsrc/core/CMakeLists.txt b/libsrc/core/CMakeLists.txt index 92f2e8b7..46a2230b 100644 --- a/libsrc/core/CMakeLists.txt +++ b/libsrc/core/CMakeLists.txt @@ -1,16 +1,49 @@ -add_library(ngcore SHARED archive.cpp) -target_compile_definitions(ngcore PRIVATE -DNGCORE_EXPORTS) +add_library(ngcore SHARED archive.cpp logging.cpp paje_trace.cpp utils.cpp profiler.cpp) + +target_compile_definitions(ngcore PRIVATE NGCORE_EXPORTS) +if(NOT WIN32) + target_compile_options(ngcore PRIVATE -fvisibility=hidden) +endif(NOT WIN32) + +target_compile_definitions(ngcore PUBLIC $<$:NETGEN_ENABLE_CHECK_RANGE>) + +if(CHECK_RANGE) + target_compile_definitions(ngcore PUBLIC NETGEN_ENABLE_CHECK_RANGE) +endif(CHECK_RANGE) + +if(USE_SPDLOG) + include_directories(${SPDLOG_INCLUDE_DIR}) + install(DIRECTORY ${SPDLOG_INCLUDE_DIR} + DESTINATION ${NG_INSTALL_DIR_INCLUDE} + ) + add_dependencies(ngcore project_spdlog) + target_compile_definitions(ngcore PUBLIC NETGEN_USE_SPDLOG) + if(DEBUG_LOG) + target_compile_definitions(ngcore PUBLIC NETGEN_LOG_DEBUG) + endif(DEBUG_LOG) +endif(USE_SPDLOG) install(TARGETS ngcore DESTINATION ${NG_INSTALL_DIR} COMPONENT netgen) if(USE_PYTHON) + target_compile_definitions(ngcore PUBLIC NETGEN_PYTHON) target_include_directories(ngcore PUBLIC ${PYTHON_INCLUDE_DIRS}) + target_link_libraries(ngcore PUBLIC ${PYTHON_LIBRARIES}) endif(USE_PYTHON) -install(FILES ngcore.hpp archive.hpp type_traits.hpp version.hpp ngcore_api.hpp +install(FILES ngcore.hpp archive.hpp type_traits.hpp version.hpp ngcore_api.hpp logging.hpp + exception.hpp symboltable.hpp paje_trace.hpp utils.hpp profiler.hpp DESTINATION ${NG_INSTALL_DIR_INCLUDE}/core COMPONENT netgen_devel) if(ENABLE_CPP_CORE_GUIDELINES_CHECK) set_target_properties(ngcore PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}") endif(ENABLE_CPP_CORE_GUIDELINES_CHECK) + +if(USE_PYTHON) + pybind11_add_module(pyngcore SHARED python_ngcore.cpp) + target_link_libraries(pyngcore PUBLIC ngcore ${PYTHON_LIBRARIES}) + set_target_properties(pyngcore PROPERTIES INSTALL_RPATH "${NG_RPATH_TOKEN}/${NETGEN_PYTHON_RPATH}") + install(TARGETS pyngcore DESTINATION ${NG_INSTALL_DIR_PYTHON} COMPONENT netgen) +endif(USE_PYTHON) + diff --git a/libsrc/core/archive.cpp b/libsrc/core/archive.cpp index 64789904..9d7f2cd5 100644 --- a/libsrc/core/archive.cpp +++ b/libsrc/core/archive.cpp @@ -19,16 +19,6 @@ namespace ngcore void SetLibraryVersion(const std::string& library, const VersionInfo& version) { library_versions[library] = version; } -#ifdef WIN32 - // windows does demangling in typeid(T).name() - std::string Demangle(const char* typeinfo) { return typeinfo; } -#else - std::string Demangle(const char* typeinfo) { int status; return abi::__cxa_demangle(typeinfo, - nullptr, - nullptr, - &status); } -#endif - // clang-tidy should ignore this static object static std::unique_ptr> type_register; // NOLINT const detail::ClassArchiveInfo& Archive :: GetArchiveRegister(const std::string& classname) diff --git a/libsrc/core/archive.hpp b/libsrc/core/archive.hpp index fd2ee275..6a4a0792 100644 --- a/libsrc/core/archive.hpp +++ b/libsrc/core/archive.hpp @@ -3,31 +3,32 @@ #include // for complex #include // for size_t, strlen -#include // for operator<<, ifstream, ofstream, basic... +#include // for ifstream, ofstream #include // for function -#include // for map, _Rb_tree_iterator -#include // for __shared_ptr_access, __shared_ptr_acc... -#include // for runtime_error -#include // for string, operator+ +#include // for map +#include // for shared_ptr +#include // for string #include // for declval, enable_if, false_type, is_co... #include // for type_info #include // for move, swap, pair #include // for vector -#include "ngcore_api.hpp" // for NGCORE_API, unlikely +#include "exception.hpp" // for UnreachableCodeException, Exception +#include "logging.hpp" // for logger +#include "ngcore_api.hpp" // for NGCORE_API #include "type_traits.hpp" // for all_of_tmpl +#include "utils.hpp" // for Demangle, unlikely #include "version.hpp" // for VersionInfo -#ifdef NG_PYTHON +#ifdef NETGEN_PYTHON #include -#endif // NG_PYTHON +#endif // NETGEN_PYTHON namespace ngcore { // Libraries using this archive can store their version here to implement backwards compatibility NGCORE_API const VersionInfo& GetLibraryVersion(const std::string& library); NGCORE_API void SetLibraryVersion(const std::string& library, const VersionInfo& version); - NGCORE_API std::string Demangle(const char* typeinfo); class NGCORE_API Archive; @@ -36,7 +37,7 @@ namespace ngcore // create new pointer of type T if it is default constructible, else throw template T* constructIfPossible_impl(Rest... /*unused*/) - { throw std::runtime_error(std::string(Demangle(typeid(T).name())) + " is not default constructible!"); } + { throw Exception(std::string(Demangle(typeid(T).name())) + " is not default constructible!"); } template::value>::type> T* constructIfPossible_impl(int /*unused*/) { return new T; } // NOLINT @@ -96,28 +97,34 @@ namespace ngcore { const bool is_output; // how many different shared_ptr/pointer have been (un)archived - int shared_ptr_count, ptr_count; + int shared_ptr_count{0}, ptr_count{0}; // maps for archived shared pointers and pointers - std::map shared_ptr2nr, ptr2nr; + std::map shared_ptr2nr{}, ptr2nr{}; // vectors for storing the unarchived (shared) pointers - std::vector> nr2shared_ptr; - std::vector nr2ptr; + std::vector> nr2shared_ptr{}; + std::vector nr2ptr{}; protected: bool shallow_to_python = false; + std::map version_map = GetLibraryVersions(); + std::shared_ptr logger = GetLogger("Archive"); public: Archive() = delete; Archive(const Archive&) = delete; Archive(Archive&&) = delete; - Archive (bool ais_output) : - is_output(ais_output), shared_ptr_count(0), ptr_count(0) { ; } + Archive (bool ais_output) : is_output(ais_output) { ; } virtual ~Archive() { ; } + // If the object is pickled, all shallow archived objects will be pickled as a list, + // instead of written as a binary archive. This allows pickle to serialize every object only + // once and put them together correctly afterwards. Therefore all objects that may live in + // Python should be archived using this Shallow function. If Shallow is called from C++ code + // it archives the object normally. template Archive& Shallow(T& val) { static_assert(detail::is_any_pointer, "ShallowArchive must be given pointer type!"); -#ifdef NG_PYTHON +#ifdef NETGEN_PYTHON if(shallow_to_python) { if(is_output) @@ -126,25 +133,25 @@ namespace ngcore val = pybind11::cast(ShallowInPython()); } else -#endif // NG_PYTHON +#endif // NETGEN_PYTHON *this & val; return *this; } -#ifdef NG_PYTHON - virtual void ShallowOutPython(pybind11::object /*unused*/) // NOLINT (copy by val is ok for this virt func) - { throw std::runtime_error("Should not get in ShallowToPython base class implementation!"); } +#ifdef NETGEN_PYTHON + virtual void ShallowOutPython(const pybind11::object& /*unused*/) + { throw UnreachableCodeException{}; } virtual pybind11::object ShallowInPython() - { throw std::runtime_error("Should not get in ShallowFromPython base class implementation!"); } -#endif // NG_PYTHON + { throw UnreachableCodeException{}; } +#endif // NETGEN_PYTHON Archive& operator=(const Archive&) = delete; Archive& operator=(Archive&&) = delete; bool Output () const { return is_output; } bool Input () const { return !is_output; } - virtual const VersionInfo& GetVersion(const std::string& library) - { return GetLibraryVersions()[library]; } + const VersionInfo& GetVersion(const std::string& library) + { return version_map[library]; } // Pure virtual functions that have to be implemented by In-/OutArchive virtual Archive & operator & (double & d) = 0; @@ -157,7 +164,7 @@ namespace ngcore virtual Archive & operator & (std::string & str) = 0; virtual Archive & operator & (char *& str) = 0; - virtual Archive & operator & (VersionInfo & version) + Archive & operator & (VersionInfo & version) { if(Output()) (*this) << version.to_string(); @@ -198,6 +205,33 @@ namespace ngcore Do(&v[0], size); return (*this); } + + // vector has special implementation (like a bitarray) therefore + // it needs a special overload (this could probably be more efficient, but we + // don't use it that often anyway) + Archive& operator& (std::vector& v) + { + size_t size; + if(Output()) + size = v.size(); + (*this) & size; + if(Input()) + { + v.resize(size); + bool b; + for(size_t i=0; i Archive& operator& (std::map& map) { @@ -261,28 +295,39 @@ namespace ngcore { if(Output()) { + NETGEN_DEBUG_LOG(logger, "Store shared ptr of type " + Demangle(typeid(T).name())); // save -2 for nullptr if(!ptr) - return (*this) << -2; + { + NETGEN_DEBUG_LOG(logger, "Storing nullptr"); + return (*this) << -2; + } void* reg_ptr = ptr.get(); bool neededDowncast = false; // Downcasting is only possible for our registered classes if(typeid(T) != typeid(*ptr)) { + NETGEN_DEBUG_LOG(logger, "Typids are different: " + Demangle(typeid(T).name()) + " vs. " + + Demangle(typeid(*ptr).name())); if(!IsRegistered(Demangle(typeid(*ptr).name()))) - throw std::runtime_error(std::string("Archive error: Polymorphic type ") - + Demangle(typeid(*ptr).name()) - + " not registered for archive"); + throw Exception(std::string("Archive error: Polymorphic type ") + + Demangle(typeid(*ptr).name()) + + " not registered for archive"); reg_ptr = GetArchiveRegister(Demangle(typeid(*ptr).name())).downcaster(typeid(T), ptr.get()); // if there was a true downcast we have to store more information - if(reg_ptr != static_cast(ptr.get()) ) + if(reg_ptr != static_cast(ptr.get())) + { + NETGEN_DEBUG_LOG(logger, "Multiple/Virtual inheritance involved, need to cast pointer"); neededDowncast = true; + } } auto pos = shared_ptr2nr.find(reg_ptr); // if not found store -1 and the pointer if(pos == shared_ptr2nr.end()) { + NETGEN_DEBUG_LOG(logger, "Didn't find the shared_ptr, create new registry entry at " + + std::to_string(shared_ptr_count)); auto p = ptr.get(); (*this) << -1; (*this) & neededDowncast & p; @@ -293,23 +338,27 @@ namespace ngcore return *this; } // if found store the position and if it has to be downcasted and how + NETGEN_DEBUG_LOG(logger, "Found shared_ptr at position " + std::to_string(pos->second)); (*this) << pos->second << neededDowncast; if(neededDowncast) (*this) << Demangle(typeid(*ptr).name()); } else // Input { + NETGEN_DEBUG_LOG(logger, "Reading shared_ptr of type " + Demangle(typeid(T).name())); int nr; (*this) & nr; // -2 restores a nullptr if(nr == -2) { + NETGEN_DEBUG_LOG(logger, "Reading a nullptr"); ptr = nullptr; return *this; } // -1 restores a new shared ptr by restoring the inner pointer and creating a shared_ptr to it if (nr == -1) { + NETGEN_DEBUG_LOG(logger, "Createing new shared_ptr"); T* p = nullptr; bool neededDowncast; (*this) & neededDowncast & p; @@ -317,6 +366,7 @@ namespace ngcore // if we did downcast we need to store a shared_ptr to the true object if(neededDowncast) { + NETGEN_DEBUG_LOG(logger, "Shared pointer needed downcasting"); std::string name; (*this) & name; auto info = GetArchiveRegister(name); @@ -327,15 +377,20 @@ namespace ngcore ptr.get()))); } else - nr2shared_ptr.push_back(ptr); + { + NETGEN_DEBUG_LOG(logger, "Shared pointer didn't need downcasting"); + nr2shared_ptr.push_back(ptr); + } } else { + NETGEN_DEBUG_LOG(logger, "Reading already existing pointer at entry " + std::to_string(nr)); auto other = nr2shared_ptr[nr]; bool neededDowncast; (*this) & neededDowncast; if(neededDowncast) { + NETGEN_DEBUG_LOG(logger, "Shared pointer needed pointer downcast"); // if there was a downcast we can expect the class to be registered (since archiving // wouldn't have worked else) std::string name; @@ -348,7 +403,10 @@ namespace ngcore other.get()))); } else - ptr = std::static_pointer_cast(other); + { + NETGEN_DEBUG_LOG(logger, "Shared pointer didn't need pointer casts"); + ptr = std::static_pointer_cast(other); + } } } return *this; @@ -360,31 +418,44 @@ namespace ngcore { if (Output()) { + NETGEN_DEBUG_LOG(logger, "Store pointer of type " + Demangle(typeid(T).name())); // if the pointer is null store -2 if (!p) - return (*this) << -2; + { + NETGEN_DEBUG_LOG(logger, "Storing nullptr"); + return (*this) << -2; + } auto reg_ptr = static_cast(p); if(typeid(T) != typeid(*p)) { + NETGEN_DEBUG_LOG(logger, "Typeids are different: " + Demangle(typeid(T).name()) + " vs. " + + Demangle(typeid(*p).name())); if(!IsRegistered(Demangle(typeid(*p).name()))) - throw std::runtime_error(std::string("Archive error: Polymorphic type ") - + Demangle(typeid(*p).name()) - + " not registered for archive"); + throw Exception(std::string("Archive error: Polymorphic type ") + + Demangle(typeid(*p).name()) + + " not registered for archive"); reg_ptr = GetArchiveRegister(Demangle(typeid(*p).name())).downcaster(typeid(T), static_cast(p)); + if(reg_ptr != static_cast(p)) + { + NETGEN_DEBUG_LOG(logger, "Multiple/Virtual inheritance involved, need to cast pointer"); + } } auto pos = ptr2nr.find(reg_ptr); // if the pointer is not found in the map create a new entry if (pos == ptr2nr.end()) { + NETGEN_DEBUG_LOG(logger, "Didn't find pointer, create new registry entry at " + + std::to_string(ptr_count)); ptr2nr[reg_ptr] = ptr_count++; if(typeid(*p) == typeid(T)) if (std::is_constructible::value) { + NETGEN_DEBUG_LOG(logger, "Store standard class pointer (no virt. inh,...)"); return (*this) << -1 & (*p); } else - throw std::runtime_error(std::string("Archive error: Class ") + - Demangle(typeid(*p).name()) + " does not provide a default constructor!"); + throw Exception(std::string("Archive error: Class ") + + Demangle(typeid(*p).name()) + " does not provide a default constructor!"); else { // if a pointer to a base class is archived, the class hierarchy must be registered @@ -392,9 +463,10 @@ namespace ngcore // implement a void DoArchive(Archive&) member function // To recreate the object we need to store the true type of it if(!IsRegistered(Demangle(typeid(*p).name()))) - throw std::runtime_error(std::string("Archive error: Polymorphic type ") - + Demangle(typeid(*p).name()) - + " not registered for archive"); + throw Exception(std::string("Archive error: Polymorphic type ") + + Demangle(typeid(*p).name()) + + " not registered for archive"); + NETGEN_DEBUG_LOG(logger, "Store a possibly more complicated pointer"); return (*this) << -3 << Demangle(typeid(*p).name()) & (*p); } } @@ -402,27 +474,39 @@ namespace ngcore { (*this) & pos->second; bool downcasted = !(reg_ptr == static_cast(p) ); + NETGEN_DEBUG_LOG(logger, "Store a the existing position in registry at " + + std::to_string(pos->second)); + NETGEN_DEBUG_LOG(logger, std::string("Pointer ") + (downcasted ? "needs " : "doesn't need ") + + "downcasting"); // store if the class has been downcasted and the name (*this) << downcasted << Demangle(typeid(*p).name()); } } else { + NETGEN_DEBUG_LOG(logger, "Reading pointer of type " + Demangle(typeid(T).name())); int nr; (*this) & nr; if (nr == -2) // restore a nullptr + { + NETGEN_DEBUG_LOG(logger, "Loading a nullptr"); p = nullptr; + } else if (nr == -1) // create a new pointer of standard type (no virtual or multiple inheritance,...) { + NETGEN_DEBUG_LOG(logger, "Load a new pointer to a simple class"); p = detail::constructIfPossible(); nr2ptr.push_back(p); (*this) & *p; } else if(nr == -3) // restore one of our registered classes that can have multiple inheritance,... { + NETGEN_DEBUG_LOG(logger, "Load a new pointer to a potentially more complicated class " + "(allows for multiple/virtual inheritance,...)"); // As stated above, we want this special behaviour only for our classes that implement DoArchive std::string name; (*this) & name; + NETGEN_DEBUG_LOG(logger, "Name = " + name); auto info = GetArchiveRegister(name); // the creator creates a new object of type name, and returns a void* pointing // to T (which may have an offset) @@ -434,9 +518,13 @@ namespace ngcore } else { + NETGEN_DEBUG_LOG(logger, "Restoring pointer to already existing object at registry position " + + std::to_string(nr)); bool downcasted; std::string name; (*this) & downcasted & name; + NETGEN_DEBUG_LOG(logger, std::string(downcasted ? "Downcasted" : "Not downcasted") + + " object of type " + name); if(downcasted) { // if the class has been downcasted we can assume it is in the register @@ -491,11 +579,11 @@ namespace ngcore { static void* tryUpcast (const std::type_info& /*unused*/, T* /*unused*/) { - throw std::runtime_error("Upcast not successful, some classes are not registered properly for archiving!"); + throw Exception("Upcast not successful, some classes are not registered properly for archiving!"); } static void* tryDowncast (const std::type_info& /*unused*/, void* /*unused*/) { - throw std::runtime_error("Downcast not successful, some classes are not registered properly for archiving!"); + throw Exception("Downcast not successful, some classes are not registered properly for archiving!"); } }; @@ -507,7 +595,7 @@ namespace ngcore try { return GetArchiveRegister(Demangle(typeid(B1).name())). upcaster(ti, static_cast(dynamic_cast(p))); } - catch(std::exception&) + catch(const Exception&) { return Caster::tryUpcast(ti, p); } } @@ -520,7 +608,7 @@ namespace ngcore return dynamic_cast(static_cast(GetArchiveRegister(Demangle(typeid(B1).name())). downcaster(ti, p))); } - catch(std::exception&) + catch(const Exception&) { return Caster::tryDowncast(ti, p); } @@ -536,7 +624,7 @@ namespace ngcore { static_assert(detail::all_of_tmpl::value...>, "Variadic template arguments must be base classes of T"); - detail::ClassArchiveInfo info; + detail::ClassArchiveInfo info {}; info.creator = [this,&info](const std::type_info& ti) -> void* { return typeid(T) == ti ? detail::constructIfPossible() : Archive::Caster::tryUpcast(ti, detail::constructIfPossible()); }; @@ -805,7 +893,7 @@ namespace ngcore } }; -#ifdef NG_PYTHON +#ifdef NETGEN_PYTHON template class PyArchive : public ARCHIVE @@ -813,7 +901,11 @@ namespace ngcore private: pybind11::list lst; size_t index = 0; + protected: using ARCHIVE::stream; + using ARCHIVE::version_map; + using ARCHIVE::logger; + using ARCHIVE::GetLibraryVersions; public: PyArchive(const pybind11::object& alst = pybind11::none()) : ARCHIVE(std::make_shared()), @@ -821,8 +913,13 @@ namespace ngcore { ARCHIVE::shallow_to_python = true; if(Input()) - stream = std::make_shared - (pybind11::cast(lst[pybind11::len(lst)-1])); + { + stream = std::make_shared + (pybind11::cast(lst[pybind11::len(lst)-1])); + *this & version_map; + stream = std::make_shared + (pybind11::cast(lst[pybind11::len(lst)-2])); + } } using ARCHIVE::Output; @@ -831,39 +928,46 @@ namespace ngcore using ARCHIVE::operator&; using ARCHIVE::operator<<; using ARCHIVE::GetVersion; - void ShallowOutPython(pybind11::object val) override { lst.append(val); } + void ShallowOutPython(const pybind11::object& val) override { lst.append(val); } pybind11::object ShallowInPython() override { return lst[index++]; } pybind11::list WriteOut() { FlushBuffer(); lst.append(pybind11::bytes(std::static_pointer_cast(stream)->str())); + stream = std::make_shared(); + *this & GetLibraryVersions(); + lst.append(pybind11::bytes(std::static_pointer_cast(stream)->str())); return lst; } }; template - auto NGSPickle(bool printoutput=false) + auto NGSPickle() { - return pybind11::pickle([printoutput](T* self) + return pybind11::pickle([](T* self) { PyArchive ar; ar & self; auto output = pybind11::make_tuple(ar.WriteOut()); - if(printoutput) - pybind11::print("pickle output of", Demangle(typeid(T).name()),"=", output); + NETGEN_DEBUG_LOG(GetLogger("Archive"), "pickling output for object of type " + + Demangle(typeid(T).name()) + " = " + + std::string(pybind11::str(output))); return output; }, [](pybind11::tuple state) { T* val = nullptr; + NETGEN_DEBUG_LOG(GetLogger("Archive"), "State for unpickling of object of type " + + Demangle(typeid(T).name()) + " = " + + std::string(pybind11::str(state[0]))); PyArchive ar(state[0]); ar & val; return val; }); } -#endif // NG_PYTHON +#endif // NETGEN_PYTHON } // namespace ngcore #endif // NETGEN_CORE_ARCHIVE_HPP diff --git a/libsrc/core/exception.hpp b/libsrc/core/exception.hpp new file mode 100644 index 00000000..35359e65 --- /dev/null +++ b/libsrc/core/exception.hpp @@ -0,0 +1,90 @@ +#ifndef NETGEN_CORE_EXCEPTION_HPP +#define NETGEN_CORE_EXCEPTION_HPP + +#include // for stringstream +#include // for exception +#include // for string + +#include "ngcore_api.hpp" // for NGCORE_API + +namespace ngcore +{ + // Exception for code that shouldn't be executed + class NGCORE_API UnreachableCodeException : public std::exception + { + const char* what() const noexcept override + { + return "Shouldn't get here, something went wrong!"; + } + }; + + // Default exception class + class NGCORE_API Exception : public std::exception + { + /// a verbal description of the exception + std::string m_what; + public: + Exception() = default; + Exception(const Exception&) = default; + Exception(Exception&&) = default; + Exception(const std::string& s) : m_what(s) {} + Exception(const char* s) : m_what(s) {} + ~Exception() override = default; + + Exception& operator =(const Exception&) = default; + Exception& operator =(Exception&&) noexcept = default; + + /// append string to description + Exception & Append (const std::string & s) { m_what += s; return *this; } + /// append string to description + Exception & Append (const char * s) { m_what += s; return *this; } + + /// verbal description of exception + const std::string & What() const { return m_what; } + + /// implement virtual function of std::exception + const char* what() const noexcept override { return m_what.c_str(); } + }; + + // Out of Range exception + class NGCORE_API RangeException : public Exception + { + public: + /// where it occurs, index, minimal and maximal indices + RangeException (const std::string & where, + int ind, int imin, int imax) : Exception("") + { + std::stringstream str; + str << where << ": index " << ind << " out of range [" << imin << "," << imax << "]\n"; + Append (str.str()); + } + + template + RangeException(const std::string& where, const T& value) + { + std::stringstream str; + str << where << " called with wrong value " << value << "\n"; + Append(str.str()); + } + }; + + // Exception used if no simd implementation is available to fall back to standard evaluation + class NGCORE_API ExceptionNOSIMD : public Exception + { public: using Exception::Exception; }; +} // namespace ngcore + +#define NETGEN_CORE_NGEXEPTION_STR_HELPER(x) #x +#define NETGEN_CORE_NGEXEPTION_STR(x) NETGEN_CORE_NGEXEPTION_STR_HELPER(x) + +// Convenience macro to append file name and line of exception origin to the string +#define NG_EXCEPTION(s) ngcore::Exception(__FILE__ ":" NETGEN_CORE_NGEXEPTION_STR(__LINE__) "\t"+std::string(s)) + +#ifdef NETGEN_ENABLE_CHECK_RANGE +#define NETGEN_CHECK_RANGE(value, min, max) \ + { if ((value)<(min) || (value)>=(max)) \ + throw ngcore::RangeException(__FILE__ ":" NETGEN_CORE_NGEXEPTION_STR(__LINE__) "\t", (value), (min), (max)); } +#else // NETGEN_ENABLE_CHECK_RANGE +#define NETGEN_CHECK_RANGE(value, min, max) +#endif // NETGEN_ENABLE_CHECK_RANGE + +#endif // NETGEN_CORE_EXCEPTION_HPP diff --git a/libsrc/core/logging.cpp b/libsrc/core/logging.cpp new file mode 100644 index 00000000..467255cd --- /dev/null +++ b/libsrc/core/logging.cpp @@ -0,0 +1,109 @@ + +#include "logging.hpp" + +#ifdef NETGEN_USE_SPDLOG + +#include +#include + +#endif // NETGEN_USE_SPDLOG + +namespace ngcore +{ +#ifdef NETGEN_USE_SPDLOG + namespace detail + { + std::vector>& GetDefaultSinks() + { + static std::vector> sinks = + { std::make_shared() }; + return sinks; + } + std::shared_ptr CreateDefaultLogger(const std::string& name) + { + auto& default_sinks = GetDefaultSinks(); + auto logger = std::make_shared(name, default_sinks.begin(), default_sinks.end()); + spdlog::details::registry::instance().register_and_init(logger); + return logger; + } + } // namespace detail + + std::shared_ptr GetLogger(const std::string& name) + { + auto logger = spdlog::get(name); + if(!logger) + logger = detail::CreateDefaultLogger(name); + return logger; + } + + void SetLoggingLevel(spdlog::level::level_enum level, const std::string& name) + { + if(!name.empty()) + spdlog::get(name)->set_level(level); + else + spdlog::set_level(level); + } + + void AddFileSink(const std::string& filename, spdlog::level::level_enum level, const std::string& logger) + { + auto sink = std::make_shared(filename); + sink->set_level(level); + if(!logger.empty()) + GetLogger(logger)->sinks().push_back(sink); + else + { + detail::GetDefaultSinks().push_back(sink); + spdlog::details::registry::instance().apply_all([sink](auto logger) { logger->sinks().push_back(sink); }); + } + } + + void AddConsoleSink(spdlog::level::level_enum level, const std::string& logger) + { + auto sink = std::make_shared(); + sink->set_level(level); + if(!logger.empty()) + GetLogger(logger)->sinks().push_back(sink); + else + { + detail::GetDefaultSinks().push_back(sink); + spdlog::details::registry::instance().apply_all([sink](auto logger) { logger->sinks().push_back(sink); }); + } + } + + void ClearLoggingSinks(const std::string& logger) + { + if(!logger.empty()) + GetLogger(logger)->sinks().clear(); + else + { + detail::GetDefaultSinks().clear(); + spdlog::details::registry::instance().apply_all([](auto logger) { logger->sinks().clear(); }); + } + } + + void FlushOnLoggingLevel(spdlog::level::level_enum level, const std::string& logger) + { + if(!logger.empty()) + GetLogger(logger)->flush_on(level); + else + spdlog::flush_on(level); + } + +#else // NETGEN_USE_SPDLOG + + // Dummy functions if no spdlog is available + + std::shared_ptr GetLogger(const std::string& /*unused*/) + { + return std::make_shared(); + } + void SetLoggingLevel(spdlog::level::level_enum /*unused*/, const std::string& /*unused*/) {} + void AddFileSink(const std::string& /*unused*/, spdlog::level::level_enum /*unused*/, + const std::string& /*unused*/) + {} + void AddConsoleSink(spdlog::level::level_enum /*unused*/, const std::string& /*unused*/) {} + void ClearLoggingSinks(const std::string& /*unused*/) {} + void FlushOnLoggingLevel(spdlog::level::level_enum /*unused*/, const std::string& /*unused*/) {} + +#endif // NETGEN_USE_SPDLOG +} //namespace ngcore diff --git a/libsrc/core/logging.hpp b/libsrc/core/logging.hpp new file mode 100644 index 00000000..d5fbf1ac --- /dev/null +++ b/libsrc/core/logging.hpp @@ -0,0 +1,108 @@ +#ifndef NETGEN_CORE_LOGGING_HPP +#define NETGEN_CORE_LOGGING_HPP + +#include +#include +#include + +#include "ngcore_api.hpp" + +#ifdef NETGEN_USE_SPDLOG + +#ifdef NETGEN_LOG_DEBUG +#define SPDLOG_DEBUG_ON +#endif // NETGEN_LOG_DEBUG + +#include +#include +#include + +#define NETGEN_DEBUG_LOG(logger, ...) SPDLOG_DEBUG(logger, __VA_ARGS__) + +#else // NETGEN_USE_SPDLOG + +#include + +namespace spdlog +{ + // Dummys if Netgen is compiled with USE_SPDLOG=OFF. + namespace level + { + enum level_enum + { + trace = 0, + debug = 1, + info = 2, + warn = 3, + err = 4, + critical = 5, + off = 6 + }; + } // namespace level + + class logger + { + public: + void log_helper() {} + + template + void log_helper( T t) { std::clog << t; } + + template + void log_helper( T t, Args ... args) + { + std::clog << t; + log_helper(args...); + std::clog << ", "; + } + + template + void log( level::level_enum level, const char* fmt, Args ... args) + { + std::clog << level << ": " << fmt << "\t Arguments: "; + log_helper(args...); + std::clog << "\n"; + } + + template + void trace( const char* fmt, Args ... args) { log(level::level_enum::trace, fmt, args...); } + template + void debug( const char* fmt, Args ... args) { log(level::level_enum::debug, fmt, args...); } + template + void info( const char* fmt, Args ... args) { log(level::level_enum::info, fmt, args...); } + template + void warn( const char* fmt, Args ... args) { log(level::level_enum::warn, fmt, args...); } + template + void error( const char* fmt, Args ... args) { log(level::level_enum::err, fmt, args...); } + template + void critical( const char* fmt, Args ... args) { log(level::level_enum::critical, fmt, args...); } + }; + + namespace sinks + { + class sink {}; + } // namespace sinks + +} //namespace spdlog + +#define NETGEN_DEBUG_LOG(logger, ...) + +#endif // NETGEN_USE_SPDLOG + +namespace ngcore +{ + namespace detail + { + std::vector>& GetDefaultSinks(); + inline std::shared_ptr CreateDefaultLogger(const std::string& name); + } //namespace detail + + NGCORE_API std::shared_ptr GetLogger(const std::string& name); + NGCORE_API void SetLoggingLevel(spdlog::level::level_enum level, const std::string& name); + NGCORE_API void AddFileSink(const std::string& filename, spdlog::level::level_enum level, const std::string& logger); + NGCORE_API void AddConsoleSink(spdlog::level::level_enum level, const std::string& logger); + NGCORE_API void ClearLoggingSinks(const std::string& logger); + NGCORE_API void FlushOnLoggingLevel(spdlog::level::level_enum level, const std::string& logger); +} // namespace ngcore + +#endif // NETGEN_CORE_LOGGING_HPP diff --git a/libsrc/core/ngcore.hpp b/libsrc/core/ngcore.hpp index 1c8fa591..d73a3ed9 100644 --- a/libsrc/core/ngcore.hpp +++ b/libsrc/core/ngcore.hpp @@ -2,6 +2,10 @@ #define NETGEN_CORE_NGCORE_HPP #include "archive.hpp" +#include "exception.hpp" +#include "logging.hpp" +#include "profiler.hpp" +#include "symboltable.hpp" #include "version.hpp" #endif // NETGEN_CORE_NGCORE_HPP diff --git a/libsrc/core/ngcore_api.hpp b/libsrc/core/ngcore_api.hpp index c08d0ac9..a84a1381 100644 --- a/libsrc/core/ngcore_api.hpp +++ b/libsrc/core/ngcore_api.hpp @@ -5,8 +5,8 @@ #define NGCORE_API_EXPORT __declspec(dllexport) #define NGCORE_API_IMPORT __declspec(dllimport) #else - #define NGCORE_API_EXPORT - #define NGCORE_API_IMPORT + #define NGCORE_API_EXPORT __attribute__((visibility("default"))) + #define NGCORE_API_IMPORT __attribute__((visibility("default"))) #endif #ifdef NGCORE_EXPORTS @@ -15,15 +15,23 @@ #define NGCORE_API NGCORE_API_IMPORT #endif -namespace ngcore -{ -#if defined(__GNUC__) - inline bool likely (bool x) { return bool(__builtin_expect(long(x), 1L)); } - inline bool unlikely (bool x) { return bool(__builtin_expect(long(x), 0L)); } +#ifdef __INTEL_COMPILER + #ifdef WIN32 + #define NETGEN_INLINE __forceinline inline + #define NETGEN_LAMBDA_INLINE + #else + #define NETGEN_INLINE __forceinline inline + #define NETGEN_LAMBDA_INLINE __attribute__ ((__always_inline__)) + #endif #else - inline bool likely (bool x) { return x; } - inline bool unlikely (bool x) { return x; } + #ifdef __GNUC__ + #define NETGEN_INLINE __attribute__ ((__always_inline__)) inline + #define NETGEN_LAMBDA_INLINE __attribute__ ((__always_inline__)) + #define NETGEN_VLA + #else + #define NETGEN_INLINE inline + #define NETGEN_LAMBDA_INLINE + #endif #endif -} // namespace ngcore #endif // NETGEN_CORE_NGCORE_API_HPP diff --git a/libsrc/core/paje_trace.cpp b/libsrc/core/paje_trace.cpp new file mode 100644 index 00000000..23d9e49e --- /dev/null +++ b/libsrc/core/paje_trace.cpp @@ -0,0 +1,668 @@ +#include +#include +#include +#include +#include +#include + +#include "archive.hpp" // for Demangle +#include "paje_trace.hpp" +#include "profiler.hpp" + +static constexpr int MAX_TRACE_LINE_SIZE = 50; +extern const char *header; + +namespace ngcore +{ + // Produce no traces by default + size_t PajeTrace::max_tracefile_size = 11110000; + + // If true, produce variable counting active threads + // increases trace by a factor of two + bool PajeTrace::trace_thread_counter = true; + bool PajeTrace::trace_threads = true; + + PajeTrace :: PajeTrace(int anthreads, std::string aname) + { + start_time = GetTimeCounter(); + + nthreads = anthreads; + tracefile_name = std::move(aname); + + int bytes_per_event=33; + max_num_events_per_thread = std::min( static_cast(std::numeric_limits::max()), max_tracefile_size/bytes_per_event/(2*nthreads+1)*10/7); + if(max_num_events_per_thread>0) + { + logger->info( "Tracefile size = {}MB", max_tracefile_size/1024/1024); + logger->info( "Tracing {} events per thread", max_num_events_per_thread , " events per thread"); + } + + tasks.resize(nthreads); + int reserve_size = std::min(1000000U, max_num_events_per_thread); + for(auto & t : tasks) + t.reserve(reserve_size); + + links.resize(nthreads); + for(auto & l : links) + l.reserve(reserve_size); + + jobs.reserve(reserve_size); + timer_events.reserve(reserve_size); + + tracing_enabled = true; + } + + PajeTrace :: ~PajeTrace() + { + if(!tracefile_name.empty()) + Write(tracefile_name); + } + + + void PajeTrace::StopTracing() + { + if(tracing_enabled && max_num_events_per_thread>0) + { + logger->warn("Maximum number of traces reached, tracing is stopped now."); + } + tracing_enabled = false; + } + + class PajeFile + { + public: + static void Hue2RGB ( double x, double &r, double &g, double &b ) + { + double d = 1.0/6.0; + if(x logger = GetLogger("PajeTrace"); + + + double ConvertTime(TTimePoint t) { + // return time in milliseconds as double + // return std::chrono::duration(t-start_time).count()*1000.0; + // return std::chrono::duration(t-start_time).count() / 2.7e3; + return (t-start_time) / 2.7e6; + } + + enum PType + { + SET_VARIABLE=1, + ADD_VARIABLE, + SUB_VARIABLE, + PUSH_STATE, + POP_STATE, + START_LINK, + STOP_LINK + }; + + struct PajeEvent + { + PajeEvent( int aevent_type, double atime, int atype, int acontainer, double avar_value ) + : time(atime), var_value(avar_value), event_type(aevent_type), type(atype), container(acontainer) + { } + + PajeEvent( int aevent_type, double atime, int atype, int acontainer, int avalue = 0, int aid = 0, bool avalue_is_alias = true ) + : time(atime), event_type(aevent_type), type(atype), container(acontainer), value(avalue), id(aid), value_is_alias(avalue_is_alias) + { } + + PajeEvent( int aevent_type, double atime, int atype, int acontainer, int avalue, int astart_container, int akey ) + : time(atime), event_type(aevent_type), type(atype), container(acontainer), value(avalue), start_container(astart_container), id(akey) + { } + + double time; + double var_value = 0.0; + int event_type; + int type; + int container; + int value = 0; + int start_container = 0; + int id = 0; + bool value_is_alias = true; + + bool operator < (const PajeEvent & other) const { + // Same start and stop times can occur for very small tasks -> take "starting" events first (eg. PajePushState before PajePopState) + if(time == other.time) + return event_type < other.event_type; + return (time < other.time); + } + + int write(FILE *stream) + { + const int &key = id; + const int &end_container = start_container; + switch(event_type) + { + case PajeSetVariable: + return fprintf( stream, "%d\t%.15g\ta%d\ta%d\t%.15g\n", PajeSetVariable, time, type, container, var_value ); // NOLINT + case PajeAddVariable: + return fprintf( stream, "%d\t%.15g\ta%d\ta%d\t%.15g\n", PajeAddVariable, time, type, container, var_value ); // NOLINT + case PajeSubVariable: + return fprintf( stream, "%d\t%.15g\ta%d\ta%d\t%.15g\n", PajeSubVariable, time, type, container, var_value ); // NOLINT + case PajePushState: + if(value_is_alias) + return fprintf( stream, "%d\t%.15g\ta%d\ta%d\ta%d\t%d\n", PajePushState, time, type, container, value, id); // NOLINT + else + return fprintf( stream, "%d\t%.15g\ta%d\ta%d\t%d\t%d\n", PajePushState, time, type, container, value, id); // NOLINT + case PajePopState: + return fprintf( stream, "%d\t%.15g\ta%d\ta%d\n", PajePopState, time, type, container ); // NOLINT + case PajeStartLink: + return fprintf( stream, "%d\t%.15g\ta%d\ta%d\t%d\ta%d\t%d\n", PajeStartLink, time, type, container, value, start_container, key ); // NOLINT + case PajeEndLink: + return fprintf( stream, "%d\t%.15g\ta%d\ta%d\t%d\ta%d\t%d\n", PajeEndLink, time, type, container, value, end_container, key ); // NOLINT + } + return 0; + } + }; + + std::vector events; + + public: + PajeFile() = delete; + PajeFile(const PajeFile &) = delete; + PajeFile(PajeFile &&) = delete; + void operator=(const PajeFile &) = delete; + void operator=(PajeFile &&) = delete; + + PajeFile( const std::string & filename, TTimePoint astart_time ) + { + start_time = astart_time; + ctrace_stream = fopen (filename.c_str(),"w"); // NOLINT + fprintf(ctrace_stream, "%s", header ); // NOLINT + alias_counter = 0; + } + + ~PajeFile() + { + fclose (ctrace_stream); // NOLINT + } + + int DefineContainerType ( int parent_type, const std::string & name ) + { + int alias = ++alias_counter; + if(parent_type!=0) + fprintf( ctrace_stream, "%d\ta%d\ta%d\t\"%s\"\n", PajeDefineContainerType, alias, parent_type, name.c_str() ); // NOLINT + else + fprintf( ctrace_stream, "%d\ta%d\t%d\t\"%s\"\n", PajeDefineContainerType, alias, parent_type, name.c_str() ); // NOLINT + return alias; + } + + int DefineVariableType ( int container_type, const std::string & name ) + { + int alias = ++alias_counter; + fprintf( ctrace_stream, "%d\ta%d\ta%d\t\"%s\"\t\"1.0 1.0 1.0\"\n", PajeDefineVariableType, alias, container_type, name.c_str() ); // NOLINT + return alias; + } + + int DefineStateType ( int type, const std::string & name ) + { + int alias = ++alias_counter; + fprintf( ctrace_stream, "%d\ta%d\ta%d\t\"%s\"\n", PajeDefineStateType, alias, type, name.c_str() ); // NOLINT + return alias; + } + + // int DefineEventType () + // { + // Write("event not implemented"); + // } + + int DefineLinkType (int parent_container_type, int start_container_type, int stop_container_type, const std::string & name) + { + int alias = ++alias_counter; + fprintf( ctrace_stream, "%d\ta%d\ta%d\ta%d\ta%d\t\"%s\"\n", PajeDefineLinkType, alias, parent_container_type, start_container_type, stop_container_type, name.c_str() ); // NOLINT + return alias; + } + + int DefineEntityValue (int type, const std::string & name, double hue = -1) + { + if(hue==-1) + { + std::hash shash; + size_t h = shash(name); + h ^= h>>32U; + h = static_cast(h); + hue = h*1.0/std::numeric_limits::max(); + } + + int alias = ++alias_counter; + double r,g,b; + Hue2RGB( hue, r, g, b ); + fprintf( ctrace_stream, "%d\ta%d\ta%d\t\"%s\"\t\"%.15g %.15g %.15g\"\n", PajeDefineEntityValue, alias, type, name.c_str(), r,g,b ); // NOLINT + return alias; + } + + int CreateContainer ( int type, int parent, const std::string & name ) + { + int alias = ++alias_counter; + if(parent!=0) + fprintf( ctrace_stream, "%d\t0\ta%d\ta%d\ta%d\t\"%s\"\n", PajeCreateContainer, alias, type, parent, name.c_str() ); // NOLINT + else + fprintf( ctrace_stream, "%d\t0\ta%d\ta%d\t%d\t\"%s\"\n", PajeCreateContainer, alias, type, parent, name.c_str() ); // NOLINT + return alias; + } + void DestroyContainer () + {} + + void SetVariable (TTimePoint time, int type, int container, double value ) + { + events.emplace_back( PajeEvent( PajeSetVariable, ConvertTime(time), type, container, value ) ); + } + + void AddVariable (TTimePoint time, int type, int container, double value ) + { + events.emplace_back( PajeEvent( PajeAddVariable, ConvertTime(time), type, container, value ) ); + } + + void SubVariable (TTimePoint time, int type, int container, double value ) + { + events.emplace_back( PajeEvent( PajeSubVariable, ConvertTime(time), type, container, value ) ); + } + + void SetState () + {} + + void PushState ( TTimePoint time, int type, int container, int value, int id = 0, bool value_is_alias = true ) + { + events.emplace_back( PajeEvent( PajePushState, ConvertTime(time), type, container, value, id, value_is_alias) ); + } + + void PopState ( TTimePoint time, int type, int container ) + { + events.emplace_back( PajeEvent( PajePopState, ConvertTime(time), type, container ) ); + } + + void ResetState () + {} + + void StartLink ( TTimePoint time, int type, int container, int value, int start_container, int key ) + { + events.emplace_back( PajeEvent( PajeStartLink, ConvertTime(time), type, container, value, start_container, key ) ); + } + + void EndLink ( TTimePoint time, int type, int container, int value, int end_container, int key ) + { + events.emplace_back( PajeEvent( PajeEndLink, ConvertTime(time), type, container, value, end_container, key ) ); + } + + void NewEvent () + {} + + void WriteEvents() + { + logger->info("Sorting traces..."); + std::sort (events.begin(), events.end()); + + logger->info("Writing traces... "); + for (auto & event : events) + { + event.write( ctrace_stream ); +// fprintf( ctrace_stream, "%s", buf ); // NOLINT + } + logger->info("Done"); + } + + private: + enum + { + PajeDefineContainerType = 0, + PajeDefineVariableType = 1, + PajeDefineStateType = 2, + PajeDefineEventType = 3, + PajeDefineLinkType = 4, + PajeDefineEntityValue = 5, + PajeCreateContainer = 6, + PajeDestroyContainer = 7, + PajeSetVariable = 8, + PajeAddVariable = 9, + PajeSubVariable = 10, + PajeSetState = 11, + PajePushState = 12, + PajePopState = 13, + PajeResetState = 14, + PajeStartLink = 15, + PajeEndLink = 16, + PajeNewEvent = 17 + }; + + }; + + NGCORE_API PajeTrace *trace; + + void PajeTrace::Write( const std::string & filename ) + { + int n_events = jobs.size() + timer_events.size(); + for(auto & vtasks : tasks) + n_events += vtasks.size(); + + logger->info("{} events traced", n_events); + + if(n_events==0) + { + logger->info("No data traced, skip writing trace file"); + return; + } + + if(!tracing_enabled) + { + logger->warn("Tracing stopped during computation due to tracefile size limit of {} megabytes.", max_tracefile_size/1024/1024); + } + + PajeFile paje(filename, start_time); + + const int container_type_task_manager = paje.DefineContainerType( 0, "Task Manager" ); + const int container_type_node = paje.DefineContainerType( container_type_task_manager, "Node"); + const int container_type_thread = paje.DefineContainerType( container_type_task_manager, "Thread"); + const int container_type_timer = container_type_thread; //paje.DefineContainerType( container_type_task_manager, "Timers"); + const int container_type_jobs = paje.DefineContainerType( container_type_task_manager, "Jobs"); + + const int state_type_job = paje.DefineStateType( container_type_jobs, "Job" ); + const int state_type_task = paje.DefineStateType( container_type_thread, "Task" ); + const int state_type_timer = paje.DefineStateType( container_type_timer, "Timer state" ); + + const int variable_type_active_threads = paje.DefineVariableType( container_type_jobs, "Active threads" ); + + const int container_task_manager = paje.CreateContainer( container_type_task_manager, 0, "The task manager" ); + const int container_jobs = paje.CreateContainer( container_type_jobs, container_task_manager, "Jobs" ); + paje.SetVariable( start_time, variable_type_active_threads, container_jobs, 0.0 ); + + const int num_nodes = 1; //task_manager ? task_manager->GetNumNodes() : 1; + + std::vector container_nodes; + container_nodes.reserve(num_nodes); + for(int i=0; i thread_aliases; + thread_aliases.reserve(nthreads); + if(trace_threads) + for (int i=0; i job_map; + std::map job_task_map; + + for(Job & j : jobs) + if(job_map.find(j.type) == job_map.end()) + { + std::string name = Demangle(j.type->name()); + job_map[j.type] = paje.DefineEntityValue( state_type_job, name, -1 ); + job_task_map[j.type] = paje.DefineEntityValue( state_type_task, name, -1 ); + } + + for(Job & j : jobs) + { + paje.PushState( j.start_time, state_type_job, container_jobs, job_map[j.type] ); + paje.PopState( j.stop_time, state_type_job, container_jobs ); + } + + std::set timer_ids; + std::map timer_aliases; + + for(auto & event : timer_events) + timer_ids.insert(event.timer_id); + + + for(auto & vtasks : tasks) + for (Task & t : vtasks) + if(t.id_type==Task::ID_TIMER) + timer_ids.insert(t.id); + + for(auto id : timer_ids) + timer_aliases[id] = paje.DefineEntityValue( state_type_timer, NgProfiler::GetName(id), -1 ); + + int timerdepth = 0; + int maxdepth = 0; + for(auto & event : timer_events) + { + if(event.is_start) + { + timerdepth++; + maxdepth = timerdepth>maxdepth ? timerdepth : maxdepth; + } + else + timerdepth--; + } + + std::vector timer_container_aliases; + timer_container_aliases.resize(maxdepth); + for(int i=0; i links_merged; + links_merged.reserve(nlinks); + std::vector pos(nthreads); + + int nlinks_merged = 0; + while(nlinks_merged < nlinks) + { + int minpos = -1; + TTimePoint mintime = -1; + for (int t = 0; t started_links; + + int link_type = paje.DefineLinkType(container_type_node, container_type_thread, container_type_thread, "links"); + + // match links + for ( auto & l : links_merged ) + { + if(l.is_start) + { + started_links.push_back(l); + } + else + { + unsigned int i = 0; + while(i +#include + +#include "logging.hpp" // for logger +#include "ngcore_api.hpp" // for NGCORE_API +#include "utils.hpp" + +namespace ngcore +{ + + extern NGCORE_API class PajeTrace *trace; + class PajeTrace + { + public: + using TClock = std::chrono::system_clock; + + protected: + std::shared_ptr logger = GetLogger("PajeTrace"); + private: + NGCORE_API static size_t max_tracefile_size; + NGCORE_API static bool trace_thread_counter; + NGCORE_API static bool trace_threads; + + bool tracing_enabled; + TTimePoint start_time; + int nthreads; + + public: + + // Approximate number of events to trace. Tracing will + // be stopped if any thread reaches this number of events + unsigned int max_num_events_per_thread; + + static void SetTraceThreads( bool atrace_threads ) + { + trace_threads = atrace_threads; + } + + static void SetTraceThreadCounter( bool trace_threads ) + { + trace_thread_counter = trace_threads; + } + + static void SetMaxTracefileSize( size_t max_size ) + { + max_tracefile_size = max_size; + } + + std::string tracefile_name; + + struct Job + { + int job_id; + const std::type_info *type; + TTimePoint start_time; + TTimePoint stop_time; + }; + + struct Task + { + int thread_id; + + int id; + int id_type; + + int additional_value; + + TTimePoint start_time; + TTimePoint stop_time; + + static constexpr int ID_NONE = -1; + static constexpr int ID_JOB = 1; + static constexpr int ID_TIMER = 2; + }; + + struct TimerEvent + { + int timer_id; + TTimePoint time; + bool is_start; + int thread_id; + + bool operator < (const TimerEvent & other) const { return time < other.time; } + }; + + struct ThreadLink + { + int thread_id; + int key; + TTimePoint time; + bool is_start; + bool operator < (const ThreadLink & other) const { return time < other.time; } + }; + + std::vector > tasks; + std::vector jobs; + std::vector timer_events; + std::vector > links; + + public: + NGCORE_API void StopTracing(); + + PajeTrace() = delete; + PajeTrace(const PajeTrace &) = delete; + PajeTrace(PajeTrace &&) = delete; + NGCORE_API PajeTrace(int anthreads, std::string aname = ""); + NGCORE_API ~PajeTrace(); + + void operator=(const PajeTrace &) = delete; + void operator=(PajeTrace &&) = delete; + + void StartTimer(int timer_id) + { + if(!tracing_enabled) return; + if(unlikely(timer_events.size() == max_num_events_per_thread)) + StopTracing(); + timer_events.push_back(TimerEvent{timer_id, GetTimeCounter(), true}); + } + + void StopTimer(int timer_id) + { + if(!tracing_enabled) return; + if(unlikely(timer_events.size() == max_num_events_per_thread)) + StopTracing(); + timer_events.push_back(TimerEvent{timer_id, GetTimeCounter(), false}); + } + + NETGEN_INLINE int StartTask(int thread_id, int id, int id_type = Task::ID_NONE, int additional_value = -1) + { + if(!tracing_enabled) return -1; + if(!trace_threads && !trace_thread_counter) return -1; + if(unlikely(tasks[thread_id].size() == max_num_events_per_thread)) + StopTracing(); + int task_num = tasks[thread_id].size(); + tasks[thread_id].push_back( Task{thread_id, id, id_type, additional_value, GetTimeCounter()} ); + return task_num; + } + + void StopTask(int thread_id, int task_num) + { + if(!trace_threads && !trace_thread_counter) return; + if(task_num>=0) + tasks[thread_id][task_num].stop_time = GetTimeCounter(); + } + + void SetTask(int thread_id, int task_num, int additional_value) { + if(!trace_threads && !trace_thread_counter) return; + if(task_num>=0) + tasks[thread_id][task_num].additional_value = additional_value; + } + + void StartJob(int job_id, const std::type_info & type) + { + if(!tracing_enabled) return; + if(jobs.size() == max_num_events_per_thread) + StopTracing(); + jobs.push_back( Job{job_id, &type, GetTimeCounter()} ); + } + + void StopJob() + { + if(tracing_enabled) + jobs.back().stop_time = GetTimeCounter(); + } + + void StartLink(int thread_id, int key) + { + if(!tracing_enabled) return; + if(links[thread_id].size() == max_num_events_per_thread) + StopTracing(); + links[thread_id].push_back( ThreadLink{thread_id, key, GetTimeCounter(), true} ); + } + + void StopLink(int thread_id, int key) + { + if(!tracing_enabled) return; + if(links[thread_id].size() == max_num_events_per_thread) + StopTracing(); + links[thread_id].push_back( ThreadLink{thread_id, key, GetTimeCounter(), false} ); + } + + void Write( const std::string & filename ); + + }; +} // namespace ngcore + +#endif // NETGEN_CORE_PAJE_TRACE_HPP diff --git a/libsrc/core/profiler.cpp b/libsrc/core/profiler.cpp new file mode 100644 index 00000000..29a69182 --- /dev/null +++ b/libsrc/core/profiler.cpp @@ -0,0 +1,117 @@ +#include + +#include "profiler.hpp" + +namespace ngcore +{ + std::vector NgProfiler::timers(NgProfiler::SIZE); // NOLINT + + std::string NgProfiler::filename; + + size_t NgProfiler::dummy_thread_times[NgProfiler::SIZE]; + size_t * NgProfiler::thread_times = NgProfiler::dummy_thread_times; // NOLINT + size_t NgProfiler::dummy_thread_flops[NgProfiler::SIZE]; + size_t * NgProfiler::thread_flops = NgProfiler::dummy_thread_flops; // NOLINT + + std::shared_ptr NgProfiler::logger = GetLogger("Profiler"); // NOLINT + + NgProfiler :: NgProfiler() + { + for (auto & t : timers) + { + t.tottime = 0.0; + t.usedcounter = 0; + t.flops = 0.0; + } + } + + NgProfiler :: ~NgProfiler() + { + if (filename.length()) + { + logger->debug( "write profile to file {}", filename ); + FILE *prof = fopen(filename.c_str(),"w"); // NOLINT + Print (prof); + fclose(prof); // NOLINT + } + + if (getenv ("NGPROFILE")) + { + std::string filename = "netgen.prof"; +#ifdef PARALLEL + filename += "."+ToString(id); +#endif + if (id == 0) logger->info( "write profile to file {}", filename ); + FILE *prof = fopen(filename.c_str(),"w"); // NOLINT + Print (prof); + fclose(prof); // NOLINT + } + } + + void NgProfiler :: Print (FILE * prof) + { + int i = 0; + for (auto & t : timers) + { + if (t.count != 0 || t.usedcounter != 0) + { + fprintf(prof,"job %3i calls %8li, time %6.4f sec",i,t.count,t.tottime); // NOLINT + if(t.flops != 0.0) + fprintf(prof,", MFlops = %6.2f",t.flops / (t.tottime) * 1e-6); // NOLINT + if(t.loads != 0.0) + fprintf(prof,", MLoads = %6.2f",t.loads / (t.tottime) * 1e-6); // NOLINT + if(t.stores != 0.0) + fprintf(prof,", MStores = %6.2f",t.stores / (t.tottime) * 1e-6); // NOLINT + if(t.usedcounter) + fprintf(prof," %s",t.name.c_str()); // NOLINT + fprintf(prof,"\n"); // NOLINT + } + i++; + } + } + + + int NgProfiler :: CreateTimer (const std::string & name) + { + static std::mutex createtimer_mutex; + int nr = -1; + { + std::lock_guard guard(createtimer_mutex); + for (int i = SIZE-1; i > 0; i--) + { + auto & t = timers[i]; + if (!t.usedcounter) + { + t.usedcounter = 1; + t.name = name; + nr = i; + break; + } + } + } + if (nr > -1) return nr; + static bool first_overflow = true; + if (first_overflow) + { + first_overflow = false; + NgProfiler::logger->warn("no more timer available, reusing last one"); + } + return 0; + } + + void NgProfiler :: Reset () + { + for(auto & t : timers) + { + t.tottime = 0.0; + t.count = 0; + t.flops = 0.0; + t.loads = 0; + t.stores = 0; + } + } + + NgProfiler prof; // NOLINT + + +} // namespace ngcore diff --git a/libsrc/core/profiler.hpp b/libsrc/core/profiler.hpp new file mode 100644 index 00000000..96a3fc44 --- /dev/null +++ b/libsrc/core/profiler.hpp @@ -0,0 +1,304 @@ +#ifndef NETGEN_CORE_PROFILER_HPP +#define NETGEN_CORE_PROFILER_HPP + +#include +#include + +#include "logging.hpp" +#include "paje_trace.hpp" +#include "utils.hpp" + +namespace ngcore +{ + class NgProfiler + { + public: + /// maximal number of timers + enum { SIZE = 8*1024 }; + + struct TimerVal + { + TimerVal() = default; + + double tottime = 0.0; + double starttime = 0.0; + double flops = 0.0; + double loads = 0.0; + double stores = 0.0; + long count = 0; + std::string name = ""; + int usedcounter = 0; + }; + + NGCORE_API static std::vector timers; + + NGCORE_API static TTimePoint * thread_times; + NGCORE_API static TTimePoint * thread_flops; + NGCORE_API static std::shared_ptr logger; + NGCORE_API static size_t dummy_thread_times[NgProfiler::SIZE]; + NGCORE_API static size_t dummy_thread_flops[NgProfiler::SIZE]; + private: + + NGCORE_API static std::string filename; + public: + NgProfiler(); + ~NgProfiler(); + + NgProfiler(const NgProfiler &) = delete; + NgProfiler(NgProfiler &&) = delete; + void operator=(const NgProfiler &) = delete; + void operator=(NgProfiler &&) = delete; + + static void SetFileName (const std::string & afilename) { filename = afilename; } + + /// create new timer, use integer index + NGCORE_API static int CreateTimer (const std::string & name); + + NGCORE_API static void Reset (); + + + /// start timer of index nr + static void StartTimer (int nr) + { + timers[nr].starttime = WallTime(); timers[nr].count++; + } + + /// stop timer of index nr + static void StopTimer (int nr) + { + timers[nr].tottime += WallTime()-timers[nr].starttime; + } + + static void StartThreadTimer (size_t nr, size_t tid) + { + thread_times[tid*SIZE+nr] -= GetTimeCounter(); // NOLINT + } + + static void StopThreadTimer (size_t nr, size_t tid) + { + thread_times[tid*SIZE+nr] += GetTimeCounter(); // NOLINT + } + + static void AddThreadFlops (size_t nr, size_t tid, size_t flops) + { + thread_flops[tid*SIZE+nr] += flops; // NOLINT + } + + /// if you know number of flops, provide them to obtain the MFlop - rate + static void AddFlops (int nr, double aflops) { timers[nr].flops += aflops; } + static void AddLoads (int nr, double aloads) { timers[nr].loads += aloads; } + static void AddStores (int nr, double astores) { timers[nr].stores += astores; } + + static int GetNr (const std::string & name) + { + for (int i = SIZE-1; i >= 0; i--) + if (timers[i].name == name) + return i; + return -1; + } + + static double GetTime (int nr) + { + return timers[nr].tottime; + } + + static double GetTime (const std::string & name) + { + for (int i = SIZE-1; i >= 0; i--) + if (timers[i].name == name) + return GetTime (i); + return 0; + } + + static long int GetCounts (int nr) + { + return timers[nr].count; + } + + static double GetFlops (int nr) + { + return timers[nr].flops; + } + + /// change name + static void SetName (int nr, const std::string & name) { timers[nr].name = name; } + static std::string GetName (int nr) { return timers[nr].name; } + /// print profile + NGCORE_API static void Print (FILE * prof); + + class RegionTimer + { + int nr; + public: + /// start timer + RegionTimer (int anr) : nr(anr) { NgProfiler::StartTimer(nr); } + /// stop timer + ~RegionTimer () { NgProfiler::StopTimer(nr); } + + RegionTimer() = delete; + RegionTimer(const RegionTimer &) = delete; + RegionTimer(RegionTimer &&) = delete; + void operator=(const RegionTimer &) = delete; + void operator=(RegionTimer &&) = delete; + }; + }; + + + + class NGCORE_API Timer + { + int timernr; + int priority; + public: + Timer (const std::string & name, int apriority = 1) + : priority(apriority) + { + timernr = NgProfiler::CreateTimer (name); + } + void SetName (const std::string & name) + { + NgProfiler::SetName (timernr, name); + } + void Start () + { + if (priority <= 2) + NgProfiler::StartTimer (timernr); + if (priority <= 1) + if(trace) trace->StartTimer(timernr); + } + void Stop () + { + if (priority <= 2) + NgProfiler::StopTimer (timernr); + if (priority <= 1) + if(trace) trace->StopTimer(timernr); + } + void AddFlops (double aflops) + { + if (priority <= 2) + NgProfiler::AddFlops (timernr, aflops); + } + + double GetTime () { return NgProfiler::GetTime(timernr); } + long int GetCounts () { return NgProfiler::GetCounts(timernr); } + double GetMFlops () + { return NgProfiler::GetFlops(timernr) + / NgProfiler::GetTime(timernr) * 1e-6; } + operator int () { return timernr; } + }; + + + /** + Timer object. + Start / stop timer at constructor / destructor. + */ + class RegionTimer + { + Timer & timer; + public: + /// start timer + RegionTimer (Timer & atimer) : timer(atimer) { timer.Start(); } + /// stop timer + ~RegionTimer () { timer.Stop(); } + + RegionTimer() = delete; + RegionTimer(const RegionTimer &) = delete; + RegionTimer(RegionTimer &&) = delete; + void operator=(const RegionTimer &) = delete; + void operator=(RegionTimer &&) = delete; + }; + + class ThreadRegionTimer + { + size_t nr; + size_t tid; + public: + /// start timer + ThreadRegionTimer (size_t _nr, size_t _tid) : nr(_nr), tid(_tid) + { NgProfiler::StartThreadTimer(nr, tid); } + /// stop timer + ~ThreadRegionTimer () + { NgProfiler::StopThreadTimer(nr, tid); } + + ThreadRegionTimer() = delete; + ThreadRegionTimer(ThreadRegionTimer &&) = delete; + ThreadRegionTimer(const ThreadRegionTimer &) = delete; + void operator=(const ThreadRegionTimer &) = delete; + void operator=(ThreadRegionTimer &&) = delete; + }; + + class RegionTracer + { + int nr; + int thread_id; + public: + static constexpr int ID_JOB = PajeTrace::Task::ID_JOB; + static constexpr int ID_NONE = PajeTrace::Task::ID_NONE; + static constexpr int ID_TIMER = PajeTrace::Task::ID_TIMER; + + RegionTracer() = delete; + RegionTracer(RegionTracer &&) = delete; + RegionTracer(const RegionTracer &) = delete; + void operator=(const RegionTracer &) = delete; + void operator=(RegionTracer &&) = delete; + + /// start trace + RegionTracer (int athread_id, int region_id, int id_type = ID_NONE, int additional_value = -1 ) + : thread_id(athread_id) + { + if (trace) + nr = trace->StartTask (athread_id, region_id, id_type, additional_value); + } + /// start trace with timer + RegionTracer (int athread_id, Timer & timer, int additional_value = -1 ) + : thread_id(athread_id) + { + if (trace) + nr = trace->StartTask (athread_id, static_cast(timer), ID_TIMER, additional_value); + } + + /// set user defined value + void SetValue( int additional_value ) + { + if (trace) + trace->SetTask( thread_id, nr, additional_value ); + } + + /// stop trace + ~RegionTracer () + { + if (trace) + trace->StopTask (thread_id, nr); + } + }; + + + // Helper function for timings + // Run f() at least min_iterations times until max_time seconds elapsed + // returns minimum runtime for a call of f() + template + double RunTiming( TFunc f, double max_time = 0.5, int min_iterations = 10 ) + { + // Make sure the whole test run does not exceed maxtime + double tend = WallTime()+max_time; + + // warmup + f(); + + double tres = std::numeric_limits::max(); + int iteration = 0; + while(WallTime() + +#include "logging.hpp" + +namespace py = pybind11; +using namespace ngcore; + +PYBIND11_MODULE(pyngcore, m) // NOLINT +{ + py::enum_(m, "LOG_LEVEL", "Logging level") + .value("Trace", spdlog::level::trace) + .value("Debug", spdlog::level::debug) + .value("Info", spdlog::level::info) + .value("Warn", spdlog::level::warn) + .value("Error", spdlog::level::err) + .value("Critical", spdlog::level::critical) + .value("Off", spdlog::level::off); + + m.def("SetLoggingLevel", &SetLoggingLevel, py::arg("level"), py::arg("logger")="", + "Set logging level, if name is given only to the specific logger, else set the global logging level"); + m.def("AddFileSink", &AddFileSink, py::arg("filename"), py::arg("level"), py::arg("logger")="", + "Add File sink, either only to logger specified or globally to all loggers"); + m.def("AddConsoleSink", &AddConsoleSink, py::arg("level"), py::arg("logger")="", + "Add console output for specific logger or all if none given"); + m.def("ClearLoggingSinks", &ClearLoggingSinks, py::arg("logger")="", + "Clear sinks of specific logger, or all if none given"); + m.def("FlushOnLoggingLevel", &FlushOnLoggingLevel, py::arg("level"), py::arg("logger")="", + "Flush every message with level at least `level` for specific logger or all loggers if none given."); +} diff --git a/libsrc/core/symboltable.hpp b/libsrc/core/symboltable.hpp new file mode 100644 index 00000000..fdab713c --- /dev/null +++ b/libsrc/core/symboltable.hpp @@ -0,0 +1,144 @@ +#ifndef NETGEN_CORE_SYMBOLTABLE_HPP +#define NETGEN_CORE_SYMBOLTABLE_HPP + +#include +#include +#include + +#include "archive.hpp" +#include "exception.hpp" +#include "ngcore_api.hpp" + +namespace ngcore +{ + /** + A symbol table. + + The symboltable provides a mapping from string identifiers + to the generic type T. The strings are copied. + Complexity by name access is linear, by index is constant. + */ + template + class SymbolTable + { + std::vector names; + std::vector data; + public: + using value_type = T; + using reference = typename std::vector::reference; + using const_reference = typename std::vector::const_reference; + + /// Creates a symboltable + SymbolTable () = default; + SymbolTable (const SymbolTable &) = default; + SymbolTable (SymbolTable &&) noexcept = default; + + ~SymbolTable() = default; + + SymbolTable& operator=(const SymbolTable&) = default; + SymbolTable& operator=(SymbolTable&&) = default; + + template + auto DoArchive(Archive& ar) -> typename std::enable_if, void>::type + { + ar & names & data; + } + + /// INDEX of symbol name, throws exception if unused + size_t Index (const std::string & name) const + { + for (size_t i = 0; i < names.size(); i++) + if (names[i] == name) return i; + throw RangeException("SymbolTable", name); + } + + /// Index of symbol name, returns -1 if unused + int CheckIndex (const std::string & name) const + { + for (int i = 0; i < names.size(); i++) + if (names[i] == name) return i; + return -1; + } + + /// number of identifiers + size_t Size() const + { + return data.size(); + } + + /// Returns reference to element. exception for unused identifier + reference operator[] (const std::string & name) + { + return data[Index (name)]; + } + + const_reference operator[] (const std::string & name) const + { + return data[Index (name)]; + } + + /// Returns reference to i-th element, range check only in debug build + reference operator[] (size_t i) + { + NETGEN_CHECK_RANGE(i, 0, data.size()); + return data[i]; + } + + /// Returns const reference to i-th element, range check only in debug build + const_reference operator[] (size_t i) const + { + NETGEN_CHECK_RANGE(i, 0, data.size()); + return data[i]; + } + + /// Returns name of i-th element, range check only in debug build + const std::string & GetName (size_t i) const + { + NETGEN_CHECK_RANGE(i, 0, names.size()); + return names[i]; + } + + /// Associates el to the string name, overrides if name is used + void Set (const std::string & name, const T & el) + { + int i = CheckIndex (name); + if (i >= 0) + data[i] = el; + else + { + data.push_back(el); + names.push_back(name); + } + } + + bool Used (const std::string & name) const + { + return CheckIndex(name) >= 0; + } + + /// Deletes symboltable + inline void DeleteAll () + { + names.clear(); + data.clear(); + } + + // Adds all elements from other symboltable + SymbolTable& Update(const SymbolTable& tbl2) + { + for (size_t i = 0; i < tbl2.Size(); i++) + Set (tbl2.GetName(i), tbl2[i]); + return *this; + } + }; + + template + std::ostream & operator<< (std::ostream & ost, const SymbolTable & st) + { + for (int i = 0; i < st.Size(); i++) + ost << st.GetName(i) << " : " << st[i] << std::endl; + return ost; + } +} // namespace ngcore + +#endif // NETGEN_CORE_SYMBOLTABLE_HPP diff --git a/libsrc/core/utils.cpp b/libsrc/core/utils.cpp new file mode 100644 index 00000000..0347d244 --- /dev/null +++ b/libsrc/core/utils.cpp @@ -0,0 +1,42 @@ +#include "utils.hpp" +#include "logging.hpp" + +#ifndef WIN32 +#include +#endif +#include + +namespace ngcore +{ + // parallel netgen + int id = 0, ntasks = 1; + +#ifdef WIN32 + // windows does demangling in typeid(T).name() + NGCORE_API std::string Demangle(const char* typeinfo) { return typeinfo; } +#else + NGCORE_API std::string Demangle(const char* typeinfo) { int status; return abi::__cxa_demangle(typeinfo, + nullptr, + nullptr, + &status); } +#endif + + double ticks_per_second = [] () noexcept + { + auto tick_start = GetTimeCounter(); + double tstart = WallTime(); + double tend = WallTime()+0.001; + + // wait for 1ms and compare wall time with time counter + while(WallTime() wall_time_start = TClock::now(); + +} // namespace ngcore + diff --git a/libsrc/core/utils.hpp b/libsrc/core/utils.hpp new file mode 100644 index 00000000..da90e1d3 --- /dev/null +++ b/libsrc/core/utils.hpp @@ -0,0 +1,62 @@ +#ifndef NETGEN_CORE_UTILS_HPP +#define NETGEN_CORE_UTILS_HPP + +#include +#include +#include + +#ifdef WIN32 +#include // for __rdtsc() CPU time step counter +#else +#include // for __rdtsc() CPU time step counter +#endif // WIN32 + +#include "ngcore_api.hpp" // for NGCORE_API + +namespace ngcore +{ + // MPI rank, nranks TODO: Rename + extern NGCORE_API int id, ntasks; + + NGCORE_API std::string Demangle(const char* typeinfo); + +#if defined(__GNUC__) + inline bool likely (bool x) { return bool(__builtin_expect(long(x), 1L)); } + inline bool unlikely (bool x) { return bool(__builtin_expect(long(x), 0L)); } +#else + inline bool likely (bool x) { return x; } + inline bool unlikely (bool x) { return x; } +#endif + + using TClock = std::chrono::system_clock; + extern NGCORE_API const std::chrono::time_point wall_time_start; + + // Time in seconds since program start + inline double WallTime () noexcept + { + std::chrono::time_point now = TClock::now(); + std::chrono::duration elapsed_seconds = now-wall_time_start; + return elapsed_seconds.count(); + } + + // High precision clock counter register + using TTimePoint = size_t; + extern NGCORE_API double ticks_per_second; + + inline TTimePoint GetTimeCounter() noexcept + { + return TTimePoint(__rdtsc()); + } + + template + inline std::string ToString (const T& t) + { + std::stringstream ss; + ss << t; + return ss.str(); + } + + +} // namespace ngcore + +#endif // NETGEN_CORE_UTILS_HPP diff --git a/libsrc/csg/CMakeLists.txt b/libsrc/csg/CMakeLists.txt index 224a48e4..d05d62c1 100644 --- a/libsrc/csg/CMakeLists.txt +++ b/libsrc/csg/CMakeLists.txt @@ -12,11 +12,11 @@ if(APPLE) endif(APPLE) if(NOT WIN32) - target_link_libraries(csg mesh ${PYTHON_LIBRARIES}) - target_link_libraries(csg ${PYTHON_LIBRARIES}) + target_link_libraries(csg PUBLIC mesh ${PYTHON_LIBRARIES}) install( TARGETS csg ${NG_INSTALL_DIR}) endif(NOT WIN32) +target_link_libraries(csg PUBLIC ngcore) if(USE_GUI) add_library(csgvis ${NG_LIB_TYPE} vscsg.cpp ) diff --git a/libsrc/csg/csgeom.cpp b/libsrc/csg/csgeom.cpp index 87cae1b4..4aebe271 100644 --- a/libsrc/csg/csgeom.cpp +++ b/libsrc/csg/csgeom.cpp @@ -562,7 +562,7 @@ namespace netgen const Surface * CSGeometry :: GetSurface (const char * name) const { if (surfaces.Used(name)) - return surfaces.Get(name); + return surfaces[name]; else return NULL; } @@ -585,7 +585,7 @@ namespace netgen Solid * oldsol = NULL; if (solids.Used (name)) - oldsol = solids.Get(name); + oldsol = solids[name]; solids.Set (name, sol); sol->SetName (name); @@ -605,7 +605,7 @@ namespace netgen const Solid * CSGeometry :: GetSolid (const char * name) const { if (solids.Used(name)) - return solids.Get(name); + return solids[name]; else return NULL; } @@ -616,8 +616,8 @@ namespace netgen const Solid * CSGeometry :: GetSolid (const string & name) const { - if (solids.Used(name.c_str())) - return solids.Get(name.c_str()); + if (solids.Used(name)) + return solids[name]; else return NULL; } @@ -637,15 +637,15 @@ namespace netgen const SplineGeometry<2> * CSGeometry :: GetSplineCurve2d (const string & name) const { - if (splinecurves2d.Used(name.c_str())) - return splinecurves2d.Get(name.c_str()); + if (splinecurves2d.Used(name)) + return splinecurves2d[name]; else return NULL; } const SplineGeometry<3> * CSGeometry :: GetSplineCurve3d (const string & name) const { - if (splinecurves3d.Used(name.c_str())) - return splinecurves3d.Get(name.c_str()); + if (splinecurves3d.Used(name)) + return splinecurves3d[name]; else return NULL; } @@ -721,7 +721,7 @@ namespace netgen void CSGeometry :: SetFlags (const char * solidname, const Flags & flags) { - Solid * solid = solids.Elem(solidname); + Solid * solid = solids[solidname]; Array surfind; int i; diff --git a/libsrc/csg/csgeom.hpp b/libsrc/csg/csgeom.hpp index f82407b4..40d90d51 100644 --- a/libsrc/csg/csgeom.hpp +++ b/libsrc/csg/csgeom.hpp @@ -102,7 +102,7 @@ namespace netgen { private: /// all surfaces - SYMBOLTABLE surfaces; + SymbolTable surfaces; public: /// primitive of surface @@ -112,12 +112,12 @@ namespace netgen Array delete_them; /// all named solids - SYMBOLTABLE solids; + SymbolTable solids; /// all 2d splinecurves - SYMBOLTABLE< SplineGeometry<2>* > splinecurves2d; + SymbolTable< SplineGeometry<2>* > splinecurves2d; /// all 3d splinecurves - SYMBOLTABLE< SplineGeometry<3>* > splinecurves3d; + SymbolTable< SplineGeometry<3>* > splinecurves3d; /// all top level objects: solids and surfaces Array toplevelobjects; @@ -203,7 +203,7 @@ namespace netgen const Solid * GetSolid (const string & name) const; int GetNSolids () const { return solids.Size(); } const Solid * GetSolid (int i) const { return solids[i]; } - const SYMBOLTABLE & GetSolids () const { return solids; } + const SymbolTable & GetSolids () const { return solids; } void SetSplineCurve (const char * name, SplineGeometry<2> * spl); diff --git a/libsrc/csg/solid.cpp b/libsrc/csg/solid.cpp index a7e5fff5..c61f4e39 100644 --- a/libsrc/csg/solid.cpp +++ b/libsrc/csg/solid.cpp @@ -419,9 +419,9 @@ namespace netgen - static Solid * CreateSolidExpr (istream & ist, const SYMBOLTABLE & solids); - static Solid * CreateSolidTerm (istream & ist, const SYMBOLTABLE & solids); - static Solid * CreateSolidPrim (istream & ist, const SYMBOLTABLE & solids); + static Solid * CreateSolidExpr (istream & ist, const SymbolTable & solids); + static Solid * CreateSolidTerm (istream & ist, const SymbolTable & solids); + static Solid * CreateSolidPrim (istream & ist, const SymbolTable & solids); static void ReadString (istream & ist, char * str) { @@ -461,7 +461,7 @@ namespace netgen } - Solid * CreateSolidExpr (istream & ist, const SYMBOLTABLE & solids) + Solid * CreateSolidExpr (istream & ist, const SymbolTable & solids) { // cout << "create expr" << endl; @@ -484,7 +484,7 @@ namespace netgen return s1; } - Solid * CreateSolidTerm (istream & ist, const SYMBOLTABLE & solids) + Solid * CreateSolidTerm (istream & ist, const SymbolTable & solids) { // cout << "create term" << endl; @@ -508,7 +508,7 @@ namespace netgen return s1; } - Solid * CreateSolidPrim (istream & ist, const SYMBOLTABLE & solids) + Solid * CreateSolidPrim (istream & ist, const SymbolTable & solids) { Solid * s1; char ch; @@ -533,7 +533,7 @@ namespace netgen } (*testout) << "get terminal " << str << endl; - s1 = solids.Get(str); + s1 = solids[str]; if (s1) { // cout << "primitive: " << str << endl; @@ -545,7 +545,7 @@ namespace netgen } - Solid * Solid :: CreateSolid (istream & ist, const SYMBOLTABLE & solids) + Solid * Solid :: CreateSolid (istream & ist, const SymbolTable & solids) { Solid * nsol = CreateSolidExpr (ist, solids); nsol = new Solid (ROOT, nsol); diff --git a/libsrc/csg/solid.hpp b/libsrc/csg/solid.hpp index 4d620c04..193cb363 100644 --- a/libsrc/csg/solid.hpp +++ b/libsrc/csg/solid.hpp @@ -158,7 +158,7 @@ namespace netgen { return maxh; } void GetSolidData (ostream & ost, int first = 1) const; - static Solid * CreateSolid (istream & ist, const SYMBOLTABLE & solids); + static Solid * CreateSolid (istream & ist, const SymbolTable & solids); static BlockAllocator ball; diff --git a/libsrc/general/CMakeLists.txt b/libsrc/general/CMakeLists.txt index 00c59ea2..3f6c2aad 100644 --- a/libsrc/general/CMakeLists.txt +++ b/libsrc/general/CMakeLists.txt @@ -1,21 +1,19 @@ add_definitions(-DNGINTERFACE_EXPORTS) -add_library(gen OBJECT - array.cpp bitarray.cpp dynamicmem.cpp flags.cpp - hashtabl.cpp mystring.cpp ngexception.cpp optmem.cpp parthreads.cpp - profiler.cpp seti.cpp sort.cpp spbita2d.cpp symbolta.cpp table.cpp - mpi_interface.cpp gzstream.cpp +add_library(gen INTERFACE) +set(sdir ${CMAKE_CURRENT_SOURCE_DIR}) +target_sources(gen INTERFACE + ${sdir}/array.cpp ${sdir}/bitarray.cpp ${sdir}/dynamicmem.cpp ${sdir}/flags.cpp + ${sdir}/hashtabl.cpp ${sdir}/mystring.cpp ${sdir}/optmem.cpp ${sdir}/parthreads.cpp + ${sdir}/seti.cpp ${sdir}/sort.cpp ${sdir}/spbita2d.cpp ${sdir}/table.cpp + ${sdir}/mpi_interface.cpp ${sdir}/gzstream.cpp ) -set_target_properties( gen PROPERTIES POSITION_INDEPENDENT_CODE ON ) - -install( FILES ngexception.hpp DESTINATION ${NG_INSTALL_DIR_INCLUDE} COMPONENT netgen_devel ) - install(FILES array.hpp autodiff.hpp autoptr.hpp bitarray.hpp dynamicmem.hpp flags.hpp hashtabl.hpp mpi_interface.hpp myadt.hpp - ngsimd.hpp mystring.hpp netgenout.hpp ngexception.hpp ngpython.hpp - optmem.hpp parthreads.hpp profiler.hpp seti.hpp sort.hpp - spbita2d.hpp stack.hpp symbolta.hpp table.hpp template.hpp + ngsimd.hpp mystring.hpp netgenout.hpp ngpython.hpp + optmem.hpp parthreads.hpp seti.hpp sort.hpp + spbita2d.hpp stack.hpp table.hpp template.hpp gzstream.h DESTINATION ${NG_INSTALL_DIR_INCLUDE}/general COMPONENT netgen_devel ) diff --git a/libsrc/general/flags.cpp b/libsrc/general/flags.cpp index e5916f87..993413ed 100644 --- a/libsrc/general/flags.cpp +++ b/libsrc/general/flags.cpp @@ -83,7 +83,7 @@ namespace netgen Flags :: GetStringFlag (const char * name, const char * def) const { if (strflags.Used (name)) - return strflags.Get(name); + return strflags[name]; else return def; } @@ -91,7 +91,7 @@ namespace netgen double Flags :: GetNumFlag (const char * name, double def) const { if (numflags.Used (name)) - return numflags.Get(name); + return numflags[name]; else return def; } @@ -99,7 +99,7 @@ namespace netgen const double * Flags :: GetNumFlagPtr (const char * name) const { if (numflags.Used (name)) - return & ((SYMBOLTABLE&)numflags).Elem(name); + return & ((SymbolTable&)numflags)[name]; else return NULL; } @@ -107,7 +107,7 @@ namespace netgen double * Flags :: GetNumFlagPtr (const char * name) { if (numflags.Used (name)) - return & ((SYMBOLTABLE&)numflags).Elem(name); + return & ((SymbolTable&)numflags)[name]; else return NULL; } @@ -122,7 +122,7 @@ namespace netgen Flags :: GetStringListFlag (const char * name) const { if (strlistflags.Used (name)) - return *strlistflags.Get(name); + return *strlistflags[name]; else { static Array dummy_array(0); @@ -134,7 +134,7 @@ namespace netgen Flags ::GetNumListFlag (const char * name) const { if (numlistflags.Used (name)) - return *numlistflags.Get(name); + return *numlistflags[name]; else { static Array dummy_array(0); @@ -170,9 +170,9 @@ namespace netgen ofstream outfile (filename); for (i = 1; i <= strflags.Size(); i++) - outfile << strflags.GetName(i) << " = " << strflags.Get(i) << endl; + outfile << strflags.GetName(i) << " = " << strflags[i] << endl; for (i = 1; i <= numflags.Size(); i++) - outfile << numflags.GetName(i) << " = " << numflags.Get(i) << endl; + outfile << numflags.GetName(i) << " = " << numflags[i] << endl; for (i = 1; i <= defflags.Size(); i++) outfile << defflags.GetName(i) << endl; } @@ -184,9 +184,9 @@ namespace netgen int i; for (i = 1; i <= strflags.Size(); i++) - ost << strflags.GetName(i) << " = " << strflags.Get(i) << endl; + ost << strflags.GetName(i) << " = " << strflags[i] << endl; for (i = 1; i <= numflags.Size(); i++) - ost << numflags.GetName(i) << " = " << numflags.Get(i) << endl; + ost << numflags.GetName(i) << " = " << numflags[i] << endl; for (i = 1; i <= defflags.Size(); i++) ost << defflags.GetName(i) << endl; } diff --git a/libsrc/general/flags.hpp b/libsrc/general/flags.hpp index e156d328..e7ba0382 100644 --- a/libsrc/general/flags.hpp +++ b/libsrc/general/flags.hpp @@ -19,15 +19,15 @@ namespace netgen class Flags { /// - SYMBOLTABLE strflags; + SymbolTable strflags; /// - SYMBOLTABLE numflags; + SymbolTable numflags; /// - SYMBOLTABLE defflags; + SymbolTable defflags; /// - SYMBOLTABLE*> strlistflags; + SymbolTable*> strlistflags; /// - SYMBOLTABLE*> numlistflags; + SymbolTable*> numlistflags; public: /// DLL_HEADER Flags (); diff --git a/libsrc/general/mpi_interface.hpp b/libsrc/general/mpi_interface.hpp index c45494b6..91cb0af3 100644 --- a/libsrc/general/mpi_interface.hpp +++ b/libsrc/general/mpi_interface.hpp @@ -14,9 +14,8 @@ namespace netgen { - - extern DLL_HEADER int id, ntasks; - + using ngcore::id; + using ngcore::ntasks; #ifdef PARALLEL diff --git a/libsrc/general/myadt.hpp b/libsrc/general/myadt.hpp index 601a3da0..4284a42f 100644 --- a/libsrc/general/myadt.hpp +++ b/libsrc/general/myadt.hpp @@ -21,8 +21,8 @@ namespace netgen { using namespace ngcore; + using NgException = Exception; } -#include "ngexception.hpp" #include "parthreads.hpp" // #include "moveablemem.hpp" #include "dynamicmem.hpp" @@ -33,7 +33,6 @@ namespace netgen #include "hashtabl.hpp" -#include "symbolta.hpp" #include "bitarray.hpp" #include "flags.hpp" #include "spbita2d.hpp" @@ -44,7 +43,6 @@ namespace netgen #include "sort.hpp" #include "stack.hpp" #include "mystring.hpp" -#include "profiler.hpp" #include "mpi_interface.hpp" #include "netgenout.hpp" diff --git a/libsrc/general/netgenout.hpp b/libsrc/general/netgenout.hpp index fafe495e..076a6df1 100644 --- a/libsrc/general/netgenout.hpp +++ b/libsrc/general/netgenout.hpp @@ -4,14 +4,11 @@ // #include // #include // #include +#include "mpi_interface.hpp" namespace netgen { -#ifdef PARALLEL -extern int id; -extern int ntasks; -#endif DLL_HEADER extern int printmessage_importance; DLL_HEADER extern int printdots; diff --git a/libsrc/general/ngexception.cpp b/libsrc/general/ngexception.cpp deleted file mode 100644 index 6bcd2cc9..00000000 --- a/libsrc/general/ngexception.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/**************************************************************************/ -/* File: ngexception.cpp */ -/* Author: Joachim Schoeberl */ -/* Date: 16. Jan. 02 */ -/**************************************************************************/ - -#include - -namespace netgen -{ - //using namespace netgen; - - - - NgException :: NgException (const string & s) - : m_what(s) - { - ; - } - - - NgException :: ~NgException () - { - ; - } - - /// append string to description - void NgException :: Append (const string & s) - { - m_what += s; - } - -} diff --git a/libsrc/general/ngexception.hpp b/libsrc/general/ngexception.hpp deleted file mode 100644 index 6e06498c..00000000 --- a/libsrc/general/ngexception.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef FILE_NGEXCEPTION -#define FILE_NGEXCEPTION - -/**************************************************************************/ -/* File: ngexception.hpp */ -/* Author: Joachim Schoeberl */ -/* Date: 16. Jan. 2002 */ -/**************************************************************************/ - -namespace netgen -{ - -/// Base class for all ng exceptions -class NgException : public std::exception -{ - /// verbal description of exception - string m_what; -public: - /// - DLL_HEADER NgException (const string & s); - /// - DLL_HEADER virtual ~NgException (); - - /// append string to description - DLL_HEADER void Append (const string & s); - // void Append (const char * s); - - /// verbal description of exception - const string & What() const { return m_what; } - virtual const char* what() const noexcept override { return m_what.c_str(); } -}; -} - -#endif diff --git a/libsrc/general/ngpython.hpp b/libsrc/general/ngpython.hpp index fa9862b1..0604cb00 100644 --- a/libsrc/general/ngpython.hpp +++ b/libsrc/general/ngpython.hpp @@ -66,16 +66,7 @@ namespace netgen return static_cast::pointer>(lambda); } - - template - inline std::string ToString (const T& t) - { - std::stringstream ss; - ss << t; - return ss.str(); - } - -} +} // namespace netgen #endif diff --git a/libsrc/general/profiler.cpp b/libsrc/general/profiler.cpp deleted file mode 100644 index e6a9685b..00000000 --- a/libsrc/general/profiler.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/**************************************************************************/ -/* File: profiler.cpp */ -/* Author: Joachim Schoeberl */ -/* Date: 19. Apr. 2002 */ -/**************************************************************************/ - - -#include - -namespace netgen -{ - //using namespace netgen; - - long int NgProfiler::tottimes[SIZE]; - long int NgProfiler::starttimes[SIZE]; - long int NgProfiler::counts[SIZE]; - string NgProfiler::names[SIZE]; - int NgProfiler::usedcounter[SIZE]; - - - NgProfiler :: NgProfiler() - { - for (int i = 0; i < SIZE; i++) - { - tottimes[i] = 0; - usedcounter[i] = 0; - } - - total_timer = CreateTimer ("total CPU time"); - StartTimer (total_timer); - } - - NgProfiler :: ~NgProfiler() - { -#ifndef PARALLEL - StopTimer (total_timer); -#endif - - //ofstream prof; - //prof.open("ng.prof"); - - // ofstream-constructor may be called after STL-stuff is destructed, - // which leads to an "order of destruction"-problem, - // thus we use the C-variant: - - if (getenv ("NGPROFILE")) - { - char filename[100]; -#ifdef PARALLEL - sprintf (filename, "netgen.prof.%d", id); -#else - sprintf (filename, "netgen.prof"); -#endif - - if (id == 0) printf ("write profile to file netgen.prof\n"); - FILE *prof = fopen(filename,"w"); - Print (prof); - fclose(prof); - } - } - - -// void NgProfiler :: Print (ostream & prof) -// { -// for (int i = 0; i < SIZE; i++) -// if (counts[i] != 0 || usedcounter[i] != 0) -// { -// prof.setf (ios::fixed, ios::floatfield); -// prof.setf (ios::showpoint); - -// prof // << "job " << setw(3) << i -// << "calls " << setw(8) << counts[i] -// << ", time " << setprecision(2) << setw(6) << double(tottimes[i]) / CLOCKS_PER_SEC << " sec"; - -// if (usedcounter[i]) -// prof << " " << names[i]; -// else -// prof << " " << i; - -// prof << endl; -// } -// } - - - void NgProfiler :: Print (FILE * prof) - { - for (int i = 0; i < SIZE; i++) - if (counts[i] != 0 || usedcounter[i] != 0) - { - //fprintf(prof,"job %3i calls %8i, time %6.2f sec",i,counts[i],double(tottimes[i]) / CLOCKS_PER_SEC); -#ifndef USE_TSC - fprintf(prof,"calls %8li, time %6.2f sec",counts[i],double(tottimes[i]) / CLOCKS_PER_SEC); -#else - fprintf(prof,"calls %8li, time %6.2f sec",counts[i],double(tottimes[i]) / 2.7e9); -#endif - if(usedcounter[i]) - fprintf(prof," %s",names[i].c_str()); - else - fprintf(prof," %i",i); - fprintf(prof,"\n"); - } - } - - int NgProfiler :: CreateTimer (const string & name) - { - for (int i = SIZE-1; i > 0; i--) - if(names[i] == name) - return i; - - for (int i = SIZE-1; i > 0; i--) - if (!usedcounter[i]) - { - usedcounter[i] = 1; - names[i] = name; - return i; - } - return -1; - } - - - void NgProfiler :: ClearTimers () - { - for (int i = 0; i < SIZE; i++) - { - tottimes[i] = 0; - counts[i] = 0; - } - } - - NgProfiler prof; -} diff --git a/libsrc/general/profiler.hpp b/libsrc/general/profiler.hpp deleted file mode 100644 index 98039114..00000000 --- a/libsrc/general/profiler.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef FILE_NG_PROFILER -#define FILE_NG_PROFILER - -/**************************************************************************/ -/* File: profiler.hpp */ -/* Author: Joachim Schoeberl */ -/* Date: 5. Jan. 2005 */ -/**************************************************************************/ - - - -#ifdef VTRACE -#include "vt_user.h" -#else - #define VT_USER_START(n) - #define VT_USER_END(n) - #define VT_TRACER(n) -#endif - - -// #define USE_TSC -#ifdef USE_TSC -#include // for __rdtsc() CPU time step counter -#endif - -namespace netgen -{ - -class NgProfiler -{ - enum { SIZE = 1000 }; - - static long int tottimes[SIZE]; - static long int starttimes[SIZE]; - static long int counts[SIZE]; - static string names[SIZE]; - static int usedcounter[SIZE]; - - int total_timer; -public: - NgProfiler(); - ~NgProfiler(); - static int CreateTimer (const string & name); -#ifndef USE_TSC - static void StartTimer (int nr) - { - starttimes[nr] = clock(); counts[nr]++; - // VT_USER_START (const_cast (names[nr].c_str())); - VT_USER_START ( (char * const) (names[nr].c_str())); - } - static void StopTimer (int nr) - { - tottimes[nr] += clock()-starttimes[nr]; - VT_USER_END (const_cast (names[nr].c_str())); - } -#else - static void StartTimer (int nr) - { - starttimes[nr] = __rdtsc(); counts[nr]++; - // VT_USER_START (const_cast (names[nr].c_str())); - // VT_USER_START ( (char * const) (names[nr].c_str())); - } - static void StopTimer (int nr) - { - tottimes[nr] += __rdtsc()-starttimes[nr]; - VT_USER_END (const_cast (names[nr].c_str())); - } -#endif - - - //static void Print (ostream & ost); - static void Print (FILE * prof); - - static void ClearTimers (); - - class RegionTimer - { - int nr; - public: - RegionTimer (int anr) : nr(anr) - { StartTimer (nr); } - ~RegionTimer () { StopTimer (nr); } - }; -}; - -} - -#endif diff --git a/libsrc/general/symbolta.cpp b/libsrc/general/symbolta.cpp deleted file mode 100644 index bd35ac7c..00000000 --- a/libsrc/general/symbolta.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/**************************************************************************/ -/* File: symbolta.cc */ -/* Author: Joachim Schoeberl */ -/* Date: 01. Jun. 95 */ -/**************************************************************************/ - -/* - Abstract data type Symbol Table -*/ - -#include -#include - - -#ifndef FILE_SYMBOLTABLECC -#define FILE_SYMBOLTABLECC -// necessary for SGI ???? - - -namespace netgen -{ - //using namespace netgen; - - BASE_SYMBOLTABLE :: BASE_SYMBOLTABLE () - { - ; - } - - - BASE_SYMBOLTABLE :: ~BASE_SYMBOLTABLE() - { - DelNames(); - } - - - void BASE_SYMBOLTABLE :: DelNames() - { - for (int i = 0; i < names.Size(); i++) - delete [] names[i]; - names.SetSize (0); - } - - int BASE_SYMBOLTABLE :: Index (const char * name) const - { - if (!name) return 0; - for (int i = 0; i < names.Size(); i++) - if (strcmp (names[i], name) == 0) return i+1; - return 0; - } -} - -#endif diff --git a/libsrc/general/symbolta.hpp b/libsrc/general/symbolta.hpp deleted file mode 100644 index b599ea42..00000000 --- a/libsrc/general/symbolta.hpp +++ /dev/null @@ -1,163 +0,0 @@ -#ifndef FILE_SYMBOLTA -#define FILE_SYMBOLTA - - -/**************************************************************************/ -/* File: symbolta.hh */ -/* Author: Joachim Schoeberl */ -/* Date: 01. Jun. 95 */ -/**************************************************************************/ - -namespace netgen -{ - -/** - Base class for the generic SYMBOLTABLE. - An array of identifiers is maintained. -*/ -class DLL_HEADER BASE_SYMBOLTABLE -{ -protected: - /// identifiers - Array names; - -public: - /// Constructor - BASE_SYMBOLTABLE (); - /// - ~BASE_SYMBOLTABLE (); - /// - void DelNames (); - /// Index of symbol name, returns 0 if not used. - int Index (const char * name) const; -}; - - -/** - Abstract data type Symbol Table. - - To a string an value of the generic type T is associated. - The string is not copied into the symbol table class! -*/ -template -class SYMBOLTABLE : public BASE_SYMBOLTABLE -{ -private: - /// Associated data - Array data; - -public: - /// Creates a symboltable - inline SYMBOLTABLE (); - /// Returns size of symboltable - inline INDEX Size() const; - /// Returns reference to element, error if not used - inline T & Elem (const char * name); - /// Returns reference to i-th element - inline T & Elem (int i) - { return data.Elem(i); } - /// Returns element, error if not used - inline const T & Get (const char * name) const; - /// Returns i-th element - inline const T & Get (int i) const; - /// Returns name of i-th element - inline const char* GetName (int i) const; - /// Associates el to the string name, overrides if name is used - inline void Set (const char * name, const T & el); - /// Checks whether name is used - inline bool Used (const char * name) const; - /// Deletes symboltable - inline void DeleteAll (); - - void DoArchive(Archive& archive) { archive & names & data;} - - inline T & operator[] (int i) - { return data[i]; } - inline const T & operator[] (int i) const - { return data[i]; } - -private: - /// Prevents from copying symboltable by pointer assignment - SYMBOLTABLE & operator= (SYMBOLTABLE &); -}; - - - - -template -inline SYMBOLTABLE :: SYMBOLTABLE () -{ - ; -} - - -template -inline INDEX SYMBOLTABLE :: Size() const -{ - return data.Size(); -} - -template -inline T & SYMBOLTABLE :: Elem (const char * name) -{ - int i = Index (name); - if (i) - return data.Elem (i); - else - return data.Elem(1); -} - -template -inline const T & SYMBOLTABLE :: Get (const char * name) const -{ - int i; - i = Index (name); - if (i) - return data.Get(i); - else - return data.Get(1); -} - -template -inline const T & SYMBOLTABLE :: Get (int i) const -{ - return data.Get(i); -} - -template -inline const char* SYMBOLTABLE :: GetName (int i) const -{ - return names.Get(i); -} - -template -inline void SYMBOLTABLE :: Set (const char * name, const T & el) -{ - int i; - i = Index (name); - if (i) - data.Set(i, el); - else - { - data.Append (el); - char * hname = new char [strlen (name) + 1]; - strcpy (hname, name); - names.Append (hname); - } -} - -template -inline bool SYMBOLTABLE :: Used (const char * name) const -{ - return (Index(name)) ? true : false; -} - -template -inline void SYMBOLTABLE :: DeleteAll () -{ - DelNames(); - data.DeleteAll(); -} - -} -#endif diff --git a/libsrc/geom2d/CMakeLists.txt b/libsrc/geom2d/CMakeLists.txt index 754c79fb..26d0db92 100644 --- a/libsrc/geom2d/CMakeLists.txt +++ b/libsrc/geom2d/CMakeLists.txt @@ -9,6 +9,8 @@ if(NOT WIN32) install( TARGETS geom2d ${NG_INSTALL_DIR}) endif(NOT WIN32) +target_link_libraries(geom2d ngcore) + if(USE_GUI) add_library(geom2dvis ${NG_LIB_TYPE} vsgeom2d.cpp) if(NOT WIN32) diff --git a/libsrc/gprim/CMakeLists.txt b/libsrc/gprim/CMakeLists.txt index 3957476a..402973dc 100644 --- a/libsrc/gprim/CMakeLists.txt +++ b/libsrc/gprim/CMakeLists.txt @@ -1,11 +1,11 @@ add_definitions(-DNGINTERFACE_EXPORTS) -add_library(gprim OBJECT - adtree.cpp geom2d.cpp geom3d.cpp geomfuncs.cpp - geomtest3d.cpp transform3d.cpp spline.cpp splinegeometry.cpp +add_library(gprim INTERFACE) +set(sdir ${CMAKE_CURRENT_SOURCE_DIR}) +target_sources(gprim INTERFACE + ${sdir}/adtree.cpp ${sdir}/geom2d.cpp ${sdir}/geom3d.cpp ${sdir}/geomfuncs.cpp + ${sdir}/geomtest3d.cpp ${sdir}/transform3d.cpp ${sdir}/spline.cpp ${sdir}/splinegeometry.cpp ) -set_target_properties( gprim PROPERTIES POSITION_INDEPENDENT_CODE ON ) - install(FILES adtree.hpp geom2d.hpp geom3d.hpp geomfuncs.hpp geomobjects2.hpp geomobjects.hpp geomops2.hpp geomops.hpp geomtest3d.hpp gprim.hpp diff --git a/libsrc/linalg/CMakeLists.txt b/libsrc/linalg/CMakeLists.txt index a5a5a4f2..a8ab1b5a 100644 --- a/libsrc/linalg/CMakeLists.txt +++ b/libsrc/linalg/CMakeLists.txt @@ -1,8 +1,8 @@ -add_library( la OBJECT - densemat.cpp polynomial.cpp bfgs.cpp linopt.cpp linsearch.cpp - ) - -set_target_properties(la PROPERTIES POSITION_INDEPENDENT_CODE ON ) +add_library( la INTERFACE ) +set(sdir ${CMAKE_CURRENT_SOURCE_DIR}) +target_sources( la INTERFACE + ${sdir}/densemat.cpp ${sdir}/polynomial.cpp ${sdir}/bfgs.cpp ${sdir}/linopt.cpp ${sdir}/linsearch.cpp +) install(FILES densemat.hpp linalg.hpp opti.hpp diff --git a/libsrc/meshing/CMakeLists.txt b/libsrc/meshing/CMakeLists.txt index bc9f7f18..0c7433cc 100644 --- a/libsrc/meshing/CMakeLists.txt +++ b/libsrc/meshing/CMakeLists.txt @@ -1,12 +1,4 @@ add_definitions(-DNGINTERFACE_EXPORTS) -if(NOT WIN32) - set(mesh_object_libs - $ - $ - $ - ) -endif(NOT WIN32) - add_library(mesh ${NG_LIB_TYPE} adfront2.cpp adfront3.cpp bisect.cpp boundarylayer.cpp clusters.cpp curvedelems.cpp delaunay.cpp delaunay2d.cpp @@ -29,8 +21,10 @@ if(APPLE) set_target_properties( mesh PROPERTIES SUFFIX ".so") endif(APPLE) +target_link_libraries( mesh PUBLIC ngcore PRIVATE gprim la gen ) + if(NOT WIN32) - target_link_libraries( mesh ngcore ${ZLIB_LIBRARIES} ${MPI_CXX_LIBRARIES} ${PYTHON_LIBRARIES} ${METIS_LIBRARY}) + target_link_libraries( mesh PUBLIC ${ZLIB_LIBRARIES} ${MPI_CXX_LIBRARIES} ${PYTHON_LIBRARIES} ${METIS_LIBRARY}) install( TARGETS mesh ${NG_INSTALL_DIR}) endif(NOT WIN32) diff --git a/libsrc/meshing/global.cpp b/libsrc/meshing/global.cpp index df6bcece..e5036cde 100644 --- a/libsrc/meshing/global.cpp +++ b/libsrc/meshing/global.cpp @@ -50,10 +50,6 @@ namespace netgen string ngdir = "."; - // parallel netgen - int id = 0, ntasks = 1; - - void Ng_PrintDest(const char * s) { if (id == 0) diff --git a/libsrc/meshing/meshclass.cpp b/libsrc/meshing/meshclass.cpp index ebf75ff4..9e96a055 100644 --- a/libsrc/meshing/meshclass.cpp +++ b/libsrc/meshing/meshclass.cpp @@ -6016,7 +6016,7 @@ namespace netgen void Mesh :: SetUserData(const char * id, Array & data) { if(userdata_int.Used(id)) - delete userdata_int.Get(id); + delete userdata_int[id]; Array * newdata = new Array(data); @@ -6026,10 +6026,10 @@ namespace netgen { if(userdata_int.Used(id)) { - if(data.Size() < (*userdata_int.Get(id)).Size()+shift) - data.SetSize((*userdata_int.Get(id)).Size()+shift); - for(int i=0; i<(*userdata_int.Get(id)).Size(); i++) - data[i+shift] = (*userdata_int.Get(id))[i]; + if(data.Size() < (*userdata_int[id]).Size()+shift) + data.SetSize((*userdata_int[id]).Size()+shift); + for(int i=0; i<(*userdata_int[id]).Size(); i++) + data[i+shift] = (*userdata_int[id])[i]; return true; } else @@ -6041,7 +6041,7 @@ namespace netgen void Mesh :: SetUserData(const char * id, Array & data) { if(userdata_double.Used(id)) - delete userdata_double.Get(id); + delete userdata_double[id]; Array * newdata = new Array(data); @@ -6051,10 +6051,10 @@ namespace netgen { if(userdata_double.Used(id)) { - if(data.Size() < (*userdata_double.Get(id)).Size()+shift) - data.SetSize((*userdata_double.Get(id)).Size()+shift); - for(int i=0; i<(*userdata_double.Get(id)).Size(); i++) - data[i+shift] = (*userdata_double.Get(id))[i]; + if(data.Size() < (*userdata_double[id]).Size()+shift) + data.SetSize((*userdata_double[id]).Size()+shift); + for(int i=0; i<(*userdata_double[id]).Size(); i++) + data[i+shift] = (*userdata_double[id])[i]; return true; } else diff --git a/libsrc/meshing/meshclass.hpp b/libsrc/meshing/meshclass.hpp index fe72de2a..956946ce 100644 --- a/libsrc/meshing/meshclass.hpp +++ b/libsrc/meshing/meshclass.hpp @@ -132,8 +132,8 @@ namespace netgen /// mesh access semaphors. NgMutex majormutex; - SYMBOLTABLE< Array* > userdata_int; - SYMBOLTABLE< Array* > userdata_double; + SymbolTable< Array* > userdata_int; + SymbolTable< Array* > userdata_double; mutable Array< Point3d > pointcurves; diff --git a/libsrc/meshing/python_mesh.cpp b/libsrc/meshing/python_mesh.cpp index 6360174a..d349dfcf 100644 --- a/libsrc/meshing/python_mesh.cpp +++ b/libsrc/meshing/python_mesh.cpp @@ -506,7 +506,6 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) ) */ - .def("__str__", &ToString) .def_property_readonly("_timestamp", &Mesh::GetTimeStamp) .def("Load", FunctionPointer ([](Mesh & self, const string & filename) diff --git a/libsrc/occ/CMakeLists.txt b/libsrc/occ/CMakeLists.txt index 831a3875..1f093ad8 100644 --- a/libsrc/occ/CMakeLists.txt +++ b/libsrc/occ/CMakeLists.txt @@ -8,11 +8,13 @@ if(USE_GUI) add_library(occvis ${NG_LIB_TYPE} vsocc.cpp) endif(USE_GUI) +target_link_libraries(occ PUBLIC ngcore) + if(NOT WIN32) - target_link_libraries( occ ${OCC_LIBRARIES} ${PYTHON_LIBRARIES}) + target_link_libraries( occ PUBLIC ${OCC_LIBRARIES} ${PYTHON_LIBRARIES}) install( TARGETS occ ${NG_INSTALL_DIR}) if (USE_GUI) - target_link_libraries( occvis occ ) + target_link_libraries( occvis PUBLIC occ ) install( TARGETS occvis ${NG_INSTALL_DIR}) endif(USE_GUI) endif(NOT WIN32) diff --git a/libsrc/stlgeom/CMakeLists.txt b/libsrc/stlgeom/CMakeLists.txt index 3620c496..8925c828 100644 --- a/libsrc/stlgeom/CMakeLists.txt +++ b/libsrc/stlgeom/CMakeLists.txt @@ -4,11 +4,13 @@ add_library(stl ${NG_LIB_TYPE} ) if(NOT WIN32) - target_link_libraries( stl mesh ${PYTHON_LIBRARIES}) - target_link_libraries( stl ${PYTHON_LIBRARIES}) + target_link_libraries( stl mesh ${PYTHON_LIBRARIES}) install( TARGETS stl ${NG_INSTALL_DIR}) endif(NOT WIN32) +target_link_libraries( stl ngcore ) + + if(USE_GUI) add_library(stlvis ${NG_LIB_TYPE} vsstl.cpp diff --git a/libsrc/stlgeom/meshstlsurface.cpp b/libsrc/stlgeom/meshstlsurface.cpp index 4e267193..02753fb8 100644 --- a/libsrc/stlgeom/meshstlsurface.cpp +++ b/libsrc/stlgeom/meshstlsurface.cpp @@ -598,7 +598,6 @@ void STLSurfaceMeshing1 (STLGeometry & geom, for (int fnr = 1; fnr <= mesh.GetNFD(); fnr++) { - if (fnr == 100) NgProfiler::ClearTimers(); if (!opensegsperface[fnr]) continue; if (multithread.terminate) return; diff --git a/libsrc/visualization/CMakeLists.txt b/libsrc/visualization/CMakeLists.txt index 8f803d92..f1c82976 100644 --- a/libsrc/visualization/CMakeLists.txt +++ b/libsrc/visualization/CMakeLists.txt @@ -10,7 +10,7 @@ endif(USE_GUI) add_library(visual ${NG_LIB_TYPE} ${LIB_VISUAL_SOURCES}) if(NOT WIN32) - target_link_libraries( visual ${PYTHON_LIBRARIES} ${MPI_CXX_LIBRARIES} ${OPENGL_LIBRARIES} ) + target_link_libraries( visual ngcore ${PYTHON_LIBRARIES} ${MPI_CXX_LIBRARIES} ${OPENGL_LIBRARIES} ) install( TARGETS visual ${NG_INSTALL_DIR}) endif(NOT WIN32) diff --git a/ng/ngpkg.cpp b/ng/ngpkg.cpp index a80b0ea6..5b67637a 100644 --- a/ng/ngpkg.cpp +++ b/ng/ngpkg.cpp @@ -1840,9 +1840,9 @@ namespace netgen - SYMBOLTABLE & GetVisualizationScenes () + SymbolTable & GetVisualizationScenes () { - static SYMBOLTABLE vss; + static SymbolTable vss; return vss; } @@ -1860,7 +1860,7 @@ namespace netgen vs = &vscross; if (GetVisualizationScenes().Used(vismode)) { - vs = GetVisualizationScenes().Get(vismode); + vs = GetVisualizationScenes()[vismode]; } else if (vismode) { diff --git a/nglib/CMakeLists.txt b/nglib/CMakeLists.txt index ff8bd1d5..7253230a 100644 --- a/nglib/CMakeLists.txt +++ b/nglib/CMakeLists.txt @@ -8,9 +8,6 @@ if(WIN32) $ $ $ - $ - $ - $ $ $ @@ -32,6 +29,7 @@ if(NOT WIN32) endif(USE_GUI) endif(NOT WIN32) +# target_link_libraries(nglib PRIVATE gen la gprim PUBLIC ngcore) target_link_libraries(nglib PUBLIC ngcore) target_link_libraries( nglib PRIVATE ${OCC_LIBRARIES} ${MPI_CXX_LIBRARIES} ${OPENGL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${X11_Xmu_LIB} ${JPEG_LIBRARIES} ${MKL_LIBRARIES} ${ZLIB_LIBRARIES} ${OCC_LIBRARIES} ) diff --git a/tests/build_nospdlog.sh b/tests/build_nospdlog.sh new file mode 100644 index 00000000..2fedbef2 --- /dev/null +++ b/tests/build_nospdlog.sh @@ -0,0 +1,8 @@ +cd +mkdir -p build/netgen +cd build/netgen +cmake ../../src/netgen -DUSE_CCACHE=ON -DUSE_SPDLOG=OFF -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_C_COMPILER=clang +make -j12 +make install + diff --git a/tests/catch/CMakeLists.txt b/tests/catch/CMakeLists.txt index fc0ebe41..f0a25744 100644 --- a/tests/catch/CMakeLists.txt +++ b/tests/catch/CMakeLists.txt @@ -3,8 +3,7 @@ if(ENABLE_UNIT_TESTS) add_custom_target(unit_tests) # Build catch_main test object -message("netgen include dir = ${NETGEN_INCLUDE_DIR_ABSOLUTE} --------------------------------------") -include_directories(${CATCH_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../../libsrc/include}) +include_directories(${CATCH_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../../libsrc/include ${SPDLOG_INCLUDE_DIR}) add_library(catch_main STATIC main.cpp) set_target_properties(catch_main PROPERTIES CXX_STANDARD 17) add_dependencies(unit_tests catch_main) @@ -15,23 +14,19 @@ add_test(NAME unit_tests_built COMMAND ${CMAKE_COMMAND} --build . --target unit_ macro(add_unit_test name sources) add_executable(test_${name} ${sources} ) - if (WIN32) - target_link_libraries(test_${name} ngcore catch_main) - else(WIN32) - target_link_libraries(test_${name} ngcore catch_main) - endif(WIN32) + target_link_libraries(test_${name} ngcore catch_main) add_dependencies(unit_tests test_${name}) add_test(NAME unit_${name} COMMAND test_${name}) set_tests_properties(unit_${name} PROPERTIES DEPENDS unit_tests_built) + + if(ENABLE_CPP_CORE_GUIDELINES_CHECK) + set_target_properties(test_${name} PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}") + endif(ENABLE_CPP_CORE_GUIDELINES_CHECK) endmacro() add_unit_test(archive archive.cpp) +add_unit_test(symboltable symboltable.cpp) add_unit_test(version version.cpp) -if(ENABLE_CPP_CORE_GUIDELINES_CHECK) - set_target_properties(test_archive PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}") - set_target_properties(test_version PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}") -endif(ENABLE_CPP_CORE_GUIDELINES_CHECK) - endif(ENABLE_UNIT_TESTS) diff --git a/tests/catch/symboltable.cpp b/tests/catch/symboltable.cpp new file mode 100644 index 00000000..5e6ecd2c --- /dev/null +++ b/tests/catch/symboltable.cpp @@ -0,0 +1,64 @@ + +#include "catch.hpp" +#include <../core/ngcore.hpp> +using namespace ngcore; +using namespace std; + +TEST_CASE("Symboltable") +{ + SymbolTable table; + CHECK_THROWS_AS(table["test"], RangeException); + table.Set("first", 1); + CHECK(table["first"] == 1); + table["first"] = 2; + CHECK(table["first"] == 2); + auto index = table.Index("first"); + CHECK(index == 0); + CHECK(table[index] == 2); + table[index] = 3; + CHECK(table["first"] == 3); +#ifndef NDEBUG + int a; + CHECK_THROWS_AS(a = table[5], RangeException); + CHECK_THROWS_AS(table.GetName(5), RangeException); +#endif + CHECK(table.Used("first")); + CHECK(!table.Used("second")); + SymbolTable another; + another.Set("first", 1); + another.Set("second", 2); + table.Update(another); + CHECK(table["first"] == 1); + CHECK(table["second"] == 2); + std::stringstream s; + s << table; + CHECK(s.str() == "first : 1\nsecond : 2\n"); + auto ss = std::make_shared(); + BinaryOutArchive ao(ss); + ao & table; + ao.FlushBuffer(); + BinaryInArchive ai(ss); + SymbolTable read; + ai & read; + for(size_t i = 0; i is special because of vector is special... + SymbolTable btable; + btable.Set("true", true); + btable.Set("false", false); + CHECK(btable[0]); + CHECK(!btable[1]); + CHECK(btable["true"]); + CHECK(!btable["false"]); + ao & btable; + ao.FlushBuffer(); + SymbolTable bread; + ai & bread; + CHECK(bread["true"]); + CHECK(!bread["false"]); +}