Merge branch 'ngcore' into 'master'

Ngcore

See merge request jschoeberl/netgen!114
This commit is contained in:
Matthias Hochsteger 2019-01-14 12:32:17 +00:00
commit 18044b8897
56 changed files with 2303 additions and 716 deletions

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -1,4 +1,4 @@
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

View File

@ -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 $<$<CONFIG:DEBUG>: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)

View File

@ -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<std::map<std::string, detail::ClassArchiveInfo>> type_register; // NOLINT
const detail::ClassArchiveInfo& Archive :: GetArchiveRegister(const std::string& classname)

View File

@ -3,31 +3,32 @@
#include <complex> // for complex
#include <cstring> // for size_t, strlen
#include <fstream> // for operator<<, ifstream, ofstream, basic...
#include <fstream> // for ifstream, ofstream
#include <functional> // for function
#include <map> // for map, _Rb_tree_iterator
#include <memory> // for __shared_ptr_access, __shared_ptr_acc...
#include <stdexcept> // for runtime_error
#include <string> // for string, operator+
#include <map> // for map
#include <memory> // for shared_ptr
#include <string> // for string
#include <type_traits> // for declval, enable_if, false_type, is_co...
#include <typeinfo> // for type_info
#include <utility> // for move, swap, pair
#include <vector> // 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 <pybind11/pybind11.h>
#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<typename T, typename ...Rest>
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<typename T, typename= typename std::enable_if<std::is_constructible<T>::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<void*, int> shared_ptr2nr, ptr2nr;
std::map<void*, int> shared_ptr2nr{}, ptr2nr{};
// vectors for storing the unarchived (shared) pointers
std::vector<std::shared_ptr<void>> nr2shared_ptr;
std::vector<void*> nr2ptr;
std::vector<std::shared_ptr<void>> nr2shared_ptr{};
std::vector<void*> nr2ptr{};
protected:
bool shallow_to_python = false;
std::map<std::string, VersionInfo> version_map = GetLibraryVersions();
std::shared_ptr<spdlog::logger> 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<typename T>
Archive& Shallow(T& val)
{
static_assert(detail::is_any_pointer<T>, "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<T>(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<bool> 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<bool>& v)
{
size_t size;
if(Output())
size = v.size();
(*this) & size;
if(Input())
{
v.resize(size);
bool b;
for(size_t i=0; i<size; i++)
{
(*this) & b;
v[i] = b;
}
}
else
{
for(bool b : v)
(*this) & b;
}
return *this;
}
template<typename T1, typename T2>
Archive& operator& (std::map<T1, T2>& 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)
{
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 ")
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<void*>(ptr.get()) )
if(reg_ptr != static_cast<void*>(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<void> 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
{
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,9 +403,12 @@ namespace ngcore
other.get())));
}
else
{
NETGEN_DEBUG_LOG(logger, "Shared pointer didn't need pointer casts");
ptr = std::static_pointer_cast<T>(other);
}
}
}
return *this;
}
@ -360,30 +418,43 @@ 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)
{
NETGEN_DEBUG_LOG(logger, "Storing nullptr");
return (*this) << -2;
}
auto reg_ptr = static_cast<void*>(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 ")
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<void*>(p));
if(reg_ptr != static_cast<void*>(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<T>::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 ") +
throw Exception(std::string("Archive error: Class ") +
Demangle(typeid(*p).name()) + " does not provide a default constructor!");
else
{
@ -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 ")
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<void*>(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<T>();
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<void*>(dynamic_cast<B1*>(p))); }
catch(std::exception&)
catch(const Exception&)
{ return Caster<T, Brest...>::tryUpcast(ti, p); }
}
@ -520,7 +608,7 @@ namespace ngcore
return dynamic_cast<T*>(static_cast<B1*>(GetArchiveRegister(Demangle(typeid(B1).name())).
downcaster(ti, p)));
}
catch(std::exception&)
catch(const Exception&)
{
return Caster<T, Brest...>::tryDowncast(ti, p);
}
@ -536,7 +624,7 @@ namespace ngcore
{
static_assert(detail::all_of_tmpl<std::is_base_of<Bases,T>::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<T>()
: Archive::Caster<T, Bases...>::tryUpcast(ti, detail::constructIfPossible<T>()); };
@ -805,7 +893,7 @@ namespace ngcore
}
};
#ifdef NG_PYTHON
#ifdef NETGEN_PYTHON
template<typename ARCHIVE>
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<std::stringstream>()),
@ -821,8 +913,13 @@ namespace ngcore
{
ARCHIVE::shallow_to_python = true;
if(Input())
{
stream = std::make_shared<std::stringstream>
(pybind11::cast<pybind11::bytes>(lst[pybind11::len(lst)-1]));
*this & version_map;
stream = std::make_shared<std::stringstream>
(pybind11::cast<pybind11::bytes>(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<std::stringstream>(stream)->str()));
stream = std::make_shared<std::stringstream>();
*this & GetLibraryVersions();
lst.append(pybind11::bytes(std::static_pointer_cast<std::stringstream>(stream)->str()));
return lst;
}
};
template<typename T, typename T_ARCHIVE_OUT=BinaryOutArchive, typename T_ARCHIVE_IN=BinaryInArchive>
auto NGSPickle(bool printoutput=false)
auto NGSPickle()
{
return pybind11::pickle([printoutput](T* self)
return pybind11::pickle([](T* self)
{
PyArchive<T_ARCHIVE_OUT> 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<T_ARCHIVE_IN> ar(state[0]);
ar & val;
return val;
});
}
#endif // NG_PYTHON
#endif // NETGEN_PYTHON
} // namespace ngcore
#endif // NETGEN_CORE_ARCHIVE_HPP

