diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d4618644..a58801b4c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,17 @@ SET (PACKAGE_INC_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/inc) option (USE_LIBFTDI "USE_LIBFTDI" OFF) option (USE_OPENMP "USE_OPENMP" OFF) option (WARNINGS_AS_ERRORS "WARNINGS_AS_ERRORS" OFF) + +if (APPLE AND USE_OPENMP) + if (NOT DEFINED OpenMP_ROOT AND NOT DEFINED ENV{OpenMP_ROOT}) + if (EXISTS "/opt/homebrew/opt/libomp") + set (OpenMP_ROOT "/opt/homebrew/opt/libomp") + elseif (EXISTS "/usr/local/opt/libomp") + set (OpenMP_ROOT "/usr/local/opt/libomp") + endif () + endif () +endif () + option (BUILD_OYMOTION_SDK "BUILD_OYMOTION_SDK" OFF) option (BUILD_SYNCHRONI_SDK "BUILD_SYNCHRONI_SDK" ON) option (BUILD_BLUETOOTH "BUILD_BLUETOOTH" OFF) diff --git a/docs/BuildBrainFlow.rst b/docs/BuildBrainFlow.rst index 6a87bdcca..b11b0a817 100644 --- a/docs/BuildBrainFlow.rst +++ b/docs/BuildBrainFlow.rst @@ -280,6 +280,36 @@ MacOS python3 build.py --help +OpenMP Acceleration +~~~~~~~~~~~~~~~~~~~~ + +BrainFlow supports multi-core parallel execution via OpenMP for intensive data handling routines, including multi-channel signal filtering, wavelet denoising, and band power calculations across channels. + +To build BrainFlow with OpenMP support, first ensure that the OpenMP development package is available on your system: + +- **Linux:** Install the OpenMP development headers using your package manager, e.g. :code:`sudo apt-get install libomp-dev` on Debian/Ubuntu or :code:`sudo dnf install libgomp` on Fedora/RHEL. +- **MacOS:** Install OpenMP via Homebrew: :code:`brew install libomp`. BrainFlow's build configuration automatically discovers Homebrew's :code:`libomp` installation. +- **Windows:** OpenMP is included with MSVC in Visual Studio; no additional installation is needed. + +.. compound:: + + Using :code:`build.py`: :: + + python3 tools/build.py --use-openmp + +.. compound:: + + Using CMake directly: :: + + cmake -B build -DUSE_OPENMP=ON + cmake --build build --config Release + +.. note:: + + On Apple Silicon macOS, Homebrew's :code:`libomp` is built for the native architecture (:code:`arm64`). When :code:`--use-openmp` is specified without an explicit :code:`--cmake-osx-architectures`, :code:`tools/build.py` automatically configures the build for your machine's native architecture. If invoking CMake directly on Apple Silicon, pass :code:`-DCMAKE_OSX_ARCHITECTURES=arm64` (or :code:`x86_64` on Intel Macs). + + + Android --------- diff --git a/src/data_handler/build.cmake b/src/data_handler/build.cmake index 7aa3d5b50..f2e2bdc5d 100644 --- a/src/data_handler/build.cmake +++ b/src/data_handler/build.cmake @@ -1,7 +1,9 @@ + include (${CMAKE_CURRENT_SOURCE_DIR}/third_party/DSPFilters/build.cmake) include (${CMAKE_CURRENT_SOURCE_DIR}/third_party/wavelib/build.cmake) include (${CMAKE_CURRENT_SOURCE_DIR}/third_party/kissfft/build.cmake) + if (CMAKE_SIZEOF_VOID_P EQUAL 8) SET (DATA_HANDLER_NAME "DataHandler") if (APPLE) diff --git a/third_party/kissfft/build.cmake b/third_party/kissfft/build.cmake index 413e226e6..dd8cc42fd 100644 --- a/third_party/kissfft/build.cmake +++ b/third_party/kissfft/build.cmake @@ -143,7 +143,13 @@ endif() # if(KISSFFT_OPENMP) - if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang|AppleClang") + find_package(OpenMP) + if(TARGET OpenMP::OpenMP_C) + target_link_libraries(kissfft PRIVATE OpenMP::OpenMP_C) + elseif(OpenMP_C_FOUND) + target_compile_options(kissfft PRIVATE ${OpenMP_C_FLAGS}) + target_link_libraries(kissfft PRIVATE ${OpenMP_C_LIBRARIES}) + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang|AppleClang|MSVC") if (NOT MSVC) target_compile_options(kissfft PRIVATE -fopenmp) if(${CMAKE_VERSION} VERSION_LESS "3.13.0") @@ -159,11 +165,11 @@ if(KISSFFT_OPENMP) target_link_options(kissfft PRIVATE "/openmp") endif() endif() - set(KISSFFT_EXPORT_SUFFIX "-openmp") - set(KISSFFT_OUTPUT_NAME "kissfft-${KISSFFT_DATATYPE}-openmp") else() message(FATAL_ERROR "Don't know how to enable OpenMP for this compiler") endif() + set(KISSFFT_EXPORT_SUFFIX "-openmp") + set(KISSFFT_OUTPUT_NAME "kissfft-${KISSFFT_DATATYPE}-openmp") endif() # @@ -227,7 +233,9 @@ function(add_kissfft_executable NAME) set_target_properties(${NAME} PROPERTIES OUTPUT_NAME "${NAME}-${KISSFFT_DATATYPE}") else() - if (NOT MSVC) + if(TARGET OpenMP::OpenMP_C) + target_link_libraries(${NAME} PRIVATE OpenMP::OpenMP_C) + elseif (NOT MSVC) target_compile_options(${NAME} PRIVATE -fopenmp) if(${CMAKE_VERSION} VERSION_LESS "3.13.0") target_link_libraries(${NAME} PRIVATE "-fopenmp") diff --git a/tools/build.py b/tools/build.py index 5a93d08bd..fb1b42648 100644 --- a/tools/build.py +++ b/tools/build.py @@ -170,14 +170,8 @@ def prepare_args(): '--cmake-system-version', type=str, help='system version for win', required=False) elif platform.system() == 'Darwin': - macos_ver = platform.mac_ver()[0] - versions = [int(x) for x in macos_ver.split('.')] - if versions[0] >= 11: - parser.add_argument('--cmake-osx-architectures', type=str, - help='archs for osx', required=False, default='arm64;x86_64') - else: - parser.add_argument('--cmake-osx-architectures', - type=str, help='archs for osx', required=False) + parser.add_argument('--cmake-osx-architectures', type=str, + help='archs for osx', required=False, default=None) parser.add_argument('--cmake-osx-deployment-target', type=str, help='min supported version of osx', required=False, default='10.15') parser.add_argument('--use-libftdi', action='store_true') @@ -260,9 +254,23 @@ def config(args): cmd_config.append('-DUSE_OPENMP=ON') if hasattr(args, 'oymotion') and args.oymotion: cmd_config.append('-DBUILD_OYMOTION_SDK=ON') - if hasattr(args, 'cmake_osx_architectures') and args.cmake_osx_architectures: - cmd_config.append('-DCMAKE_OSX_ARCHITECTURES=%s' % - args.cmake_osx_architectures) + if hasattr(args, 'cmake_osx_architectures'): + osx_arch = args.cmake_osx_architectures + if osx_arch is None: + if args.use_openmp: + print('Building with OpenMP on macOS: defaulting architecture to native %s because Homebrew libomp is single-architecture.' % platform.machine()) + osx_arch = platform.machine() + else: + macos_ver = platform.mac_ver()[0] + if macos_ver: + try: + major_ver = int(macos_ver.split('.')[0]) + if major_ver >= 11: + osx_arch = 'arm64;x86_64' + except ValueError: + pass + if osx_arch: + cmd_config.append('-DCMAKE_OSX_ARCHITECTURES=%s' % osx_arch) if hasattr(args, 'cmake_osx_deployment_target') and args.cmake_osx_deployment_target: cmd_config.append('-DCMAKE_OSX_DEPLOYMENT_TARGET=%s' % args.cmake_osx_deployment_target)