diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 46543fac83..29fe458bdf 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -3,180 +3,15 @@ name: Nightly # This job is run at 00:00 UTC every day or on demand. on: - workflow_dispatch: - schedule: - - cron: '0 0 * * *' + push: + branches-ignore: + - 'dependabot/**' + pull_request: permissions: contents: read jobs: - fuzz-test: - name: Fuzz test - strategy: - fail-fast: false - matrix: - build_type: [Debug, Release] - compiler: [{c: clang, cxx: clang++}] - - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - fetch-depth: 0 - - - name: Install apt packages - run: | - sudo apt-get update - sudo apt-get install -y cmake hwloc libhwloc-dev libnuma-dev libtbb-dev - - - name: Configure CMake - run: > - cmake - -B ${{github.workspace}}/build - -DCMAKE_BUILD_TYPE=${{matrix.build_type}} - -DCMAKE_C_COMPILER=${{matrix.compiler.c}} - -DCMAKE_CXX_COMPILER=${{matrix.compiler.cxx}} - -DUMF_TESTS_FAIL_ON_SKIP=ON - -DUMF_DEVELOPER_MODE=ON - -DUMF_BUILD_FUZZTESTS=ON - - - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{matrix.build_type}} --verbose -j$(nproc) - - - name: Fuzz long test - working-directory: ${{github.workspace}}/build - run: ctest -C ${{matrix.build_type}} --output-on-failure --verbose -L "fuzz-long" - - valgrind: - name: Valgrind - strategy: - fail-fast: false - matrix: - tool: ['memcheck', 'drd', 'helgrind'] - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - fetch-depth: 0 - - - name: Install apt packages - run: | - sudo apt-get update - sudo apt-get install -y cmake hwloc libhwloc-dev libnuma-dev libtbb-dev valgrind - - - name: Configure CMake - run: > - cmake - -B ${{github.workspace}}/build - -DCMAKE_BUILD_TYPE=Debug - -DUMF_FORMAT_CODE_STYLE=OFF - -DUMF_DEVELOPER_MODE=ON - -DUMF_BUILD_LIBUMF_POOL_DISJOINT=ON - -DUMF_BUILD_LIBUMF_POOL_JEMALLOC=ON - -DUMF_BUILD_LEVEL_ZERO_PROVIDER=OFF - -DUMF_BUILD_CUDA_PROVIDER=OFF - -DUMF_USE_VALGRIND=1 - -DUMF_TESTS_FAIL_ON_SKIP=ON - - - name: Build - run: cmake --build ${{github.workspace}}/build --config Debug -j$(nproc) - - - name: Run tests under valgrind - run: ${{github.workspace}}/test/test_valgrind.sh ${{github.workspace}} ${{github.workspace}}/build ${{matrix.tool}} - - # TODO fix #843 - #icx: - # name: ICX - # env: - # VCPKG_PATH: "${{github.workspace}}/build/vcpkg/packages/hwloc_x64-windows;${{github.workspace}}/build/vcpkg/packages/tbb_x64-windows;${{github.workspace}}/build/vcpkg/packages/jemalloc_x64-windows" - # BUILD_DIR : "${{github.workspace}}/build" - # strategy: - # matrix: - # os: ['windows-2019', 'windows-2022'] - # build_type: [Debug] - # compiler: [{c: icx, cxx: icx}] - # shared_library: ['ON', 'OFF'] - # include: - # - os: windows-2022 - # build_type: Release - # compiler: {c: icx, cxx: icx} - # shared_library: 'ON' - # - # runs-on: ${{matrix.os}} - # - # steps: - # - name: Checkout - # uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - # with: - # fetch-depth: 0 - # - # - name: Initialize vcpkg - # uses: lukka/run-vcpkg@5e0cab206a5ea620130caf672fce3e4a6b5666a1 # v11.5 - # with: - # vcpkgGitCommitId: 3dd44b931481d7a8e9ba412621fa810232b66289 - # vcpkgDirectory: ${{env.BUILD_DIR}}/vcpkg - # vcpkgJsonGlob: '**/vcpkg.json' - # - # - name: Install dependencies - # run: vcpkg install - # - # - name: Install Ninja - # uses: seanmiddleditch/gha-setup-ninja@96bed6edff20d1dd61ecff9b75cc519d516e6401 # v5 - # - # - name: Download icx compiler - # env: - # # Link source: https://www.intel.com/content/www/us/en/developer/tools/oneapi/dpc-compiler-download.html - # CMPLR_LINK: "https://registrationcenter-download.intel.com/akdlm/IRC_NAS/15a35578-2f9a-4f39-804b-3906e0a5f8fc/w_dpcpp-cpp-compiler_p_2024.2.1.83_offline.exe" - # run: | - # Invoke-WebRequest -Uri "${{ env.CMPLR_LINK }}" -OutFile compiler_install.exe - # - # - name: Install icx compiler - # shell: cmd - # run: | - # start /b /wait .\compiler_install.exe -s -x -f extracted --log extract.log - # extracted\bootstrapper.exe -s --action install --eula=accept -p=NEED_VS2017_INTEGRATION=0 ^ - # -p=NEED_VS2019_INTEGRATION=0 -p=NEED_VS2022_INTEGRATION=0 --log-dir=. - # - # - name: Configure build - # shell: cmd - # run: | - # call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" - # call "C:\Program Files (x86)\Intel\oneAPI\setvars-vcvarsall.bat" - # cmake ^ - # -B ${{env.BUILD_DIR}} ^ - # -DCMAKE_PREFIX_PATH="${{env.VCPKG_PATH}}" ^ - # -DCMAKE_C_COMPILER=${{matrix.compiler.c}} ^ - # -DCMAKE_CXX_COMPILER=${{matrix.compiler.cxx}} ^ - # -G Ninja ^ - # -DUMF_BUILD_SHARED_LIBRARY=${{matrix.shared_library}} ^ - # -DUMF_FORMAT_CODE_STYLE=OFF ^ - # -DUMF_DEVELOPER_MODE=ON ^ - # -DUMF_BUILD_LIBUMF_POOL_DISJOINT=ON ^ - # -DUMF_BUILD_LIBUMF_POOL_JEMALLOC=ON ^ - # -DUMF_BUILD_LEVEL_ZERO_PROVIDER=ON ^ - # -DUMF_BUILD_CUDA_PROVIDER=ON ^ - # -DUMF_TESTS_FAIL_ON_SKIP=ON - # - # - name: Build UMF - # shell: cmd - # run: | - # call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" - # call "C:\Program Files (x86)\Intel\oneAPI\setvars-vcvarsall.bat" - # cmake --build ${{env.BUILD_DIR}} --config ${{matrix.build_type}} -j %NUMBER_OF_PROCESSORS% - # - # - name: Run tests - # shell: cmd - # working-directory: ${{env.BUILD_DIR}} - # run: | - # call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" - # call "C:\Program Files (x86)\Intel\oneAPI\setvars-vcvarsall.bat" - # ctest -C ${{matrix.build_type}} --output-on-failure --test-dir test - L0: uses: ./.github/workflows/reusable_gpu.yml with: @@ -185,12 +20,3 @@ jobs: uses: ./.github/workflows/reusable_gpu.yml with: name: "CUDA" - - # Full execution of QEMU tests - QEMU: - uses: ./.github/workflows/reusable_qemu.yml - with: - short_run: false - # Beside the 2 LTS Ubuntu, we also test this on the latest Ubuntu - to be updated - # every 6 months, so we verify the latest version of packages (compilers, etc.). - os: "['ubuntu-22.04', 'ubuntu-24.04', 'ubuntu-24.10']" diff --git a/.github/workflows/pr_push.yml b/.github/workflows/pr_push.yml deleted file mode 100644 index 9623b69f1b..0000000000 --- a/.github/workflows/pr_push.yml +++ /dev/null @@ -1,90 +0,0 @@ -# Run checks required for a PR to merge and verify if post-merge commit is valid. -# This workflow only call other workflows. -name: PR/push - -on: - push: - branches-ignore: - - 'dependabot/**' - pull_request: - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - CodeChecks: - uses: ./.github/workflows/reusable_checks.yml - DocsBuild: - uses: ./.github/workflows/reusable_docs_build.yml - FastBuild: - name: Fast builds - needs: [CodeChecks, DocsBuild] - uses: ./.github/workflows/reusable_fast.yml - Build: - name: Basic builds - needs: [FastBuild] - uses: ./.github/workflows/reusable_basic.yml - DevDax: - needs: [FastBuild] - uses: ./.github/workflows/reusable_dax.yml - MultiNuma: - needs: [FastBuild] - uses: ./.github/workflows/reusable_multi_numa.yml - L0: - needs: [Build] - uses: ./.github/workflows/reusable_gpu.yml - with: - name: "LEVEL_ZERO" - shared_lib: "['ON']" - CUDA: - needs: [Build] - uses: ./.github/workflows/reusable_gpu.yml - with: - name: "CUDA" - shared_lib: "['ON']" - Sanitizers: - needs: [FastBuild] - uses: ./.github/workflows/reusable_sanitizers.yml - QEMU: - needs: [FastBuild] - uses: ./.github/workflows/reusable_qemu.yml - with: - short_run: true - Benchmarks: - needs: [Build] - uses: ./.github/workflows/reusable_benchmarks.yml - ProxyLib: - needs: [Build] - uses: ./.github/workflows/reusable_proxy_lib.yml - Valgrind: - needs: [Build] - uses: ./.github/workflows/reusable_valgrind.yml - Coverage: - # total coverage (on upstream only) - if: github.repository == 'oneapi-src/unified-memory-framework' - needs: [Build, DevDax, L0, CUDA, MultiNuma, QEMU, ProxyLib] - uses: ./.github/workflows/reusable_coverage.yml - secrets: inherit - with: - trigger: "${{github.event_name}}" - Coverage_partial: - # partial coverage (on forks) - if: github.repository != 'oneapi-src/unified-memory-framework' - needs: [Build, QEMU, ProxyLib] - uses: ./.github/workflows/reusable_coverage.yml - CodeQL: - needs: [Build] - permissions: - contents: read - security-events: write - uses: ./.github/workflows/reusable_codeql.yml - Trivy: - needs: [Build] - permissions: - contents: read - security-events: write - uses: ./.github/workflows/reusable_trivy.yml diff --git a/benchmark/benchmark.cpp b/benchmark/benchmark.cpp index c10bbda877..655545d1e2 100644 --- a/benchmark/benchmark.cpp +++ b/benchmark/benchmark.cpp @@ -37,20 +37,27 @@ struct glibc_malloc : public allocator_interface { }; struct os_provider : public provider_interface { - umf_os_memory_provider_params_handle_t params = NULL; - os_provider() { - umfOsMemoryProviderParamsCreate(¶ms); - return; - } - - ~os_provider() { - if (params != NULL) { - umfOsMemoryProviderParamsDestroy(params); + provider_interface::params_ptr + getParams(::benchmark::State &state) override { + umf_os_memory_provider_params_handle_t raw_params = nullptr; + umfOsMemoryProviderParamsCreate(&raw_params); + if (!raw_params) { + state.SkipWithError("Failed to create os provider params"); + return {nullptr, [](void *) {}}; } + + // Use a lambda as the custom deleter + auto deleter = [](void *p) { + auto handle = + static_cast(p); + umfOsMemoryProviderParamsDestroy(handle); + }; + + return {static_cast(raw_params), deleter}; } - void *getParams() override { return params; } - umf_memory_provider_ops_t *getOps() override { + umf_memory_provider_ops_t * + getOps([[maybe_unused]] ::benchmark::State &state) override { return umfOsMemoryProviderOps(); } static std::string name() { return "os_provider"; } @@ -62,73 +69,60 @@ struct proxy_pool : public pool_interface { getOps([[maybe_unused]] ::benchmark::State &state) override { return umfProxyPoolOps(); } - void *getParams([[maybe_unused]] ::benchmark::State &state) override { - return nullptr; - } + static std::string name() { return "proxy_pool<" + Provider::name() + ">"; } }; #ifdef UMF_POOL_DISJOINT_ENABLED template struct disjoint_pool : public pool_interface { - umf_disjoint_pool_params_handle_t disjoint_memory_pool_params; + umf_memory_pool_ops_t * + getOps([[maybe_unused]] ::benchmark::State &state) override { + return umfDisjointPoolOps(); + } - disjoint_pool() { - disjoint_memory_pool_params = NULL; - auto ret = umfDisjointPoolParamsCreate(&disjoint_memory_pool_params); + typename pool_interface::params_ptr + getParams(::benchmark::State &state) override { + umf_disjoint_pool_params_handle_t raw_params = nullptr; + auto ret = umfDisjointPoolParamsCreate(&raw_params); if (ret != UMF_RESULT_SUCCESS) { - return; + state.SkipWithError("Failed to create disjoint pool params"); + return {nullptr, [](void *) {}}; } - // those function should never fail, so error handling is minimal. - ret = umfDisjointPoolParamsSetSlabMinSize(disjoint_memory_pool_params, - 4096); - if (ret != UMF_RESULT_SUCCESS) { - goto err; - } + typename pool_interface::params_ptr params( + raw_params, [](void *p) { + umfDisjointPoolParamsDestroy( + static_cast(p)); + }); - ret = umfDisjointPoolParamsSetCapacity(disjoint_memory_pool_params, 4); + ret = umfDisjointPoolParamsSetSlabMinSize(raw_params, 4096); if (ret != UMF_RESULT_SUCCESS) { - goto err; + state.SkipWithError("Failed to set slab min size"); + return {nullptr, [](void *) {}}; } - ret = umfDisjointPoolParamsSetMinBucketSize(disjoint_memory_pool_params, - 4096); + ret = umfDisjointPoolParamsSetCapacity(raw_params, 4); if (ret != UMF_RESULT_SUCCESS) { - goto err; + state.SkipWithError("Failed to set capacity"); + return {nullptr, [](void *) {}}; } - ret = umfDisjointPoolParamsSetMaxPoolableSize( - disjoint_memory_pool_params, 4096 * 16); - + ret = umfDisjointPoolParamsSetMinBucketSize(raw_params, 4096); if (ret != UMF_RESULT_SUCCESS) { - goto err; + state.SkipWithError("Failed to set min bucket size"); + return {nullptr, [](void *) {}}; } - return; - err: - umfDisjointPoolParamsDestroy(disjoint_memory_pool_params); - disjoint_memory_pool_params = NULL; - } - - ~disjoint_pool() { - if (disjoint_memory_pool_params != NULL) { - umfDisjointPoolParamsDestroy(disjoint_memory_pool_params); + ret = umfDisjointPoolParamsSetMaxPoolableSize(raw_params, 4096 * 16); + if (ret != UMF_RESULT_SUCCESS) { + state.SkipWithError("Failed to set max poolable size"); + return {nullptr, [](void *) {}}; } - } - umf_memory_pool_ops_t * - getOps([[maybe_unused]] ::benchmark::State &state) override { - return umfDisjointPoolOps(); + return params; } - void *getParams([[maybe_unused]] ::benchmark::State &state) override { - if (disjoint_memory_pool_params == NULL) { - state.SkipWithError("Failed to create disjoint pool params"); - } - - return disjoint_memory_pool_params; - } static std::string name() { return "disjoint_pool<" + Provider::name() + ">"; } @@ -142,9 +136,7 @@ struct jemalloc_pool : public pool_interface { getOps([[maybe_unused]] ::benchmark::State &state) override { return umfJemallocPoolOps(); } - void *getParams([[maybe_unused]] ::benchmark::State &state) override { - return NULL; - } + static std::string name() { return "jemalloc_pool<" + Provider::name() + ">"; } @@ -158,10 +150,7 @@ struct scalable_pool : public pool_interface { getOps([[maybe_unused]] ::benchmark::State &state) override { return umfScalablePoolOps(); } - virtual void * - getParams([[maybe_unused]] ::benchmark::State &state) override { - return NULL; - } + static std::string name() { return "scalable_pool<" + Provider::name() + ">"; } diff --git a/benchmark/benchmark.hpp b/benchmark/benchmark.hpp index ad9ab7cc83..6ac7a4dfa5 100644 --- a/benchmark/benchmark.hpp +++ b/benchmark/benchmark.hpp @@ -232,12 +232,14 @@ class alloc_benchmark : public benchmark_interface { state.ResumeTiming(); } } + static std::vector argsName() { auto n = benchmark_interface::argsName(); std::vector res = {"max_allocs", "pre_allocs"}; res.insert(res.end(), n.begin(), n.end()); return res; } + static std::string name() { return base::name() + "/alloc"; } static int64_t iterations() { return 200000; } @@ -320,13 +322,16 @@ class multiple_malloc_free_benchmark : public alloc_benchmark { static std::string name() { return base::base::name() + "/multiple_malloc_free"; } + static std::vector argsName() { auto n = benchmark_interface::argsName(); std::vector res = {"max_allocs"}; res.insert(res.end(), n.begin(), n.end()); return res; } + static int64_t iterations() { return 2000; } + std::default_random_engine generator; distribution dist; }; @@ -352,9 +357,11 @@ class provider_allocator : public allocator_interface { } return ptr; } + void benchFree(void *ptr, size_t size) override { umfMemoryProviderFree(provider.provider, ptr, size); } + static std::string name() { return Provider::name(); } private: @@ -374,6 +381,7 @@ template class pool_allocator : public allocator_interface { virtual void *benchAlloc(size_t size) override { return umfPoolMalloc(pool.pool, size); } + virtual void benchFree(void *ptr, [[maybe_unused]] size_t size) override { umfPoolFree(pool.pool, ptr); } diff --git a/benchmark/benchmark_interfaces.hpp b/benchmark/benchmark_interfaces.hpp index e25c977710..516a20b697 100644 --- a/benchmark/benchmark_interfaces.hpp +++ b/benchmark/benchmark_interfaces.hpp @@ -6,6 +6,7 @@ * */ +#include #include #include #include @@ -39,6 +40,7 @@ struct benchmark_interface : public benchmark::Fixture { int argPos = alloc_size.SetUp(state, 0); allocator.SetUp(state, argPos); } + void TearDown(::benchmark::State &state) { alloc_size.TearDown(state); allocator.TearDown(state); @@ -54,6 +56,7 @@ struct benchmark_interface : public benchmark::Fixture { res.insert(res.end(), a.begin(), a.end()); return res; } + static std::string name() { return Allocator::name(); } static int64_t iterations() { return 10000; } Size alloc_size; @@ -61,13 +64,16 @@ struct benchmark_interface : public benchmark::Fixture { }; struct provider_interface { + using params_ptr = std::unique_ptr; + umf_memory_provider_handle_t provider = NULL; virtual void SetUp(::benchmark::State &state) { if (state.thread_index() != 0) { return; } + auto params = getParams(state); auto umf_result = - umfMemoryProviderCreate(getOps(), getParams(), &provider); + umfMemoryProviderCreate(getOps(state), params.get(), &provider); if (umf_result != UMF_RESULT_SUCCESS) { state.SkipWithError("umfMemoryProviderCreate() failed"); } @@ -83,21 +89,30 @@ struct provider_interface { } } - virtual umf_memory_provider_ops_t *getOps() { return nullptr; } - virtual void *getParams() { return nullptr; } + virtual umf_memory_provider_ops_t * + getOps([[maybe_unused]] ::benchmark::State &state) { + return nullptr; + } + + virtual params_ptr getParams([[maybe_unused]] ::benchmark::State &state) { + return {nullptr, [](void *) {}}; + } }; template ::value>> struct pool_interface { + using params_ptr = std::unique_ptr; + virtual void SetUp(::benchmark::State &state) { provider.SetUp(state); if (state.thread_index() != 0) { return; } + auto params = getParams(state); auto umf_result = umfPoolCreate(getOps(state), provider.provider, - getParams(state), 0, &pool); + params.get(), 0, &pool); if (umf_result != UMF_RESULT_SUCCESS) { state.SkipWithError("umfPoolCreate() failed"); } @@ -121,8 +136,8 @@ struct pool_interface { getOps([[maybe_unused]] ::benchmark::State &state) { return nullptr; } - virtual void *getParams([[maybe_unused]] ::benchmark::State &state) { - return nullptr; + virtual params_ptr getParams([[maybe_unused]] ::benchmark::State &state) { + return {nullptr, [](void *) {}}; } T provider; umf_memory_pool_handle_t pool;