90
libsrc/core/exception.hpp Normal file
View File

@ -0,0 +1,90 @@
#ifndef NETGEN_CORE_EXCEPTION_HPP
#define NETGEN_CORE_EXCEPTION_HPP
#include <sstream> // for stringstream
#include <stdexcept> // for exception
#include <string> // 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<typename T>
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

109
libsrc/core/logging.cpp Normal file
View File

@ -0,0 +1,109 @@
#include "logging.hpp"
#ifdef NETGEN_USE_SPDLOG
#include <spdlog/sinks/ansicolor_sink.h>
#include <spdlog/sinks/basic_file_sink.h>
#endif // NETGEN_USE_SPDLOG
namespace ngcore
{
#ifdef NETGEN_USE_SPDLOG
namespace detail
{
std::vector<std::shared_ptr<spdlog::sinks::sink>>& GetDefaultSinks()
{
static std::vector<std::shared_ptr<spdlog::sinks::sink>> sinks =
{ std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>() };
return sinks;
}
std::shared_ptr<spdlog::logger> CreateDefaultLogger(const std::string& name)
{
auto& default_sinks = GetDefaultSinks();
auto logger = std::make_shared<spdlog::logger>(name, default_sinks.begin(), default_sinks.end());
spdlog::details::registry::instance().register_and_init(logger);
return logger;
}
} // namespace detail
std::shared_ptr<spdlog::logger> 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<spdlog::sinks::basic_file_sink_mt>(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<spdlog::sinks::ansicolor_stdout_sink_mt>();
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<spdlog::logger> GetLogger(const std::string& /*unused*/)
{
return std::make_shared<spdlog::logger>();
}
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

108
libsrc/core/logging.hpp Normal file
View File

@ -0,0 +1,108 @@
#ifndef NETGEN_CORE_LOGGING_HPP
#define NETGEN_CORE_LOGGING_HPP
#include <memory>
#include <string>
#include <vector>
#include "ngcore_api.hpp"
#ifdef NETGEN_USE_SPDLOG
#ifdef NETGEN_LOG_DEBUG
#define SPDLOG_DEBUG_ON
#endif // NETGEN_LOG_DEBUG
#include <spdlog/logger.h>
#include <spdlog/sinks/sink.h>
#include <spdlog/spdlog.h>
#define NETGEN_DEBUG_LOG(logger, ...) SPDLOG_DEBUG(logger, __VA_ARGS__)
#else // NETGEN_USE_SPDLOG
#include <iostream>
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<typename T>
void log_helper( T t) { std::clog << t; }
template<typename T, typename ... Args>
void log_helper( T t, Args ... args)
{
std::clog << t;
log_helper(args...);
std::clog << ", ";
}
template<typename ... Args>
void log( level::level_enum level, const char* fmt, Args ... args)
{
std::clog << level << ": " << fmt << "\t Arguments: ";
log_helper(args...);
std::clog << "\n";
}
template<typename ... Args>
void trace( const char* fmt, Args ... args) { log(level::level_enum::trace, fmt, args...); }
template<typename ... Args>
void debug( const char* fmt, Args ... args) { log(level::level_enum::debug, fmt, args...); }
template<typename ... Args>
void info( const char* fmt, Args ... args) { log(level::level_enum::info, fmt, args...); }
template<typename ... Args>
void warn( const char* fmt, Args ... args) { log(level::level_enum::warn, fmt, args...); }
template<typename ... Args>
void error( const char* fmt, Args ... args) { log(level::level_enum::err, fmt, args...); }
template<typename ... Args>
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<std::shared_ptr<spdlog::sinks::sink>>& GetDefaultSinks();
inline std::shared_ptr<spdlog::logger> CreateDefaultLogger(const std::string& name);
} //namespace detail
NGCORE_API std::shared_ptr<spdlog::logger> 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

View File

@ -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

View File

@ -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

668
libsrc/core/paje_trace.cpp Normal file
View File

@ -0,0 +1,668 @@
#include <algorithm>
#include <atomic>
#include <iostream>
#include <map>
#include <set>
#include <thread>
#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<size_t>(std::numeric_limits<int>::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<d)
r=1, g=6*x,b=0;
else if (x<2*d)
r=1.0-6*(x-d),g=1,b=0;
else if (x<3*d)
r=0, g=1,b=6*(x-2*d);
else if (x<4*d)
r=0, g=1-6*(x-3*d),b=1;
else if (x<5*d)
r=6*(x-4*d), g=0,b=1;
else
r=1, g=0,b=1-5*(x-d);
};
int alias_counter;
FILE * ctrace_stream;
TTimePoint start_time;
std::shared_ptr<spdlog::logger> logger = GetLogger("PajeTrace");
double ConvertTime(TTimePoint t) {
// return time in milliseconds as double
// return std::chrono::duration<double>(t-start_time).count()*1000.0;
// return std::chrono::duration<double>(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<PajeEvent> 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<std::string> shash;
size_t h = shash(name);
h ^= h>>32U;
h = static_cast<uint32_t>(h);
hue = h*1.0/std::numeric_limits<uint32_t>::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<int> container_nodes;
container_nodes.reserve(num_nodes);
for(int i=0; i<num_nodes; i++)
container_nodes.emplace_back( paje.CreateContainer( container_type_node, container_task_manager, "Node " + ToString(i)) );
std::vector <int> thread_aliases;
thread_aliases.reserve(nthreads);
if(trace_threads)
for (int i=0; i<nthreads; i++)
{
auto name = "Timer level " + ToString(i);
thread_aliases.emplace_back( paje.CreateContainer( container_type_thread, container_nodes[i*num_nodes/nthreads], name ) );
}
std::map<const std::type_info *, int> job_map;
std::map<const std::type_info *, int> 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<int> timer_ids;
std::map<int,int> 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<int> timer_container_aliases;
timer_container_aliases.resize(maxdepth);
for(int i=0; i<maxdepth; i++)
{
auto name = "Timer level " + ToString(i);
timer_container_aliases[i] = paje.CreateContainer( container_type_timer, container_task_manager, name );
}
timerdepth = 0;
for(auto & event : timer_events)
{
if(event.is_start)
paje.PushState( event.time, state_type_timer, timer_container_aliases[timerdepth++], timer_aliases[event.timer_id] );
else
paje.PopState( event.time, state_type_timer, timer_container_aliases[--timerdepth] );
}
for(auto & vtasks : tasks)
{
for (Task & t : vtasks) {
int value_id = t.id;
switch(t.id_type)
{
case Task::ID_JOB:
value_id = job_task_map[jobs[t.id-1].type];
if(trace_thread_counter)
{
paje.AddVariable( t.start_time, variable_type_active_threads, container_jobs, 1.0 );
paje.SubVariable( t.stop_time, variable_type_active_threads, container_jobs, 1.0 );
}
if(trace_threads)
{
paje.PushState( t.start_time, state_type_task, thread_aliases[t.thread_id], value_id, t.additional_value, true );
paje.PopState( t.stop_time, state_type_task, thread_aliases[t.thread_id] );
}
break;
case Task::ID_TIMER:
value_id = timer_aliases[t.id];
paje.PushState( t.start_time, state_type_timer, thread_aliases[t.thread_id], value_id, t.additional_value, true );
paje.PopState( t.stop_time, state_type_timer, thread_aliases[t.thread_id] );
break;
default:
paje.PushState( t.start_time, state_type_task, thread_aliases[t.thread_id], value_id, t.additional_value, false );
paje.PopState( t.stop_time, state_type_task, thread_aliases[t.thread_id] );
break;
}
}
}
// Merge link event
int nlinks = 0;
for( auto & l : links)
nlinks += l.size();
std::vector<ThreadLink> links_merged;
links_merged.reserve(nlinks);
std::vector<unsigned int> pos(nthreads);
int nlinks_merged = 0;
while(nlinks_merged < nlinks)
{
int minpos = -1;
TTimePoint mintime = -1;
for (int t = 0; t<nthreads; t++)
{
if(pos[t] < links[t].size() && (minpos==-1 || links[t][pos[t]].time < mintime))
{
minpos = t;
mintime = links[t][pos[t]].time;
}
}
links_merged.push_back( links[minpos][pos[minpos]] );
pos[minpos]++;
nlinks_merged++;
}
std::vector<ThreadLink> 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<started_links.size())
{
while(i<started_links.size() && started_links[i].key == l.key)
{
ThreadLink & sl = started_links[i];
// Avoid links on same thread
if(sl.thread_id != l.thread_id)
{
paje.StartLink( sl.time, link_type, container_nodes[sl.thread_id*num_nodes/nthreads], l.key, thread_aliases[sl.thread_id], l.key);
paje.EndLink( l.time, link_type, container_nodes[l.thread_id*num_nodes/nthreads], l.key, thread_aliases[l.thread_id], l.key);
}
started_links.erase(started_links.begin()+i);
}
i++;
}
}
}
paje.WriteEvents();
}
} // namespace ngcore
const char *header =
"%EventDef PajeDefineContainerType 0 \n"
"% Alias string \n"
"% Type string \n"
"% Name string \n"
"%EndEventDef \n"
"%EventDef PajeDefineVariableType 1 \n"
"% Alias string \n"
"% Type string \n"
"% Name string \n"
"% Color color \n"
"%EndEventDef \n"
"%EventDef PajeDefineStateType 2 \n"
"% Alias string \n"
"% Type string \n"
"% Name string \n"
"%EndEventDef \n"
"%EventDef PajeDefineEventType 3 \n"
"% Alias string \n"
"% Type string \n"
"% Name string \n"
"% Color color \n"
"%EndEventDef \n"
"%EventDef PajeDefineLinkType 4 \n"
"% Alias string \n"
"% Type string \n"
"% StartContainerType string \n"
"% EndContainerType string \n"
"% Name string \n"
"%EndEventDef \n"
"%EventDef PajeDefineEntityValue 5 \n"
"% Alias string \n"
"% Type string \n"
"% Name string \n"
"% Color color \n"
"%EndEventDef \n"
"%EventDef PajeCreateContainer 6 \n"
"% Time date \n"
"% Alias string \n"
"% Type string \n"
"% Container string \n"
"% Name string \n"
"%EndEventDef \n"
"%EventDef PajeDestroyContainer 7 \n"
"% Time date \n"
"% Type string \n"
"% Name string \n"
"%EndEventDef \n"
"%EventDef PajeSetVariable 8 \n"
"% Time date \n"
"% Type string \n"
"% Container string \n"
"% Value double \n"
"%EndEventDef\n"
"%EventDef PajeAddVariable 9 \n"
"% Time date \n"
"% Type string \n"
"% Container string \n"
"% Value double \n"
"%EndEventDef\n"
"%EventDef PajeSubVariable 10 \n"
"% Time date \n"
"% Type string \n"
"% Container string \n"
"% Value double \n"
"%EndEventDef\n"
"%EventDef PajeSetState 11 \n"
"% Time date \n"
"% Type string \n"
"% Container string \n"
"% Value string \n"
"%EndEventDef\n"
"%EventDef PajePushState 12 \n"
"% Time date \n"
"% Type string \n"
"% Container string \n"
"% Value string \n"
"% Id string \n"
"%EndEventDef\n"
"%EventDef PajePopState 13 \n"
"% Time date \n"
"% Type string \n"
"% Container string \n"
"%EndEventDef\n"
"%EventDef PajeResetState 14 \n"
"% Time date \n"
"% Type string \n"
"% Container string \n"
"%EndEventDef\n"
"%EventDef PajeStartLink 15 \n"
"% Time date \n"
"% Type string \n"
"% Container string \n"
"% Value string \n"
"% StartContainer string \n"
"% Key string \n"
"%EndEventDef\n"
"%EventDef PajeEndLink 16 \n"
"% Time date \n"
"% Type string \n"
"% Container string \n"
"% Value string \n"
"% EndContainer string \n"
"% Key string \n"
"%EndEventDef\n"
"%EventDef PajeNewEvent 17 \n"
"% Time date \n"
"% Type string \n"
"% Container string \n"
"% Value string \n"
"%EndEventDef\n";

190
libsrc/core/paje_trace.hpp Normal file
View File

@ -0,0 +1,190 @@
#ifndef NETGEN_CORE_PAJE_TRACE_HPP
#define NETGEN_CORE_PAJE_TRACE_HPP
#include <limits>
#include <vector>
#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<spdlog::logger> 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<std::vector<Task> > tasks;
std::vector<Job> jobs;
std::vector<TimerEvent> timer_events;
std::vector<std::vector<ThreadLink> > 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

117
libsrc/core/profiler.cpp Normal file
View File

@ -0,0 +1,117 @@
#include <mutex>
#include "profiler.hpp"
namespace ngcore
{
std::vector<NgProfiler::TimerVal> 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<spdlog::logger> 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<std::mutex> 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

304
libsrc/core/profiler.hpp Normal file
View File

@ -0,0 +1,304 @@
#ifndef NETGEN_CORE_PROFILER_HPP
#define NETGEN_CORE_PROFILER_HPP
#include <chrono>
#include <string>
#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<TimerVal> timers;
NGCORE_API static TTimePoint * thread_times;
NGCORE_API static TTimePoint * thread_flops;
NGCORE_API static std::shared_ptr<spdlog::logger> 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<int>(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<typename TFunc>
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<double>::max();
int iteration = 0;
while(WallTime()<tend || iteration++ < min_iterations)
{
double t = -WallTime();
f();
t += WallTime();
tres = std::min(tres, t);
}
return tres;
}
} // namespace ngcore
#endif // NETGEN_CORE_PROFILER_HPP

View File

@ -0,0 +1,30 @@
#include <pybind11/pybind11.h>
#include "logging.hpp"
namespace py = pybind11;
using namespace ngcore;
PYBIND11_MODULE(pyngcore, m) // NOLINT
{
py::enum_<spdlog::level::level_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.");
}

144
libsrc/core/symboltable.hpp Normal file
View File

@ -0,0 +1,144 @@
#ifndef NETGEN_CORE_SYMBOLTABLE_HPP
#define NETGEN_CORE_SYMBOLTABLE_HPP
#include <ostream>
#include <string>
#include <vector>
#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 T>
class SymbolTable
{
std::vector<std::string> names;
std::vector<T> data;
public:
using value_type = T;
using reference = typename std::vector<T>::reference;
using const_reference = typename std::vector<T>::const_reference;
/// Creates a symboltable
SymbolTable () = default;
SymbolTable (const SymbolTable<T> &) = default;
SymbolTable (SymbolTable<T> &&) noexcept = default;
~SymbolTable() = default;
SymbolTable& operator=(const SymbolTable<T>&) = default;
SymbolTable& operator=(SymbolTable<T>&&) = default;
template<typename T2=T>
auto DoArchive(Archive& ar) -> typename std::enable_if<is_archivable<T2>, 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<T>& Update(const SymbolTable<T>& tbl2)
{
for (size_t i = 0; i < tbl2.Size(); i++)
Set (tbl2.GetName(i), tbl2[i]);
return *this;
}
};
template <typename T>
std::ostream & operator<< (std::ostream & ost, const SymbolTable<T> & 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

42
libsrc/core/utils.cpp Normal file
View File

@ -0,0 +1,42 @@
#include "utils.hpp"
#include "logging.hpp"
#ifndef WIN32
#include <cxxabi.h>
#endif
#include <iostream>
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()<tend);
auto tick_end = GetTimeCounter();
tend = WallTime();
return (tick_end-tick_start)/(tend-tstart);
}();
const std::chrono::time_point<TClock> wall_time_start = TClock::now();
} // namespace ngcore

62
libsrc/core/utils.hpp Normal file
View File

@ -0,0 +1,62 @@
#ifndef NETGEN_CORE_UTILS_HPP
#define NETGEN_CORE_UTILS_HPP
#include <chrono>
#include <sstream>
#include <string>
#ifdef WIN32
#include <intrin.h> // for __rdtsc() CPU time step counter
#else
#include <x86intrin.h> // 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<TClock> wall_time_start;
// Time in seconds since program start
inline double WallTime () noexcept
{
std::chrono::time_point<TClock> now = TClock::now();
std::chrono::duration<double> 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 <class T>
inline std::string ToString (const T& t)
{
std::stringstream ss;
ss << t;
return ss.str();
}
} // namespace ngcore
#endif // NETGEN_CORE_UTILS_HPP

View File

@ -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 )

View File

@ -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<int> surfind;
int i;

View File

@ -102,7 +102,7 @@ namespace netgen
{
private:
/// all surfaces
SYMBOLTABLE<Surface*> surfaces;
SymbolTable<Surface*> surfaces;
public:
/// primitive of surface
@ -112,12 +112,12 @@ namespace netgen
Array<Surface*> delete_them;
/// all named solids
SYMBOLTABLE<Solid*> solids;
SymbolTable<Solid*> 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<TopLevelObject*> 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<Solid*> & GetSolids () const { return solids; }
const SymbolTable<Solid*> & GetSolids () const { return solids; }
void SetSplineCurve (const char * name, SplineGeometry<2> * spl);

View File

@ -419,9 +419,9 @@ namespace netgen
static Solid * CreateSolidExpr (istream & ist, const SYMBOLTABLE<Solid*> & solids);
static Solid * CreateSolidTerm (istream & ist, const SYMBOLTABLE<Solid*> & solids);
static Solid * CreateSolidPrim (istream & ist, const SYMBOLTABLE<Solid*> & solids);
static Solid * CreateSolidExpr (istream & ist, const SymbolTable<Solid*> & solids);
static Solid * CreateSolidTerm (istream & ist, const SymbolTable<Solid*> & solids);
static Solid * CreateSolidPrim (istream & ist, const SymbolTable<Solid*> & solids);
static void ReadString (istream & ist, char * str)
{
@ -461,7 +461,7 @@ namespace netgen
}
Solid * CreateSolidExpr (istream & ist, const SYMBOLTABLE<Solid*> & solids)
Solid * CreateSolidExpr (istream & ist, const SymbolTable<Solid*> & solids)
{
// cout << "create expr" << endl;
@ -484,7 +484,7 @@ namespace netgen
return s1;
}
Solid * CreateSolidTerm (istream & ist, const SYMBOLTABLE<Solid*> & solids)
Solid * CreateSolidTerm (istream & ist, const SymbolTable<Solid*> & solids)
{
// cout << "create term" << endl;
@ -508,7 +508,7 @@ namespace netgen
return s1;
}
Solid * CreateSolidPrim (istream & ist, const SYMBOLTABLE<Solid*> & solids)
Solid * CreateSolidPrim (istream & ist, const SymbolTable<Solid*> & 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<Solid*> & solids)
Solid * Solid :: CreateSolid (istream & ist, const SymbolTable<Solid*> & solids)
{
Solid * nsol = CreateSolidExpr (ist, solids);
nsol = new Solid (ROOT, nsol);

View File

@ -158,7 +158,7 @@ namespace netgen
{ return maxh; }
void GetSolidData (ostream & ost, int first = 1) const;
static Solid * CreateSolid (istream & ist, const SYMBOLTABLE<Solid*> & solids);
static Solid * CreateSolid (istream & ist, const SymbolTable<Solid*> & solids);
static BlockAllocator ball;

View File

@ -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
)

View File

@ -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<double>&)numflags).Elem(name);
return & ((SymbolTable<double>&)numflags)[name];
else
return NULL;
}
@ -107,7 +107,7 @@ namespace netgen
double * Flags :: GetNumFlagPtr (const char * name)
{
if (numflags.Used (name))
return & ((SYMBOLTABLE<double>&)numflags).Elem(name);
return & ((SymbolTable<double>&)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<char*> 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<double> 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;
}

View File

@ -19,15 +19,15 @@ namespace netgen
class Flags
{
///
SYMBOLTABLE<char *> strflags;
SymbolTable<char *> strflags;
///
SYMBOLTABLE<double> numflags;
SymbolTable<double> numflags;
///
SYMBOLTABLE<int> defflags;
SymbolTable<int> defflags;
///
SYMBOLTABLE<Array<char*>*> strlistflags;
SymbolTable<Array<char*>*> strlistflags;
///
SYMBOLTABLE<Array<double>*> numlistflags;
SymbolTable<Array<double>*> numlistflags;
public:
///
DLL_HEADER Flags ();

View File

@ -14,9 +14,8 @@
namespace netgen
{
extern DLL_HEADER int id, ntasks;
using ngcore::id;
using ngcore::ntasks;
#ifdef PARALLEL

View File

@ -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"

View File

@ -4,14 +4,11 @@
// #include <ostream>
// #include <mystdlib.h>
// #include <meshing.hpp>
#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;

View File

@ -1,33 +0,0 @@
/**************************************************************************/
/* File: ngexception.cpp */
/* Author: Joachim Schoeberl */
/* Date: 16. Jan. 02 */
/**************************************************************************/
#include <myadt.hpp>
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;
}
}

View File

@ -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

View File

@ -66,16 +66,7 @@ namespace netgen
return static_cast<typename function_traits<Function>::pointer>(lambda);
}
template <class T>
inline std::string ToString (const T& t)
{
std::stringstream ss;
ss << t;
return ss.str();
}
}
} // namespace netgen
#endif

