Commit af2d3b59 authored by Ben Huber's avatar Ben Huber

optimization tutorial

parent e6c2e40a
Pipeline #705 failed with stages
in 4 minutes and 45 seconds
# Nomenclature
Tensor (Network) methods were developed independently in several different fields. As such there is a large variety of different
names for the same concepts and at times even several concepts for the same name, depending on the context.
To avoid confusion we want to explain the terms as they are used throughout this library. Unfortunately we are not aware of any
introductory publication using our exact notation and nomenclature, but everybody familiar with the notation from Numerical Analysis
will be familiar with most definitions. We saw it necessary to expand these terms though to have precise names for every aspect.
Tensor, degree, dimension, mode, index, multi-index, position, span, slate, entry, (element - unused??), contraction, shuffling,
TensorNetwork, Node, external link
TTTensor, TTOperator, rounding, core, cannonical form, component
# Optimizations
If you are like us, you want to get the fastest possible version of your numerical code to run as many samples as possible and
solve the largest systems possible. To this end there are a number of possible optimizations already provided for you by the
`xerus` library. The following list expands on the most relevant of them in roughly the order of effectiveness.
## Disabling Runtime Checks
The library contains many runtime checks for out-of-bounds access, other invalid inputs (like illegal contractions), consistency
and even to check the correct behaviour of internal structures. Depending on the complexity your code and the time spent inside
`xerus` (and not one of the libraries it uses) you can expect a large performance gain by disabling these checks in the `config.mk`
file during compilation of xerus.
It is not advisable to do this while developing, as it will be much more difficult to detect errors in your calls to `xerus`
functions, but once you have established, that your code works as expected you might want to try replacing the `libxerus.so` object
used by your project with one compiled with the `-D XERUS_DISABLE_RUNTIME_CHECKS` flag.
## Compiling Xerus with High Optimizations
Per default the library already compiles with high optimization settings (corresponding basically to `-O3`) as there is rarely
any reason to use lower settings for numerical code. If you want to spend a significant amount of cpu hours in numerical code
using the `xerus` library though, it might be worthwile to go even further.
The most significant change in runtime speed gains due to compiler settings at this point will come from link-time optimizations
(for `c++`projects using `xerus`).
To make use of them you will need a sufficiently recent versions of the `g++` compiler and `ar` archiver. After compiling the
`libxerus.so` object with the `USE_LTO = TRUE` flag you can then enable `flto` in your own compilation process. The optimizations
that will be used then extending more than a single compilation unit and might thus use significant system resources during
compilation.
If link-time optimization is not an option (or not sufficient) it is also possible to replace the high optimizations flag in your
`config.mk` file with the `DANGEROUS_OPTIMIZATION = TRUE` flag. This will enable non-IEEE-conform optimizations that should
typically only change floating point results in the least significant bit but might lead to undefined behaviour in case a `NaN`
or overflow is encountered during runtime. (It is rumored that there is an even higher optimization setting available for `xerus`
for those who know how to find it and want to get even the last 1% of speedup...)
## Avoiding Indexed Expressions
The comfort of being able to write Einstein-notation-like equations in the source code of the form `A(i,k) = B(i,j)*C(j,k);`
comes with the price of a certain overhead during runtime. It is in the low single-digit percent range for typical applications
but can become significant when very small tensors are being used and the time for the actual contraction thus becomes negligible.
In such cases it can be useful to replace such equations (especially ones that are as simple as above) with the explicit statement
of contractions and reshuffels. For above equation that would simply be
~~~.cpp
contract(A, B, false, C, false, 1);
~~~
i.e. read as: contract two tensors and store the result in A, left hand side B, not transposed, right hand side C, not transposed, contract a single mode.
If it is necessary to reshuffle a tensor to be able to contract it in such a way, e.g. `A(i,j,k) = B(i,k,j)`, this can be done
with the `reshuffle` function.
~~~.cpp
reshuffle(A, B, {0,2,1});
~~~
It is our opinion that code written with these functions instead of indexed espressions are often much harder to understand
and the speedup is typically small... but just in case you really want to, you now have the option to use them.
......@@ -10,7 +10,7 @@ and to use typical piping syntax for the messages.
XERUS_LOG(als_warning, "The ALS encountered a mishap " << variable_1 << " <= " << variable_2);
~~~
The following warning levels are predefined: `fatal`, `critical`, `error`, `warning`, `info`, `debug`. The default config file `config.mk.default` defines the preprocessor
variable `XERUS_INFO` which causes all but the `debug` level to be printed. Per default any other log-level is printed. This can be turned off by including
variable `XERUS_LOG_INFO` which causes all but the `debug` level to be printed. Per default any other log-level is printed. This can be turned off by including
`XERUS_SET_LOGGING(level, xerus::err::NO_LOGGING)` inside a commonly included header.
The `fatal` loglevel is special in that it will not just print the message but also throw an exception including the message itself and a callstack in the `.what()` string.
......
......@@ -92,81 +92,6 @@ namespace xerus {
#define XERUS_STRINGIFY2( x) #x
#define XERUS_STRINGIFY(x) XERUS_STRINGIFY2(x)
/**
* @def XERUS_SET_LOGGING(lvl, value)
* @brief set the logging behaviour of severity level @a lvl to @a value (either NOT_LOGGING, LOGGING_ON_ERROR or LOGGING_FULL)
* @details this definition must not be repeated and must be defined in a global header that is included before any msg is logged with that lvl
*/
#define XERUS_SET_LOGGING(lvl, value) \
namespace xerus { namespace misc { namespace internal { \
template<> struct LogFlag<xerus::misc::internal::log_namehash(XERUS_STRINGIFY(lvl))>{ static const int flag = value; }; \
}}}
/**
* @def XERUS_SET_LOGGING_DEFAULT(value)
* @brief sets the logging behaviour of all levels that are not otherwise specified to @a value
*/
#define XERUS_SET_LOGGING_DEFAULT(value) \
namespace xerus { namespace misc { namespace internal { \
template<uint64_t lvl> struct LogFlag { static const int flag = value; }; \
}}} \
SET_DEFAULT_LOG_LEVELS
// Default log levels
#define SET_DEFAULT_LOG_LEVELS \
XERUS_SET_LOGGING(fatal, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(critical, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(error, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(warning, xerus::misc::internal::LOGGING_FULL)\
XERUS_SET_LOGGING(info, xerus::misc::internal::LOGGING_FULL)\
XERUS_SET_LOGGING(debug, xerus::misc::internal::LOGGING_ON_ERROR)
#ifdef XERUS_LOG_ERROR
#undef SET_DEFAULT_LOG_LEVELS
#define SET_DEFAULT_LOG_LEVELS \
XERUS_SET_LOGGING(fatal, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(critical, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(error, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(warning, xerus::misc::internal::LOGGING_ON_ERROR)\
XERUS_SET_LOGGING(info, xerus::misc::internal::LOGGING_ON_ERROR)\
XERUS_SET_LOGGING(debug, xerus::misc::internal::LOGGING_ON_ERROR)
#endif
#ifdef XERUS_LOG_WARNING
#undef SET_DEFAULT_LOG_LEVELS
#define SET_DEFAULT_LOG_LEVELS \
XERUS_SET_LOGGING(fatal, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(critical, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(error, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(warning, xerus::misc::internal::LOGGING_FULL)\
XERUS_SET_LOGGING(info, xerus::misc::internal::LOGGING_ON_ERROR)\
XERUS_SET_LOGGING(debug, xerus::misc::internal::LOGGING_ON_ERROR)
#endif
#ifdef XERUS_LOG_INFO
#undef SET_DEFAULT_LOG_LEVELS
#define SET_DEFAULT_LOG_LEVELS \
XERUS_SET_LOGGING(fatal, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(critical, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(error, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(warning, xerus::misc::internal::LOGGING_FULL)\
XERUS_SET_LOGGING(info, xerus::misc::internal::LOGGING_FULL)\
XERUS_SET_LOGGING(debug, xerus::misc::internal::LOGGING_ON_ERROR)
#endif
#ifdef XERUS_LOG_DEBUG
#undef SET_DEFAULT_LOG_LEVELS
#define SET_DEFAULT_LOG_LEVELS \
XERUS_SET_LOGGING(fatal, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(critical, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(error, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(warning, xerus::misc::internal::LOGGING_FULL)\
XERUS_SET_LOGGING(info, xerus::misc::internal::LOGGING_FULL)\
XERUS_SET_LOGGING(debug, xerus::misc::internal::LOGGING_FULL)
#endif
/**
* @def XERUS_COMPILE_TIME_EVAL(e)
......@@ -274,4 +199,73 @@ namespace xerus {
(::xerus::misc::internal::LogFlag<xerus::misc::internal::log_namehash(XERUS_STRINGIFY(lvl))>::flag != xerus::misc::internal::NOT_LOGGING)
XERUS_SET_LOGGING_DEFAULT(xerus::misc::internal::LOGGING_FULL)
namespace xerus { namespace misc { namespace internal {
template<uint64_t lvl> struct LogFlag { static const int flag = xerus::misc::internal::LOGGING_FULL; };
}}}
/**
* @def XERUS_SET_LOGGING(lvl, value)
* @brief set the logging behaviour of severity level @a lvl to @a value (either NOT_LOGGING, LOGGING_ON_ERROR or LOGGING_FULL)
* @details this definition must not be repeated and must be defined in a global header that is included before any msg is logged with that lvl
*/
#define XERUS_SET_LOGGING(lvl, value) \
namespace xerus { namespace misc { namespace internal { \
template<> struct LogFlag<xerus::misc::internal::log_namehash(XERUS_STRINGIFY(lvl))>{ static const int flag = value; }; \
}}}
// Default log levels
#define XERUS_SET_DEFAULT_LOG_LEVELS \
XERUS_SET_LOGGING(fatal, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(critical, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(error, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(warning, xerus::misc::internal::LOGGING_FULL)\
XERUS_SET_LOGGING(info, xerus::misc::internal::LOGGING_FULL)\
XERUS_SET_LOGGING(debug, xerus::misc::internal::LOGGING_ON_ERROR)
#ifdef XERUS_LOG_ERROR
#undef XERUS_SET_DEFAULT_LOG_LEVELS
#define XERUS_SET_DEFAULT_LOG_LEVELS \
XERUS_SET_LOGGING(fatal, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(critical, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(error, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(warning, xerus::misc::internal::LOGGING_ON_ERROR)\
XERUS_SET_LOGGING(info, xerus::misc::internal::LOGGING_ON_ERROR)\
XERUS_SET_LOGGING(debug, xerus::misc::internal::LOGGING_ON_ERROR)
#endif
#ifdef XERUS_LOG_WARNING
#undef XERUS_SET_DEFAULT_LOG_LEVELS
#define XERUS_SET_DEFAULT_LOG_LEVELS \
XERUS_SET_LOGGING(fatal, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(critical, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(error, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(warning, xerus::misc::internal::LOGGING_FULL)\
XERUS_SET_LOGGING(info, xerus::misc::internal::LOGGING_ON_ERROR)\
XERUS_SET_LOGGING(debug, xerus::misc::internal::LOGGING_ON_ERROR)
#endif
#ifdef XERUS_LOG_INFO
#undef XERUS_SET_DEFAULT_LOG_LEVELS
#define XERUS_SET_DEFAULT_LOG_LEVELS \
XERUS_SET_LOGGING(fatal, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(critical, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(error, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(warning, xerus::misc::internal::LOGGING_FULL)\
XERUS_SET_LOGGING(info, xerus::misc::internal::LOGGING_FULL)\
XERUS_SET_LOGGING(debug, xerus::misc::internal::LOGGING_ON_ERROR)
#endif
#ifdef XERUS_LOG_DEBUG
#undef XERUS_SET_DEFAULT_LOG_LEVELS
#define XERUS_SET_DEFAULT_LOG_LEVELS \
XERUS_SET_LOGGING(fatal, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(critical, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(error, xerus::misc::internal::LOGGING_EXCEPTION)\
XERUS_SET_LOGGING(warning, xerus::misc::internal::LOGGING_FULL)\
XERUS_SET_LOGGING(info, xerus::misc::internal::LOGGING_FULL)\
XERUS_SET_LOGGING(debug, xerus::misc::internal::LOGGING_FULL)
#endif
XERUS_SET_DEFAULT_LOG_LEVELS
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment