kfr

Fast, modern C++ DSP framework, FFT, Sample Rate Conversion, FIR/IIR/Biquad Filters (SSE, AVX, AVX-512, ARM NEON)
Log | Files | Refs | README

commit 663dc63501c39c60f5080963fd6dad594ab11030
parent 59b08652e4e0d554d1e067a8cc93280ac23a3d4d
Author: d.levin256@gmail.com <d.levin256@gmail.com>
Date:   Wed,  4 Dec 2019 19:29:02 +0000

Upgrade Windows CI to Clang 9, C API tests

Diffstat:
MCMakeLists.txt | 19++++++-------------
Mazure-pipelines.yml | 24++++++++++++------------
Mcapi/capi.cpp | 3+++
Minclude/kfr/capi.h | 5+++++
Mtests/CMakeLists.txt | 18++++++++++++++----
Atests/capi_test.cpp | 222+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 262 insertions(+), 29 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -165,19 +165,6 @@ if (X86) target_set_arch(detect_cpu PRIVATE generic) endif () -if (ENABLE_TESTS) - - if (MSVC) - else() - # disable exceptions and rtti - add_compile_options(-fno-exceptions -fno-rtti -fno-asynchronous-unwind-tables) - endif () - - add_subdirectory(examples) - add_subdirectory(tests) - add_subdirectory(tools) -endif () - if (ENABLE_DFT) add_library(kfr_dft ${KFR_DFT_SRC}) target_link_libraries(kfr_dft kfr use_arch) @@ -198,6 +185,12 @@ if (ENABLE_DFT) endif () endif() +if (ENABLE_TESTS) + add_subdirectory(examples) + add_subdirectory(tests) + add_subdirectory(tools) +endif () + add_library(kfr_io ${KFR_IO_SRC}) target_link_libraries(kfr_io kfr) diff --git a/azure-pipelines.yml b/azure-pipelines.yml @@ -25,7 +25,7 @@ jobs: mkdir "$(Agent.TempDirectory)/sde-bin" tar -C "$(Agent.TempDirectory)/sde-bin" -xjf "$(Agent.TempDirectory)/sde.tar.bz2" --strip 1 sudo ln -s $(Agent.TempDirectory)/sde-bin/sde64 /usr/bin/sde - ci/run.sh build-release -DARCH_TESTS=sse2,sse3,sse41,avx,avx2,avx512 -DCPU_ARCH=detect -DUSE_SDE=ON -DCMAKE_CXX_COMPILER=clang++-8 -DCMAKE_BUILD_TYPE=Release + ci/run.sh build-release -DENABLE_CAPI_BUILD=ON -DARCH_TESTS=sse2,sse3,sse41,avx,avx2,avx512 -DCPU_ARCH=detect -DUSE_SDE=ON -DCMAKE_CXX_COMPILER=clang++-8 -DCMAKE_BUILD_TYPE=Release - job: Linux_x86_64_GCC7 timeoutInMinutes: 120 @@ -133,7 +133,7 @@ jobs: set -e /bin/bash -c "sudo xcode-select -s /Applications/Xcode_11.2.app/Contents/Developer" brew install ninja - ci/run.sh build-release -DARCH_TESTS=sse2,sse3,sse41,avx -DCMAKE_BUILD_TYPE=Release + ci/run.sh build-release -DENABLE_CAPI_BUILD=ON -DARCH_TESTS=sse2,sse3,sse41,avx -DCMAKE_BUILD_TYPE=Release - job: iOS_ARM_Clang_Release timeoutInMinutes: 120 @@ -185,7 +185,7 @@ jobs: call "%ANDROID_HOME%\tools\bin\sdkmanager.bat" "ndk-bundle" < %TMP%\always_yes ci\run.cmd build-release -DCMAKE_TOOLCHAIN_FILE="%ANDROID_HOME%\ndk-bundle\build\cmake\android.toolchain.cmake" -DANDROID_ABI=arm64-v8a -DANDROID_ARM_NEON=TRUE -DSKIP_TESTS=ON -DCMAKE_BUILD_TYPE=Release -- job: Windows_MinGW_x86_Clang701_Release +- job: Windows_MinGW_x86_Clang9_Release timeoutInMinutes: 120 pool: vmImage: 'vs2017-win2016' @@ -194,13 +194,13 @@ jobs: choco uninstall mingw choco install msys2 choco install ninja - choco install llvm --version 7.0.1 + choco install llvm set PATH=C:\ProgramData\Chocolatey\bin;C:\Program Files\CMake\bin;C:\tools\msys64\usr\local\bin;C:\tools\msys64\usr\bin;C:\tools\msys64\mingw32\bin;C:\windows;C:\windows\system32;C:\Windows;C:\Windows\System32\WindowsPowerShell\v1.0 call C:\tools\msys64\msys2_shell.cmd -defterm -mingw32 -no-start -full-path -here -c "pacman -S --noconfirm mingw32/mingw-w64-i686-gcc" call C:\tools\msys64\msys2_shell.cmd -defterm -mingw32 -no-start -full-path -here -c "pacman -S --noconfirm mingw32/mingw-w64-i686-ninja" call C:\tools\msys64\msys2_shell.cmd -defterm -mingw32 -no-start -full-path -here -c "ci/run.sh build-release -DCMAKE_CXX_COMPILER='C:/Program Files/LLVM/bin/clang++.exe' -DCPU_ARCH=detect -DCMAKE_CXX_FLAGS=--target=i686-w64-windows-gnu -DCMAKE_BUILD_TYPE=Release" -- job: Windows_MinGW_x86_64_Clang701_Release +- job: Windows_MinGW_x86_64_Clang9_Release timeoutInMinutes: 120 pool: vmImage: 'vs2017-win2016' @@ -209,20 +209,20 @@ jobs: choco uninstall mingw choco install msys2 choco install ninja - choco install llvm --version 7.0.1 + choco install llvm set PATH=C:\ProgramData\Chocolatey\bin;C:\Program Files\CMake\bin;C:\tools\msys64\usr\local\bin;C:\tools\msys64\usr\bin;C:\tools\msys64\mingw64\bin;C:\windows;C:\windows\system32;C:\Windows;C:\Windows\System32\WindowsPowerShell\v1.0 call C:\tools\msys64\msys2_shell.cmd -defterm -mingw64 -no-start -full-path -here -c "pacman -S --noconfirm mingw64/mingw-w64-x86_64-gcc" call C:\tools\msys64\msys2_shell.cmd -defterm -mingw64 -no-start -full-path -here -c "pacman -S --noconfirm mingw64/mingw-w64-x86_64-ninja" call C:\tools\msys64\msys2_shell.cmd -defterm -mingw64 -no-start -full-path -here -c "ci/run.sh build-release -DCMAKE_CXX_COMPILER='C:/Program Files/LLVM/bin/clang++.exe' -DCPU_ARCH=detect -DCMAKE_CXX_FLAGS=--target=x86_64-w64-windows-gnu -DCMAKE_BUILD_TYPE=Release" -- job: Windows_MSVC_x86_64_AVX512_Clang701_Release +- job: Windows_MSVC_x86_64_AVX512_Clang9_Release timeoutInMinutes: 120 pool: vmImage: 'vs2017-win2016' steps: - script: | choco install ninja - choco install llvm --version 7.0.1 + choco install llvm curl -o "$(Agent.TempDirectory)/sde.zip" -L $(SDEURL) "C:\Program Files\7-Zip\7z.exe" x -oC:\sde "$(Agent.TempDirectory)/sde.zip" @@ -232,16 +232,16 @@ jobs: set PATH=%PATH:C:\Program Files\Git\mingw64\bin;=% set PATH=%PATH:C:\Strawberry\c\bin;=% set PATH=C:\sde;%PATH% - ci\run.cmd build-release -DARCH_TESTS=ON -DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang-cl.exe" -DUSE_SDE=ON -DCPU_ARCH=avx512 -DCMAKE_CXX_FLAGS=-m64 -DCMAKE_BUILD_TYPE=Release + ci\run.cmd build-release -DENABLE_CAPI_BUILD=ON -DARCH_TESTS=ON -DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang-cl.exe" -DUSE_SDE=ON -DCPU_ARCH=avx512 -DCMAKE_CXX_FLAGS=-m64 -DCMAKE_BUILD_TYPE=Release -- job: Windows_MSVC_x86_AVX512_Clang701_Release +- job: Windows_MSVC_x86_AVX512_Clang9_Release timeoutInMinutes: 120 pool: vmImage: 'vs2017-win2016' steps: - script: | choco install ninja - choco install llvm --version 7.0.1 + choco install llvm curl -o "$(Agent.TempDirectory)/sde.zip" -L $(SDEURL) "C:\Program Files\7-Zip\7z.exe" x -oC:\sde "$(Agent.TempDirectory)/sde.zip" @@ -291,7 +291,7 @@ jobs: set PATH=C:\sde;%PATH% ci\run.cmd build-release -DARCH_TESTS=OFF -DUSE_SDE=ON -DCPU_ARCH=avx512 -DENABLE_DFT=OFF -DCMAKE_BUILD_TYPE=Release -- job: Windows_MSVC2019_x86_64_Clang_Release +- job: Windows_MSVC2019_x86_64_Clang9_Release timeoutInMinutes: 120 pool: vmImage: 'windows-2019' diff --git a/capi/capi.cpp b/capi/capi.cpp @@ -335,6 +335,9 @@ extern "C" reinterpret_cast<filter<double>*>(plan)->apply(output, input, size); } + void kfr_filter_reset_f32(KFR_FILTER_F32* plan) { reinterpret_cast<filter<float>*>(plan)->reset(); } + void kfr_filter_reset_f64(KFR_FILTER_F64* plan) { reinterpret_cast<filter<double>*>(plan)->reset(); } + void kfr_filter_delete_plan_f32(KFR_FILTER_F32* plan) { delete reinterpret_cast<filter<f32>*>(plan); } void kfr_filter_delete_plan_f64(KFR_FILTER_F64* plan) { delete reinterpret_cast<filter<f64>*>(plan); } } diff --git a/include/kfr/capi.h b/include/kfr/capi.h @@ -89,9 +89,11 @@ extern "C" #if defined __STDC_IEC_559_COMPLEX__ && !defined KFR_NO_C_COMPLEX_TYPES typedef float _Complex kfr_c32; typedef double _Complex kfr_c64; +#define KFR_COMPLEX_SIZE_MULTIPLIER 1 #else typedef float kfr_c32; typedef double kfr_c64; +#define KFR_COMPLEX_SIZE_MULTIPLIER 2 #endif typedef size_t kfr_size_t; typedef int32_t kfr_int32_t; @@ -236,6 +238,9 @@ typedef double kfr_c64; size_t size); KFR_API_SPEC void kfr_filter_process_f64(KFR_FILTER_F64* plan, kfr_f64* output, const kfr_f64* input, size_t size); + + KFR_API_SPEC void kfr_filter_reset_f32(KFR_FILTER_F32* plan); + KFR_API_SPEC void kfr_filter_reset_f64(KFR_FILTER_F64* plan); KFR_API_SPEC void kfr_filter_delete_plan_f32(KFR_FILTER_F32* plan); KFR_API_SPEC void kfr_filter_delete_plan_f64(KFR_FILTER_F64* plan); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt @@ -52,6 +52,12 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_BINARY_DIR}/bin) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/tests/cmake/") +if (ENABLE_CAPI_BUILD) + add_executable(capi_test capi_test.cpp) + target_include_directories(capi_test PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../include) + target_link_libraries(capi_test PUBLIC kfr_capi) +endif () + if (ENABLE_ASMTEST) add_executable(asm_test asm_test.cpp) target_link_libraries(asm_test kfr) @@ -202,19 +208,23 @@ if (NOT SKIP_TESTS) if (X86) add_test(NAME multiarch - COMMAND ${EMULATOR} ${PROJECT_BINARY_DIR}/bin/multiarch) + COMMAND ${EMULATOR} multiarch) + + if (ENABLE_CAPI_BUILD) + add_test(NAME capi_test COMMAND ${EMULATOR} capi_test) + endif () endif () if (ARCH_TESTS) foreach(A IN LISTS ARCH_LIST) if (USE_SDE) - add_test(NAME all_tests_${A} COMMAND ${SDE} ${SDE_ARCH_${A}} -- ${PROJECT_BINARY_DIR}/bin/all_tests_${A}) + add_test(NAME all_tests_${A} COMMAND ${SDE} ${SDE_ARCH_${A}} -- all_tests_${A}) else () - add_test(NAME all_tests_${A} COMMAND ${EMULATOR} ${PROJECT_BINARY_DIR}/bin/all_tests_${A}) + add_test(NAME all_tests_${A} COMMAND ${EMULATOR} all_tests_${A}) endif () endforeach() else () add_test(NAME all_tests - COMMAND ${EMULATOR} ${PROJECT_BINARY_DIR}/bin/all_tests) + COMMAND ${EMULATOR} all_tests) endif () endif () diff --git a/tests/capi_test.cpp b/tests/capi_test.cpp @@ -0,0 +1,222 @@ +#define KFR_NO_C_COMPLEX_TYPES 1 + +#include <kfr/capi.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> + +static int failures = 0; + +#ifdef _MSC_VER +#define CHECK(condition, message, ...) \ + if (!(condition)) \ + { \ + ++failures; \ + fprintf(stderr, "[FAILED] " message "\n", __VA_ARGS__); \ + } +#else +#define CHECK(condition, message, ...) \ + if (!(condition)) \ + { \ + ++failures; \ + fprintf(stderr, "[FAILED] " message "\n", ##__VA_ARGS__); \ + } +#endif + +void test_memory() +{ + printf("[TEST] Memory allocation\n"); + uint8_t* d = (uint8_t*)(kfr_allocate(256)); + for (size_t i = 0; i < 256; i++) + d[i] = i; + CHECK(kfr_allocated_size(d) == 256, "kfr_allocated_size: wrong size: %zu", kfr_allocated_size(d)); + d = (uint8_t*)(kfr_reallocate(d, 512)); + CHECK(kfr_allocated_size(d) == 512, "kfr_allocated_size: wrong size: %zu", kfr_allocated_size(d)); + for (size_t i = 0; i < 256; i++) + { + CHECK(d[i] == i, "kfr_reallocate: data lost after reallocation\n"); + } + kfr_deallocate(d); + + void* page = kfr_allocate_aligned(4096, 4096); + CHECK(((uintptr_t)page & 0xFFF) == 0, "kfr_allocate_aligned: wrong alignment: 0x%zx", + ((uintptr_t)page & 0xFFF)); + + kfr_deallocate(page); +} + +#define DFT_SIZE 32 + +void test_dft_f32() +{ + printf("[TEST] DFT f32\n"); + const float eps = 0.00001f; + KFR_DFT_PLAN_F32* plan = kfr_dft_create_plan_f32(DFT_SIZE); + // kfr_dft_dump_f32(plan); + kfr_f32 buf[DFT_SIZE * 2]; + for (int i = 0; i < DFT_SIZE; i++) + { + buf[i * 2 + 0] = (float)(i) / DFT_SIZE; + buf[i * 2 + 1] = (float)(-i) / DFT_SIZE; + } + uint8_t* tmp = (uint8_t*)kfr_allocate(kfr_dft_get_temp_size_f32(plan)); + kfr_dft_execute_f32(plan, buf, buf, tmp); + kfr_dft_execute_inverse_f32(plan, buf, buf, tmp); + kfr_dft_delete_plan_f32(plan); + kfr_deallocate(tmp); + + for (int i = 0; i < DFT_SIZE; i++) + { + CHECK(fabsf(buf[i * 2 + 0] - (float)(i)) < eps, "DFT: wrong result at %d: re = %f", i, + buf[i * 2 + 0]); + CHECK(fabsf(buf[i * 2 + 1] - (float)(-i)) < eps, "DFT: wrong result at %d: im = %f", i, + buf[i * 2 + 1]); + } +} + +void test_dft_f64() +{ + printf("[TEST] DFT f64\n"); + const float eps = 0.00000001; + KFR_DFT_PLAN_F64* plan = kfr_dft_create_plan_f64(DFT_SIZE); + // kfr_dft_dump_f64(plan); + kfr_f64 buf[DFT_SIZE * 2]; + for (int i = 0; i < DFT_SIZE; i++) + { + buf[i * 2 + 0] = (double)(i) / DFT_SIZE; + buf[i * 2 + 1] = (double)(-i) / DFT_SIZE; + } + uint8_t* tmp = (uint8_t*)kfr_allocate(kfr_dft_get_temp_size_f64(plan)); + kfr_dft_execute_f64(plan, buf, buf, tmp); + kfr_dft_execute_inverse_f64(plan, buf, buf, tmp); + kfr_dft_delete_plan_f64(plan); + kfr_deallocate(tmp); + + for (int i = 0; i < DFT_SIZE; i++) + { + CHECK(fabs(buf[i * 2 + 0] - (double)(i)) < eps, "DFT: wrong result at %d: re = %f", i, + buf[i * 2 + 0]); + CHECK(fabs(buf[i * 2 + 1] - (double)(-i)) < eps, "DFT: wrong result at %d: im = %f", i, + buf[i * 2 + 1]); + } +} + +#define FILTER_SIZE 256 + +void test_fir_f32() +{ + printf("[TEST] FIR f32\n"); + kfr_f32 taps[] = { 1.f, 2.f, -2.f, -1.f }; + KFR_FILTER_F32* filter = kfr_filter_create_fir_plan_f32(taps, sizeof(taps) / sizeof(kfr_f32)); + + kfr_f32 buf[FILTER_SIZE]; + for (int i = 0; i < FILTER_SIZE; i++) + buf[i] = i; + + kfr_filter_process_f32(filter, buf, buf, FILTER_SIZE); + CHECK(buf[0] == 0, "FIR: wrong result at %d: %g", 0, buf[0]); + CHECK(buf[1] == 1, "FIR: wrong result at %d: %g", 1, buf[1]); + CHECK(buf[2] == 4, "FIR: wrong result at %d: %g", 2, buf[2]); + CHECK(buf[3] == 5, "FIR: wrong result at %d: %g", 3, buf[3]); + CHECK(buf[FILTER_SIZE - 1] == 5, "FIR: wrong result at %d: %g", FILTER_SIZE - 1, buf[FILTER_SIZE - 1]); + + kfr_filter_delete_plan_f32(filter); +} + +void test_fir_f64() +{ + printf("[TEST] FIR f64\n"); + kfr_f64 taps[] = { 1.f, 2.f, -2.f, -1.f }; + KFR_FILTER_F64* filter = kfr_filter_create_fir_plan_f64(taps, sizeof(taps) / sizeof(kfr_f64)); + + kfr_f64 buf[FILTER_SIZE]; + for (int i = 0; i < FILTER_SIZE; i++) + buf[i] = i; + + kfr_filter_process_f64(filter, buf, buf, FILTER_SIZE); + CHECK(buf[0] == 0, "FIR: wrong result at %d: %g", 0, buf[0]); + CHECK(buf[1] == 1, "FIR: wrong result at %d: %g", 1, buf[1]); + CHECK(buf[2] == 4, "FIR: wrong result at %d: %g", 2, buf[2]); + CHECK(buf[3] == 5, "FIR: wrong result at %d: %g", 3, buf[3]); + CHECK(buf[FILTER_SIZE - 1] == 5, "FIR: wrong result at %d: %g", FILTER_SIZE - 1, buf[FILTER_SIZE - 1]); + + kfr_filter_delete_plan_f64(filter); +} + +void test_iir_f32() +{ + const float eps = 0.00001f; + printf("[TEST] IIR f32\n"); + float sos[6] = { + 1., + -1.872871474946867, + 0.8809814578599688, + 0.002027495728275458, + 0.004054991456550916, + 0.002027495728275458, + }; + KFR_FILTER_F32* filter = kfr_filter_create_iir_plan_f32(sos, 1); + + kfr_f32 buf[FILTER_SIZE]; + kfr_f32 src[4] = { 0, 1, 0, -1 }; + for (int i = 0; i < FILTER_SIZE; i++) + buf[i] = src[i % 4]; + + kfr_filter_process_f32(filter, buf, buf, FILTER_SIZE); + + CHECK(fabsf(buf[0] - 0.f) < eps, "IIR: wrong result at %d: %f", 0, buf[0]); + CHECK(fabsf(buf[1] - 0.002027496f) < eps, "IIR: wrong result at %d: %f", 1, buf[1]); + CHECK(fabsf(buf[60] - -0.001285130f) < eps, "IIR: wrong result at %d: %f", 60, buf[60]); + + kfr_filter_delete_plan_f32(filter); +} + +void test_iir_f64() +{ + const double eps = 0.0000001; + printf("[TEST] IIR f64\n"); + double sos[6] = { + 1., + -1.872871474946867, + 0.8809814578599688, + 0.002027495728275458, + 0.004054991456550916, + 0.002027495728275458, + }; + KFR_FILTER_F64* filter = kfr_filter_create_iir_plan_f64(sos, 1); + + kfr_f64 buf[FILTER_SIZE]; + kfr_f64 src[4] = { 0, 1, 0, -1 }; + for (int i = 0; i < FILTER_SIZE; i++) + buf[i] = src[i % 4]; + + kfr_filter_process_f64(filter, buf, buf, FILTER_SIZE); + + CHECK(fabs(buf[0] - 0.) < eps, "IIR: wrong result at %d: %f", 0, buf[0]); + CHECK(fabs(buf[1] - 0.002027496) < eps, "IIR: wrong result at %d: %f", 1, buf[1]); + CHECK(fabs(buf[60] - -0.001285130) < eps, "IIR: wrong result at %d: %f", 60, buf[60]); + + kfr_filter_delete_plan_f64(filter); +} + +int main() +{ + CHECK(KFR_HEADERS_VERSION <= kfr_version(), "Dynamic library is too old. At least %d required", + KFR_HEADERS_VERSION); + + printf("[INFO] %s\n", kfr_version_string()); + + test_memory(); + test_dft_f32(); + test_dft_f64(); + test_fir_f32(); + test_fir_f64(); + test_iir_f32(); + test_iir_f64(); + + if (failures == 0) + printf("[PASSED]\n"); + else + printf("[FAILED] %d check(s)\n", failures); + return failures; +}