View File

@ -1,131 +0,0 @@
/**************************************************************************/
/* File: profiler.cpp */
/* Author: Joachim Schoeberl */
/* Date: 19. Apr. 2002 */
/**************************************************************************/
#include <myadt.hpp>
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;
}

View File

@ -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 <x86intrin.h> // 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<char*> (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<char*> (names[nr].c_str()));
}
#else
static void StartTimer (int nr)
{
starttimes[nr] = __rdtsc(); counts[nr]++;
// VT_USER_START (const_cast<char*> (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<char*> (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

View File

@ -1,52 +0,0 @@
/**************************************************************************/
/* File: symbolta.cc */
/* Author: Joachim Schoeberl */
/* Date: 01. Jun. 95 */
/**************************************************************************/
/*
Abstract data type Symbol Table
*/
#include <mystdlib.h>
#include <myadt.hpp>
#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

View File

@ -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 <char*> 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 T>
class SYMBOLTABLE : public BASE_SYMBOLTABLE
{
private:
/// Associated data
Array <T> 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<T> & operator= (SYMBOLTABLE<T> &);
};
template <class T>
inline SYMBOLTABLE<T> :: SYMBOLTABLE ()
{
;
}
template <class T>
inline INDEX SYMBOLTABLE<T> :: Size() const
{
return data.Size();
}
template <class T>
inline T & SYMBOLTABLE<T> :: Elem (const char * name)
{
int i = Index (name);
if (i)
return data.Elem (i);
else
return data.Elem(1);
}
template <class T>
inline const T & SYMBOLTABLE<T> :: Get (const char * name) const
{
int i;
i = Index (name);
if (i)
return data.Get(i);
else
return data.Get(1);
}
template <class T>
inline const T & SYMBOLTABLE<T> :: Get (int i) const
{
return data.Get(i);
}
template <class T>
inline const char* SYMBOLTABLE<T> :: GetName (int i) const
{
return names.Get(i);
}
template <class T>
inline void SYMBOLTABLE<T> :: 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 <class T>
inline bool SYMBOLTABLE<T> :: Used (const char * name) const
{
return (Index(name)) ? true : false;
}
template <class T>
inline void SYMBOLTABLE<T> :: DeleteAll ()
{
DelNames();
data.DeleteAll();
}
}
#endif

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -1,12 +1,4 @@
add_definitions(-DNGINTERFACE_EXPORTS)
if(NOT WIN32)
set(mesh_object_libs
$<TARGET_OBJECTS:la>
$<TARGET_OBJECTS:gprim>
$<TARGET_OBJECTS:gen>
)
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)

