Optimized basic math functions
The header, Harlinn/Math/Math.h
, provides alternatives to many of the core mathematical function that is specified in
the C and C++ standards. All of the functions can be constexpr
evaluated, and several offer
runtime performance benefits as well.
Harlinn/Math/Math.h
is header only.
The functions are implemented in the Harlinn::Math
namespace.
Concepts
Functions
- Basic operations
- Exponential functions
- Power functions
- Trigonometric functions
- Hyperbolic functions
- Error and gamma functions
- Nearest integer floating point operations
- Floating point manipulation functions
- Classification and comparison
- Other functions
- Special Functions
Background
The functions were created to explore constexpr
evaluation of mathematical expressions with Visual C++.
Constexpr, introduced in C++11 and further enhanced in subsequent versions, allows developers
to perform computations at compile time. This means less runtime overhead, optimized code,
and a significant boost in execution speed.
Since constexpr
evaluation doesn’t allow undefined behavior, this can be used to improve the
safety of C++ code by performing constexpr
evaluation of code in unit tests.
The constexpr
evaluated code will not compile until all code with undefined behavior is
eliminated from the execution path of the constexpr
evaluated code.
While C++ 26 will enable constexpr
evaluation of many of the functions within <cmath>
,
Harlinn/Math/Math.h
makes this possible right now.
Much of the code is based on version 0.8.5
of the OpenLibm
mathematical C library used by the Julia programming language.
The functions return NaN and +/- infinity as expected, but does not always set the exception flags in the floating point environment. The reason for this is that the code that generated the floating point exceptions in OpenLibm prevented full compile time constant evaluation.
The API is template based, which may seem odd, but this helps to reduce the number of
unintended conversions between numeric types. Most functions designed to work with
floating point values do not accept integer values without an explicit cast to
float
or double
.
Unit Tests
Unit tests for constexpr
evaluation of the core functions from Harlinn/Math/Math.h
are provided in
ConstexprMathTests.cpp.
Harlinn.Common.Core.Math.Tests contains 678 test cases, striving to demonstrate the accuracy of the computations. Be aware that running the release build of the full test suite takes nearly an hour, as several of the tests seeks to determine the result for every possible input, or very large subsets of the possible inputs - comparing the results with those produced by the standard implementation.
Benchmarks
The performance of the functions is benchmarked using the Google benchmark library, and can be verified by building and executing BasicMathBenchmarks included in the Harlinn.Windows solution.
See Benchmarks for the benchmark results.
PBRTO a micro optimized raytracing app
Benchmarks for individual functions cannot always be relied upon to accurately determine how well they will perform perform in a real application. Benchmarks provides a good idea about how the functions will perform, but when the compiler, and linker, employs global optimizations across all the compilation units, there are often a few surprises. PBRTO was created as a tool to help uncovering these surprises.
PBRTO is a micro optimized version of PBRT-v4, under development
as an example of how the functionality in Harlinn/Math/Math.h
, Harlinn/Math/Simd.h
and Harlinn/Math/VectorMath.h
can be used to
optimize the performance of real, computationally intensive, apps. It’s now about 91
%
faster than the release build of the original PBRT. more…
Implementation quality
The quality of the implementation is, since it is based on OpenLibm, high. OpenLibm does many things very well, but sometimes the Visual C++ runtime, an intrinsic function, or another alternative implemented by the library, perform better. When this is the case, the library selects the implementation with the best runtime performance.
Since some functions are implemented differently for constexpr
evaluation, they will not always return
identical results when executed at runtime. The difference is small; and can, in most cases, be ignored,
as in these cases both implementations are based on approximations.