Compare commits
3 Commits
fix-compil
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58bc640d6d | ||
|
|
5f00c5d304 | ||
|
|
f85e67e669 |
@@ -12,6 +12,11 @@ option(TRIGDX_BUILD_TESTS "Build tests" ON)
|
|||||||
option(TRIGDX_BUILD_BENCHMARKS "Build tests" ON)
|
option(TRIGDX_BUILD_BENCHMARKS "Build tests" ON)
|
||||||
option(TRIGDX_BUILD_PYTHON "Build Python interface" ON)
|
option(TRIGDX_BUILD_PYTHON "Build Python interface" ON)
|
||||||
|
|
||||||
|
# Add compiler flags
|
||||||
|
set(CMAKE_CXX_FLAGS
|
||||||
|
"${CMAKE_CXX_FLAGS} -Wall -Wnon-virtual-dtor -Wduplicated-branches -Wvla -Wpointer-arith -Wextra -Wno-unused-parameter"
|
||||||
|
)
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||||
configure_file(
|
configure_file(
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/trigdx_config.hpp.in
|
${CMAKE_CURRENT_SOURCE_DIR}/cmake/trigdx_config.hpp.in
|
||||||
|
|||||||
54
README.md
Normal file
54
README.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# TrigDx
|
||||||
|
|
||||||
|
High‑performance C++ library offering multiple implementations of transcendental trigonometric functions (e.g., sin, cos, tan and their variants), designed for numerical, signal‑processing, and real‑time systems where trading a small loss of accuracy for significantly higher throughput on modern CPUs (scalar and SIMD) and NVIDIA GPUs is acceptable.
|
||||||
|
|
||||||
|
## Why TrigDx?
|
||||||
|
Many applications use the standard library implementations, which prioritise correctness but are not always optimal for throughput on vectorized or GPU hardware. TrigDx gives you multiple implementations so you can:
|
||||||
|
|
||||||
|
- Replace `std::sin` / `std::cos` calls with faster approximations when a small, bounded reduction in accuracy is acceptable.
|
||||||
|
- Use SIMD/vectorized implementations and compact lookup tables for high throughput lookups.
|
||||||
|
- Run massively parallel kernels that take advantage of a GPU's _Special Function Units_ (SFUs).
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
- A C++ compiler with at least C++17 support (GCC, Clang)
|
||||||
|
- CMake 3.15+
|
||||||
|
- Optional: NVIDIA CUDA Toolkit 11+ to build GPU kernels
|
||||||
|
- Optional: GoogleTest (for unit tests) and GoogleBenchmark (for microbenchmarks)
|
||||||
|
|
||||||
|
## Building
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/astron-rd/TrigDx.git
|
||||||
|
cd TrigDx
|
||||||
|
mkdir build && cd build
|
||||||
|
|
||||||
|
# CPU-only:
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Release -DTRIGDX_USE_XSIMD=ON ..
|
||||||
|
cmake --build . -j
|
||||||
|
|
||||||
|
# Enable CUDA (if available):
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Release -DTRIGDX_USE_GPU=ON ..
|
||||||
|
cmake --build . -j
|
||||||
|
|
||||||
|
# Run tests:
|
||||||
|
ctest --output-on-failure -j
|
||||||
|
```
|
||||||
|
|
||||||
|
Common CMake options:
|
||||||
|
- `TRIGDX_USE_GPU=ON/OFF` — build GPU support.
|
||||||
|
- `TRIGDX_BUILD_TESTS=ON/OFF` — build tests.
|
||||||
|
- `TRIGDX_BUILD_BENCHMARKS=ON/OFF` — build benchmarks.
|
||||||
|
- `TRIGDX_BUILD_PYTHON` — build Python interface.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
- Fork → create a feature branch → open a PR.
|
||||||
|
- Include unit tests for correctness‑sensitive changes and benchmark results for performance changes.
|
||||||
|
- Follow project style (clang‑format) and run tests locally before submitting.
|
||||||
|
|
||||||
|
## Reporting issues
|
||||||
|
When opening an issue for incorrect results or performance regressions, please include:
|
||||||
|
- Platform and CPU/GPU model.
|
||||||
|
- Compiler and version with exact compile flags.
|
||||||
|
- Small reproducer (input data and the TrigDx implementation used).
|
||||||
|
|
||||||
|
## License
|
||||||
|
See the LICENSE file in the repository for licensing details.
|
||||||
@@ -2,6 +2,24 @@ include(FetchContent)
|
|||||||
include(FindAVX)
|
include(FindAVX)
|
||||||
add_library(trigdx reference.cpp lookup.cpp)
|
add_library(trigdx reference.cpp lookup.cpp)
|
||||||
|
|
||||||
|
if(HAVE_AVX2)
|
||||||
|
target_compile_definitions(trigdx PUBLIC HAVE_AVX2)
|
||||||
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "Intel" OR CMAKE_CXX_COMPILER_ID STREQUAL
|
||||||
|
"IntelLLVM")
|
||||||
|
target_compile_options(trigdx PUBLIC -xCORE-AVX2)
|
||||||
|
else()
|
||||||
|
target_compile_options(trigdx PUBLIC -mavx2)
|
||||||
|
endif()
|
||||||
|
elseif(HAVE_AVX)
|
||||||
|
target_compile_definitions(trigdx PUBLIC HAVE_AVX)
|
||||||
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "Intel" OR CMAKE_CXX_COMPILER_ID STREQUAL
|
||||||
|
"IntelLLVM")
|
||||||
|
target_compile_options(trigdx PUBLIC -xAVX)
|
||||||
|
else()
|
||||||
|
target_compile_options(trigdx PUBLIC -mavx)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
target_include_directories(trigdx PUBLIC ${PROJECT_SOURCE_DIR}/include)
|
target_include_directories(trigdx PUBLIC ${PROJECT_SOURCE_DIR}/include)
|
||||||
|
|
||||||
if(HAVE_AVX)
|
if(HAVE_AVX)
|
||||||
|
|||||||
@@ -6,6 +6,16 @@
|
|||||||
|
|
||||||
#include "trigdx/lookup_avx.hpp"
|
#include "trigdx/lookup_avx.hpp"
|
||||||
|
|
||||||
|
#if defined(HAVE_AVX) && !defined(__AVX__)
|
||||||
|
static_assert(HAVE_AVX == 0, "__AVX__ should be defined when HAVE_AVX is "
|
||||||
|
"defined");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_AVX2) && !defined(__AVX2__)
|
||||||
|
static_assert(HAVE_AVX2 == 0, "__AVX2__ should be defined when HAVE_AVX2 is "
|
||||||
|
"defined");
|
||||||
|
#endif
|
||||||
|
|
||||||
template <std::size_t NR_SAMPLES> struct LookupAVXBackend<NR_SAMPLES>::Impl {
|
template <std::size_t NR_SAMPLES> struct LookupAVXBackend<NR_SAMPLES>::Impl {
|
||||||
std::vector<float> lookup;
|
std::vector<float> lookup;
|
||||||
static constexpr std::size_t MASK = NR_SAMPLES - 1;
|
static constexpr std::size_t MASK = NR_SAMPLES - 1;
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ template <std::size_t NR_SAMPLES> struct lookup_table {
|
|||||||
cos_values[i] = cosf(i * PI_FRAC);
|
cos_values[i] = cosf(i * PI_FRAC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::array<float, NR_SAMPLES> cos_values;
|
|
||||||
std::array<float, NR_SAMPLES> sin_values;
|
std::array<float, NR_SAMPLES> sin_values;
|
||||||
|
std::array<float, NR_SAMPLES> cos_values;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <std::size_t NR_SAMPLES> struct cosf_dispatcher {
|
template <std::size_t NR_SAMPLES> struct cosf_dispatcher {
|
||||||
@@ -33,7 +33,6 @@ template <std::size_t NR_SAMPLES> struct cosf_dispatcher {
|
|||||||
|
|
||||||
constexpr uint_fast32_t VL = b_type::size;
|
constexpr uint_fast32_t VL = b_type::size;
|
||||||
const uint_fast32_t VS = n - n % VL;
|
const uint_fast32_t VS = n - n % VL;
|
||||||
const uint_fast32_t Q_PI = NR_SAMPLES / 4U;
|
|
||||||
const b_type scale = b_type::broadcast(lookup_table_.SCALE);
|
const b_type scale = b_type::broadcast(lookup_table_.SCALE);
|
||||||
const b_type pi_frac = b_type::broadcast(lookup_table_.PI_FRAC);
|
const b_type pi_frac = b_type::broadcast(lookup_table_.PI_FRAC);
|
||||||
const m_type mask = m_type::broadcast(lookup_table_.MASK);
|
const m_type mask = m_type::broadcast(lookup_table_.MASK);
|
||||||
@@ -42,7 +41,7 @@ template <std::size_t NR_SAMPLES> struct cosf_dispatcher {
|
|||||||
const b_type term2 = b_type::broadcast(lookup_table_.TERM2); // 1/2!
|
const b_type term2 = b_type::broadcast(lookup_table_.TERM2); // 1/2!
|
||||||
const b_type term3 = b_type::broadcast(lookup_table_.TERM3); // 1/3!
|
const b_type term3 = b_type::broadcast(lookup_table_.TERM3); // 1/3!
|
||||||
const b_type term4 = b_type::broadcast(lookup_table_.TERM4); // 1/4!
|
const b_type term4 = b_type::broadcast(lookup_table_.TERM4); // 1/4!
|
||||||
const m_type quarter_pi = m_type::broadcast(Q_PI);
|
|
||||||
uint_fast32_t i;
|
uint_fast32_t i;
|
||||||
for (i = 0; i < VS; i += VL) {
|
for (i = 0; i < VS; i += VL) {
|
||||||
const b_type vx = b_type::load(a + i, Tag());
|
const b_type vx = b_type::load(a + i, Tag());
|
||||||
@@ -60,7 +59,7 @@ template <std::size_t NR_SAMPLES> struct cosf_dispatcher {
|
|||||||
const b_type dx4 = xsimd::mul(dx2, dx);
|
const b_type dx4 = xsimd::mul(dx2, dx);
|
||||||
const b_type t2 = xsimd::mul(dx2, term2);
|
const b_type t2 = xsimd::mul(dx2, term2);
|
||||||
const b_type t3 = xsimd::mul(dx3, term3);
|
const b_type t3 = xsimd::mul(dx3, term3);
|
||||||
const b_type t4 = xsimd::mul(dx4, term3);
|
const b_type t4 = xsimd::mul(dx4, term4);
|
||||||
|
|
||||||
const b_type cosdx = xsimd::add(xsimd::sub(term1, t2), t4);
|
const b_type cosdx = xsimd::add(xsimd::sub(term1, t2), t4);
|
||||||
|
|
||||||
@@ -98,7 +97,6 @@ template <std::size_t NR_SAMPLES> struct sinf_dispatcher {
|
|||||||
|
|
||||||
constexpr uint_fast32_t VL = b_type::size;
|
constexpr uint_fast32_t VL = b_type::size;
|
||||||
const uint_fast32_t VS = n - n % VL;
|
const uint_fast32_t VS = n - n % VL;
|
||||||
const uint_fast32_t Q_PI = NR_SAMPLES / 4U;
|
|
||||||
const b_type scale = b_type::broadcast(lookup_table_.SCALE);
|
const b_type scale = b_type::broadcast(lookup_table_.SCALE);
|
||||||
const b_type pi_frac = b_type::broadcast(lookup_table_.PI_FRAC);
|
const b_type pi_frac = b_type::broadcast(lookup_table_.PI_FRAC);
|
||||||
const m_type mask = m_type::broadcast(lookup_table_.MASK);
|
const m_type mask = m_type::broadcast(lookup_table_.MASK);
|
||||||
@@ -107,7 +105,7 @@ template <std::size_t NR_SAMPLES> struct sinf_dispatcher {
|
|||||||
const b_type term2 = b_type::broadcast(lookup_table_.TERM2); // 1/2!
|
const b_type term2 = b_type::broadcast(lookup_table_.TERM2); // 1/2!
|
||||||
const b_type term3 = b_type::broadcast(lookup_table_.TERM3); // 1/3!
|
const b_type term3 = b_type::broadcast(lookup_table_.TERM3); // 1/3!
|
||||||
const b_type term4 = b_type::broadcast(lookup_table_.TERM4); // 1/4!
|
const b_type term4 = b_type::broadcast(lookup_table_.TERM4); // 1/4!
|
||||||
const m_type quarter_pi = m_type::broadcast(Q_PI);
|
|
||||||
uint_fast32_t i;
|
uint_fast32_t i;
|
||||||
for (i = 0; i < VS; i += VL) {
|
for (i = 0; i < VS; i += VL) {
|
||||||
const b_type vx = b_type::load(a + i, Tag());
|
const b_type vx = b_type::load(a + i, Tag());
|
||||||
@@ -160,7 +158,6 @@ template <std::size_t NR_SAMPLES> struct sin_cosf_dispatcher {
|
|||||||
|
|
||||||
constexpr uint_fast32_t VL = b_type::size;
|
constexpr uint_fast32_t VL = b_type::size;
|
||||||
const uint_fast32_t VS = n - n % VL;
|
const uint_fast32_t VS = n - n % VL;
|
||||||
const uint_fast32_t Q_PI = NR_SAMPLES / 4U;
|
|
||||||
const b_type scale = b_type::broadcast(lookup_table_.SCALE);
|
const b_type scale = b_type::broadcast(lookup_table_.SCALE);
|
||||||
const m_type mask = m_type::broadcast(lookup_table_.MASK);
|
const m_type mask = m_type::broadcast(lookup_table_.MASK);
|
||||||
const b_type pi_frac = b_type::broadcast(lookup_table_.PI_FRAC);
|
const b_type pi_frac = b_type::broadcast(lookup_table_.PI_FRAC);
|
||||||
@@ -170,7 +167,6 @@ template <std::size_t NR_SAMPLES> struct sin_cosf_dispatcher {
|
|||||||
const b_type term3 = b_type::broadcast(lookup_table_.TERM3); // 1/3!
|
const b_type term3 = b_type::broadcast(lookup_table_.TERM3); // 1/3!
|
||||||
const b_type term4 = b_type::broadcast(lookup_table_.TERM4); // 1/4!
|
const b_type term4 = b_type::broadcast(lookup_table_.TERM4); // 1/4!
|
||||||
|
|
||||||
const m_type quarter_pi = m_type::broadcast(Q_PI);
|
|
||||||
uint_fast32_t i;
|
uint_fast32_t i;
|
||||||
for (i = 0; i < VS; i += VL) {
|
for (i = 0; i < VS; i += VL) {
|
||||||
const b_type vx = b_type::load(a + i, Tag());
|
const b_type vx = b_type::load(a + i, Tag());
|
||||||
|
|||||||
Reference in New Issue
Block a user