View File

@ -50,10 +50,6 @@ namespace netgen
string ngdir = ".";
// parallel netgen
int id = 0, ntasks = 1;
void Ng_PrintDest(const char * s)
{
if (id == 0)

View File

@ -6016,7 +6016,7 @@ namespace netgen
void Mesh :: SetUserData(const char * id, Array<int> & data)
{
if(userdata_int.Used(id))
delete userdata_int.Get(id);
delete userdata_int[id];
Array<int> * newdata = new Array<int>(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<double> & data)
{
if(userdata_double.Used(id))
delete userdata_double.Get(id);
delete userdata_double[id];
Array<double> * newdata = new Array<double>(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

View File

@ -132,8 +132,8 @@ namespace netgen
/// mesh access semaphors.
NgMutex majormutex;
SYMBOLTABLE< Array<int>* > userdata_int;
SYMBOLTABLE< Array<double>* > userdata_double;
SymbolTable< Array<int>* > userdata_int;
SymbolTable< Array<double>* > userdata_double;
mutable Array< Point3d > pointcurves;

View File

@ -506,7 +506,6 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m)
)
*/
.def("__str__", &ToString<Mesh>)
.def_property_readonly("_timestamp", &Mesh::GetTimeStamp)
.def("Load", FunctionPointer
([](Mesh & self, const string & filename)

View File

@ -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)

View File

@ -5,10 +5,12 @@ add_library(stl ${NG_LIB_TYPE}
if(NOT WIN32)
target_link_libraries( stl mesh ${PYTHON_LIBRARIES})
target_link_libraries( stl ${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

View File

@ -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;

View File

@ -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)

View File

@ -1840,9 +1840,9 @@ namespace netgen
SYMBOLTABLE<VisualScene*> & GetVisualizationScenes ()
SymbolTable<VisualScene*> & GetVisualizationScenes ()
{
static SYMBOLTABLE<VisualScene*> vss;
static SymbolTable<VisualScene*> 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)
{

View File

@ -8,9 +8,6 @@ if(WIN32)
$<TARGET_OBJECTS:geom2d>
$<TARGET_OBJECTS:csg>
$<TARGET_OBJECTS:stl>
$<TARGET_OBJECTS:gen>
$<TARGET_OBJECTS:la>
$<TARGET_OBJECTS:gprim>
$<TARGET_OBJECTS:visual>
$<TARGET_OBJECTS:occ>
@ -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} )

8
tests/build_nospdlog.sh Normal file
View File

@ -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

View File

@ -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)
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)

View File

@ -0,0 +1,64 @@
#include "catch.hpp"
#include <../core/ngcore.hpp>
using namespace ngcore;
using namespace std;
TEST_CASE("Symboltable")
{
SymbolTable<int> 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<int> 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<std::stringstream>();
BinaryOutArchive ao(ss);
ao & table;
ao.FlushBuffer();
BinaryInArchive ai(ss);
SymbolTable<int> read;
ai & read;
for(size_t i = 0; i<table.Size(); i++)
{
CHECK(read[i] == table[i]);
CHECK(read.GetName(i) == table.GetName(i));
}
table.DeleteAll();
CHECK(table.Size() == 0);
// SymbolTable<bool> is special because of vector<bool> is special...
SymbolTable<bool> 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<bool> bread;
ai & bread;
CHECK(bread["true"]);
CHECK(!bread["false"]);
}