diff --git a/.gitignore b/.gitignore index 38b4644f..b8eea46d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ DartConfiguration.tcl Testing build/ +.build/ +.cache/ *.swp *.png diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d7106ba..2e2db929 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ project(redGrapesExamplesAndTests VERSION 0.1.0) # Examples & Tests ######################################################## option(redGrapes_BUILD_EXAMPLES "Build the examples" ON) +option(BUILD_TESTING "Build the tests" OFF) if(redGrapes_BUILD_EXAMPLES) add_subdirectory("examples/") diff --git a/README.md b/README.md index 166556ae..49974b3b 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,13 @@ **Re**source-based, **D**eclarative task-**Gra**phs for **P**arallel, **E**vent-driven **S**cheduling [![GitHub commits](https://img.shields.io/github/commits-since/ComputationalRadiationPhysics/redGrapes/v0.1.0/dev.svg)](https://GitHub.com/ComputationalRadiationPhysics/redGrapes/commit/) -[![Language](https://img.shields.io/badge/language-C%2B%2B14-orange)](https://isocpp.org/) +[![Language](https://img.shields.io/badge/language-C%2B%2B20-orange)](https://isocpp.org/) [![License](https://img.shields.io/badge/license-MPL--2.0-blue.svg)](https://www.mozilla.org/en-US/MPL/2.0/) [![Documentation Status](https://readthedocs.org/projects/redgrapes/badge/?version=dev)](https://redgrapes.readthedocs.io/en/dev/?badge=dev)
-RedGrapes is a C++17 framework for declaratively creating and scheduling task-graphs, based on a high-level resource description. +RedGrapes is a C++20 framework for declaratively creating and scheduling task-graphs, based on a high-level resource description. ### Motivation @@ -103,7 +103,7 @@ However since we want to achieve **declarative task dependencies**, for which th **compile time checked memory access**: The automatic creation of a task graph is often done via annotations, e.g., a pragma in OpenMP, but that does not guarantee the correctness of the access specifications. RedGrapes leverages the type system to write relatively safe code in that regard. -**native C++**: PaRSEC has a complicated toolchain using additional compilers, OpenMP makes use of pragmas that require compiler support. RedGrapes only requires the C++14 standard. +**native C++**: PaRSEC has a complicated toolchain using additional compilers, OpenMP makes use of pragmas that require compiler support. RedGrapes only requires the C++20 standard. **typesafe**: Some libraries like Legion or StarPU use an untyped ``argc``/``argv`` interface to pass parameters to tasks, which is error-prone. Both libraries in general also require a lot of C-style boilerplate. @@ -154,7 +154,7 @@ Its conceptual design is based on a [whitepaper by A. Huebl, R. Widera, and A. M ### Dependencies -RedGrapes requires a compiler supporting the C++17 standard. +RedGrapes requires a compiler supporting the C++20 standard. RedGrapes further depends on the following libraries: * [ConcurrentQueue](https://github.com/cameron314/concurrentqueue) by [Cameron Desrochers](https://moodycamel.com) diff --git a/docs/source/install.rst b/docs/source/install.rst index eaa225ae..bf5d8407 100644 --- a/docs/source/install.rst +++ b/docs/source/install.rst @@ -26,12 +26,12 @@ In order to build the examples and tests, do the typical cmake procedure: Enable Tests with :: - cmake .. BUILD_TESTING=ON + cmake .. -DBUILD_TESTING=ON Set Loglevel :: - cmake .. CMAKE_CXX_FLAGS="-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_OFF" + cmake .. -DCMAKE_CXX_FLAGS="-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_OFF" Enable Tracing with Perfetto :: - cmake .. redGrapes_ENABLE_PERFETTO=ON + cmake .. -DredGrapes_ENABLE_PERFETTO=ON diff --git a/examples/1_resources.cpp b/examples/1_resources.cpp index f4980c15..b3303a29 100644 --- a/examples/1_resources.cpp +++ b/examples/1_resources.cpp @@ -1,4 +1,4 @@ -/* Copyright 2019 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -6,31 +6,31 @@ */ #include -#include -#include -#include + +#include int main(int, char*[]) { - redGrapes::init(1); - redGrapes::FieldResource> a; - redGrapes::IOResource b; - redGrapes::IOResource c; + auto rg = redGrapes::init(1); + using TTask = decltype(rg)::RGTask; + + auto a = rg.createFieldResource>(); + auto b = rg.createIOResource(); + auto c = rg.createIOResource(); - redGrapes::ResourceUser user1( + redGrapes::ResourceUser user1( {a.read(), // complete resource a.write().area({0}, {10}), // write only indices 0 to 10 b.write()}); - redGrapes::ResourceUser user2({b.read()}); + redGrapes::ResourceUser user2({b.read()}); - redGrapes::ResourceUser user3({b.read(), c.write()}); + redGrapes::ResourceUser user3({b.read(), c.write()}); - std::cout << "is_serial(user1,user1) = " << redGrapes::ResourceUser::is_serial(user1, user1) << std::endl; - std::cout << "is_serial(user1,user2) = " << redGrapes::ResourceUser::is_serial(user1, user2) << std::endl; - std::cout << "is_serial(user1,user3) = " << redGrapes::ResourceUser::is_serial(user1, user3) << std::endl; - std::cout << "is_serial(user2,user3) = " << redGrapes::ResourceUser::is_serial(user2, user3) << std::endl; + std::cout << "is_serial(user1,user1) = " << is_serial(user1, user1) << std::endl; + std::cout << "is_serial(user1,user2) = " << is_serial(user1, user2) << std::endl; + std::cout << "is_serial(user1,user3) = " << is_serial(user1, user3) << std::endl; + std::cout << "is_serial(user2,user3) = " << is_serial(user2, user3) << std::endl; - redGrapes::finalize(); return 0; } diff --git a/examples/2_functors.cpp b/examples/2_functors.cpp index b5e9b0e4..2d2c1237 100644 --- a/examples/2_functors.cpp +++ b/examples/2_functors.cpp @@ -1,4 +1,4 @@ -/* Copyright 2019 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -6,10 +6,6 @@ */ #include -#include -#include - -#include int square(int x) { @@ -19,10 +15,9 @@ int square(int x) int main() { spdlog::set_level(spdlog::level::trace); - redGrapes::init(1); + auto rg = redGrapes::init(1); - fmt::print("square(2) = {}\n", redGrapes::emplace_task(square, 2).get()); + fmt::print("square(2) = {}\n", rg.emplace_task(square, 2).get()); - redGrapes::finalize(); return 0; } diff --git a/examples/3_functors_with_resources.cpp b/examples/3_functors_with_resources.cpp index 20364fa4..8fc35cb1 100644 --- a/examples/3_functors_with_resources.cpp +++ b/examples/3_functors_with_resources.cpp @@ -1,4 +1,4 @@ -/* Copyright 2019 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -15,13 +15,14 @@ int main(void) { spdlog::set_level(spdlog::level::trace); - redGrapes::init(); + auto rg = redGrapes::init(); - redGrapes::IOResource a, b; + auto a = rg.createIOResource(); + auto b = rg.createIOResource(); for(int i = 0; i < 1; ++i) { - redGrapes::emplace_task( + rg.emplace_task( [](auto a) { std::cout << "Write to A" << std::endl; @@ -31,7 +32,7 @@ int main(void) }, a.write()); - redGrapes::emplace_task( + rg.emplace_task( [](auto a) { std::cout << "Read A: " << *a << std::endl; @@ -39,17 +40,17 @@ int main(void) }, a.read()); - redGrapes::emplace_task( + rg.emplace_task( [](auto b) { std::cout << "Write to B" << std::endl; - std::this_thread::sleep_for(std::chrono::seconds(2)); + std::this_thread::sleep_for(std::chrono::seconds(3)); *b = 7; std::cout << "Write B done" << std::endl; }, b.write()); - redGrapes::emplace_task( + rg.emplace_task( [](auto a, auto b) { std::cout << "Read A & B: " << *a << ", " << *b << std::endl; @@ -59,7 +60,5 @@ int main(void) b.read()); } - redGrapes::finalize(); - return 0; } diff --git a/examples/4_refinements.cpp b/examples/4_refinements.cpp index e30bf56e..213ecc48 100644 --- a/examples/4_refinements.cpp +++ b/examples/4_refinements.cpp @@ -1,4 +1,4 @@ -/* Copyright 2019 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -16,43 +17,45 @@ int main(int, char*[]) spdlog::set_level(spdlog::level::trace); spdlog::set_pattern("[thread %t] %^[%l]%$ %v"); - redGrapes::init(4); - - redGrapes::emplace_task( - [] - { - std::cout << "f1" - << "..." << std::endl; - - int i = 0; - for(auto t : redGrapes::backtrace()) - fmt::print("refinement 1 backtrace [{}]: {}\n", i++, t.get().label); - - redGrapes::emplace_task( - [] - { - fmt::print("Refinement 1\n"); - std::this_thread::sleep_for(std::chrono::seconds(1)); - }); - - SPDLOG_TRACE("EX: create next task task"); - - redGrapes::emplace_task( - [] - { - fmt::print("Refinement 2\n"); - std::this_thread::sleep_for(std::chrono::seconds(1)); - - int i = 0; - for(auto t : redGrapes::backtrace()) - fmt::print("refinement 2 backtrace [{}]: {}\n", i++, (redGrapes::TaskProperties const&) t); - }) - .label("Child Task 2"); - }) + auto rg = redGrapes::init(4); + + rg.emplace_task( + [&rg] + { + std::cout << "f1" + << "..." << std::endl; + + int i = 0; + for(auto t : rg.backtrace()) + fmt::print("refinement 1 backtrace [{}]: {}\n", i++, t.get().label); + + rg.emplace_task( + [] + { + fmt::print("Refinement 1\n"); + std::this_thread::sleep_for(std::chrono::seconds(1)); + }); + + SPDLOG_TRACE("EX: create next task task"); + + rg.emplace_task( + [&rg] + { + fmt::print("Refinement 2\n"); + std::this_thread::sleep_for(std::chrono::seconds(1)); + + int i = 0; + for(auto t : rg.backtrace()) + fmt::print( + "refinement 2 backtrace [{}]: {}\n", + i++, + (decltype(rg)::RGTask::TaskProperties const&) t); // TODO cleaner way to do this + }) + .label("Child Task 2"); + }) .label("Parent Task") .submit(); - redGrapes::finalize(); return 0; } diff --git a/examples/5_access_demotion.cpp b/examples/5_access_demotion.cpp index 03e3e7c3..7bd5500a 100644 --- a/examples/5_access_demotion.cpp +++ b/examples/5_access_demotion.cpp @@ -1,4 +1,4 @@ -/* Copyright 2019 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,7 +7,6 @@ #include #include -#include #include #include @@ -18,33 +17,32 @@ namespace rg = redGrapes; int main(int, char*[]) { spdlog::set_level(spdlog::level::trace); - rg::init(); - rg::IOResource a; + auto rg = rg::init(); + auto a = rg.createIOResource(); - rg::emplace_task( - [](auto a) + rg.emplace_task( + [&](auto a) { std::cout << "f1 writes A" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "f1 now only reads A" << std::endl; - rg::update_properties( - rg::TaskProperties::Patch::Builder().remove_resources({a.write()}).add_resources({a.read()})); + rg.update_properties(decltype(rg)::RGTask::TaskProperties::Patch::Builder() + .remove_resources({a.write()}) + .add_resources({a.read()})); std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "f1 done" << std::endl; }, a.write()); - rg::emplace_task( - [](auto a) + rg.emplace_task( + []([[maybe_unused]] auto a) { std::cout << "f2 reads A" << std::endl; std::cout << "f2 done" << std::endl; }, a.read()); - rg::finalize(); - return 0; } diff --git a/examples/6_resource_scope.cpp b/examples/6_resource_scope.cpp index 5d267117..283c9737 100644 --- a/examples/6_resource_scope.cpp +++ b/examples/6_resource_scope.cpp @@ -1,4 +1,4 @@ -/* Copyright 2019 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,35 +7,33 @@ #include #include -#include -#include + +#include namespace rg = redGrapes; int main() { - rg::init(1); - rg::IOResource a; // scope-level=0 + auto rg = rg::init(1); + auto a = rg.createIOResource(); // scope-level=0 - rg::emplace_task( - [](auto a) - { - std::cout << "scope = " << rg::scope_depth() << std::endl; - rg::IOResource b; // scope-level=1 + rg.emplace_task( + [&]([[maybe_unused]] auto a) + { + std::cout << "scope = " << rg.scope_depth() << std::endl; + auto b = rg.createIOResource(); // scope-level=1 - rg::emplace_task( - [](auto b) - { - *b = 1; - std::cout << "scope = " << rg::scope_depth() << std::endl; - }, - b.write()) - .get(); + rg.emplace_task( + [&](auto b) + { + *b = 1; + std::cout << "scope = " << rg.scope_depth() << std::endl; + }, + b.write()) + .get(); - std::cout << "scope = " << rg::scope_depth() << std::endl; - }, - a.read()) + std::cout << "scope = " << rg.scope_depth() << std::endl; + }, + a.read()) .enable_stack_switching(); - - rg::finalize(); } diff --git a/examples/7_event.cpp b/examples/7_event.cpp index 96f12203..3012a146 100644 --- a/examples/7_event.cpp +++ b/examples/7_event.cpp @@ -1,4 +1,4 @@ -/* Copyright 2019 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -8,9 +8,6 @@ #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_OFF #include -#include -#include -#include #include #include @@ -21,20 +18,20 @@ int main() spdlog::set_level(spdlog::level::trace); spdlog::set_pattern("[thread %t] %^[%l]%$ %v"); - redGrapes::init(1); + auto rg = redGrapes::init(1); - redGrapes::Resource r1; + auto r1 = rg.createResource(); - auto event_f = redGrapes::emplace_task( - [] - { - std::cout << "Task 1" << std::endl; - return redGrapes::create_event(); - }) + auto event_f = rg.emplace_task( + [&] + { + std::cout << "Task 1" << std::endl; + return rg.create_event(); + }) .resources({r1.make_access(redGrapes::access::IOAccess::write)}) .submit(); - redGrapes::emplace_task([] { std::cout << "Task 2" << std::endl; }) + rg.emplace_task([] { std::cout << "Task 2" << std::endl; }) .resources({r1.make_access(redGrapes::access::IOAccess::write)}); auto event = event_f.get(); @@ -45,7 +42,6 @@ int main() std::cout << "notify event" << std::endl; event->notify(); - redGrapes::finalize(); return 0; } diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index bdae5941..b2ddd839 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -3,8 +3,6 @@ cmake_minimum_required(VERSION 3.18.0) project(redGrapesExamples LANGUAGES CXX) -set(redGrapes_CONFIG_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/config") - find_package(redGrapes REQUIRED CONFIG PATHS "${CMAKE_CURRENT_LIST_DIR}/../") include_directories(SYSTEM ${redGrapes_INCLUDE_DIRS}) @@ -57,7 +55,7 @@ if(CMAKE_CUDA_COMPILER) enable_language(CUDA) add_executable(cuda_mandelbrot cuda_mandelbrot.cu) - target_compile_features(cuda_mandelbrot PUBLIC cuda_std_14) + target_compile_features(cuda_mandelbrot PUBLIC cxx_std_${redGrapes_CXX_STANDARD}) set_target_properties(cuda_mandelbrot PROPERTIES CUDA_EXTENSIONS OFF CUDA_STANDARD_REQUIRED ON diff --git a/examples/cholesky.cpp b/examples/cholesky.cpp index 65853130..0b20402f 100644 --- a/examples/cholesky.cpp +++ b/examples/cholesky.cpp @@ -1,27 +1,37 @@ -#include - -#include -// work-around, see -// https://github.com/xianyi/OpenBLAS/issues/1992#issuecomment-459474791 -// https://github.com/xianyi/OpenBLAS/pull/1998 -#include -#define lapack_complex_float std::complex -#define lapack_complex_double std::complex -// end work-around - -#include -#include - -#include - -#define REDGRAPES_TASK_PROPERTIES redGrapes::LabelProperty +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ #include #include -namespace rg = redGrapes; +#include +#include + +#include -void print_matrix(std::vector> A, int n_blocks, int blocksize); +template +void print_matrix(std::vector> A, int nblks, int blocksize) +{ + for(int ia = 0; ia < nblks; ++ia) + { + for(int ib = 0; ib < blocksize; ++ib) + { + for(int ja = 0; ja < nblks; ++ja) + { + for(int jb = 0; jb < blocksize; ++jb) + { + std::cout << (*A[ja * nblks + ia])[jb * blocksize + ib] << "; "; + } + } + std::cout << std::endl; + } + } + std::cout << std::endl; +} int main(int argc, char* argv[]) { @@ -44,7 +54,8 @@ int main(int argc, char* argv[]) if(argc >= 4) n_threads = atoi(argv[3]); - rg::init(n_threads); + auto rg = redGrapes::init(n_threads); + using RGTask = decltype(rg)::RGTask; size_t N = nblks * blksz; @@ -59,9 +70,8 @@ int main(int argc, char* argv[]) for(size_t i = 0; i < N; i++) Alin[i * N + i] += N; - // initialize tiled matrix in column-major layout - std::vector> A(nblks * nblks); + std::vector> A(nblks * nblks); // allocate each tile (also in column-major layout) for(size_t j = 0; j < nblks; ++j) @@ -88,10 +98,10 @@ int main(int argc, char* argv[]) for(size_t i = j + 1; i < nblks; i++) { // A[i,j] = A[i,j] - A[i,k] * (A[j,k])^t - rg::emplace_task( + rg.emplace_task( [blksz](auto a, auto b, auto c) { - spdlog::info("dgemm"); + spdlog::debug("dgemm"); cblas_dgemm( CblasColMajor, CblasNoTrans, @@ -117,10 +127,10 @@ int main(int argc, char* argv[]) for(size_t i = 0; i < j; i++) { // A[j,j] = A[j,j] - A[j,i] * (A[j,i])^t - rg::emplace_task( - [blksz, nblks](auto a, auto c) + rg.emplace_task( + [blksz](auto a, auto c) { - spdlog::info("dsyrk"); + spdlog::debug("dsyrk"); cblas_dsyrk( CblasColMajor, CblasLower, @@ -139,10 +149,10 @@ int main(int argc, char* argv[]) } // Cholesky Factorization of A[j,j] - rg::emplace_task( - [j, blksz, nblks](auto a) + rg.emplace_task( + [blksz](auto a) { - spdlog::info("dpotrf"); + spdlog::debug("dpotrf"); LAPACKE_dpotrf(LAPACK_COL_MAJOR, 'L', blksz, *a, blksz); }, A[j * nblks + j].write()); @@ -150,10 +160,10 @@ int main(int argc, char* argv[]) for(size_t i = j + 1; i < nblks; i++) { // A[i,j] <- A[i,j] = X * (A[j,j])^t - rg::emplace_task( - [blksz, nblks](auto a, auto b) + rg.emplace_task( + [blksz](auto a, auto b) { - spdlog::info("dtrsm"); + spdlog::debug("dtrsm"); cblas_dtrsm( CblasColMajor, CblasRight, @@ -173,28 +183,7 @@ int main(int argc, char* argv[]) } } - rg::finalize(); - print_matrix(A, nblks, blksz); return 0; } - -void print_matrix(std::vector> A, int nblks, int blocksize) -{ - for(int ia = 0; ia < nblks; ++ia) - { - for(int ib = 0; ib < blocksize; ++ib) - { - for(int ja = 0; ja < nblks; ++ja) - { - for(int jb = 0; jb < blocksize; ++jb) - { - std::cout << (*A[ja * nblks + ia])[jb * blocksize + ib] << "; "; - } - } - std::cout << std::endl; - } - } - std::cout << std::endl; -} diff --git a/examples/config/redGrapes_config.hpp b/examples/config/redGrapes_config.hpp deleted file mode 100644 index 3aff6572..00000000 --- a/examples/config/redGrapes_config.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - - -#include -#include - -enum SchedulerTags -{ - SCHED_MPI, - SCHED_CUDA -}; - -#define REDGRAPES_TASK_PROPERTIES \ - redGrapes::LabelProperty, redGrapes::scheduler::SchedulingTagProperties - -#define REDGRAPES_ALLOC_CHUNKSIZE (64 * 1024) - -template<> -struct fmt::formatter -{ - constexpr auto parse(format_parse_context& ctx) - { - return ctx.begin(); - } - - template - auto format(SchedulerTags const& tag, FormatContext& ctx) - { - switch(tag) - { - case SCHED_MPI: - return fmt::format_to(ctx.out(), "\"MPI\""); - case SCHED_CUDA: - return fmt::format_to(ctx.out(), "\"CUDA\""); - default: - return fmt::format_to(ctx.out(), "\"undefined\""); - } - } -}; diff --git a/examples/cuda_mandelbrot.cu b/examples/cuda_mandelbrot.cu index 627b4c9e..8ae9cb68 100644 --- a/examples/cuda_mandelbrot.cu +++ b/examples/cuda_mandelbrot.cu @@ -1,11 +1,22 @@ -/* Copyright 2020 Michael Sippel +/* Copyright 2020-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include +#include #include #include @@ -13,23 +24,6 @@ #include #include -enum SchedulerTag -{ - SCHED_CUDA -}; - -#define REDGRAPES_TASK_PROPERTIES dispatch::cuda::CudaTaskProperties, scheduler::SchedulingTagProperties - -#include -#include -#include -#include -#include -#include -#include - -namespace rg = redGrapes; - struct Color { float r, g, b; @@ -70,18 +64,27 @@ __global__ void mandelbrot( out[index] = Color{cosf(float(i) / 7.0), cosf(2.0 + float(i) / 11.0), cosf(4.0 + float(i) / 13.0)}; } -int main() +struct CudaTag { - auto default_scheduler = std::make_shared(4 /* number of CPU workers */); +}; + +using RGTask = redGrapes::Task; - auto cuda_scheduler = std::make_shared( - [](rg::Task const& t) { return t.required_scheduler_tags.test(SCHED_CUDA); }, - 4 /* number of cuda streams */ - ); +int main() +{ + spdlog::set_level(spdlog::level::trace); + spdlog::set_pattern("[thread %t] %^[%l]%$ %v"); - rg::idle = [cuda_scheduler] { cuda_scheduler->poll(); }; + auto rg = redGrapes::init( + redGrapes::SchedulerDescription( + std::make_shared>(), + CudaTag{}), + redGrapes::SchedulerDescription( + std::make_shared>>( + 4), + redGrapes::DefaultTag{})); - rg::init(rg::scheduler::make_tag_match_scheduler().add({}, default_scheduler).add({SCHED_CUDA}, cuda_scheduler)); + auto& cudaSched = rg.getScheduler(); double mid_x = 0.41820187155955555; double mid_y = 0.32743154895555555; @@ -90,10 +93,10 @@ int main() size_t height = 4096; size_t area = width * height; - rg::IOResource host_buffer; - rg::IOResource device_buffer; + redGrapes::IOResource host_buffer; + redGrapes::IOResource device_buffer; - rg::emplace_task( + rg.emplace_task( [area](auto host_buffer) { void* ptr; @@ -102,7 +105,7 @@ int main() }, host_buffer.write()); - rg::emplace_task( + rg.emplace_task( [area](auto device_buffer) { void* ptr; @@ -124,52 +127,55 @@ int main() /* * calculate picture */ - rg::emplace_task( - [width, height, area, i, mid_x, mid_y, w](auto device_buffer) - { - double begin_x = mid_x - w; - double end_x = mid_x + w; - double begin_y = mid_y - w; - double end_y = mid_y + w; - - dim3 threadsPerBlock(8, 8); - dim3 numBlocks(width / threadsPerBlock.x, height / threadsPerBlock.y); - - mandelbrot<<>>( - begin_x, - end_x, - begin_y, - end_y, - width, - height, - *device_buffer); - std::cout << "launched kernel to stream " << rg::dispatch::cuda::current_stream << std::endl; - }, - rg::TaskProperties::Builder().scheduling_tags({SCHED_CUDA}), - device_buffer.write()); + rg.emplace_task( + [width, height, area, i, mid_x, mid_y, w, &cudaSched](auto device_buffer) + { + double begin_x = mid_x - w; + double end_x = mid_x + w; + double begin_y = mid_y - w; + double end_y = mid_y + w; + + dim3 threadsPerBlock(8, 8); + dim3 numBlocks(width / threadsPerBlock.x, height / threadsPerBlock.y); + + auto current_stream = cudaSched.getCudaStream(0); + mandelbrot<<>>( + begin_x, + end_x, + begin_y, + end_y, + width, + height, + *device_buffer); + std::cout << "launched kernel to stream " << current_stream << std::endl; + }, + device_buffer.write()) + .cuda_stream_index(0u); /* * copy data */ - rg::emplace_task( - [area](auto host_buffer, auto device_buffer) - { - cudaMemcpyAsync( - *host_buffer, - *device_buffer, - area * sizeof(Color), - cudaMemcpyDeviceToHost, - rg::dispatch::cuda::current_stream); - std::cout << "launched memcpy to stream " << rg::dispatch::cuda::current_stream << std::endl; - }, - rg::TaskProperties::Builder().scheduling_tags({SCHED_CUDA}), - host_buffer.write(), - device_buffer.read()); + rg.emplace_task( + [area, &cudaSched](auto host_buffer, auto device_buffer) + { + auto current_stream = cudaSched.getCudaStream(0); + cudaMemcpyAsync( + *host_buffer, + *device_buffer, + area * sizeof(Color), + cudaMemcpyDeviceToHost, + current_stream); + std::cout << "launched memcpy to stream " << current_stream << std::endl; + }, + host_buffer.write(), + device_buffer.read()) + .cuda_stream_index(0u); + ; /* * write png */ - rg::emplace_task( + rg.emplace_task( [width, height, i](auto host_buffer) { std::stringstream step; @@ -179,9 +185,9 @@ int main() pngwriter png(width, height, 0, filename.c_str()); png.setcompressionlevel(9); - for(int y = 0; y < height; ++y) + for(size_t y = 0; y < height; ++y) { - for(int x = 0; x < width; ++x) + for(size_t x = 0; x < width; ++x) { auto& color = (*host_buffer)[x + y * width]; png.plot(x + 1, height - y, color.r, color.g, color.b); @@ -194,7 +200,7 @@ int main() host_buffer.read()); } - rg::emplace_task([](auto b) {}, host_buffer.write()).get(); + rg.emplace_task([]([[maybe_unused]] auto b) {}, host_buffer.write()).get(); auto t2 = std::chrono::high_resolution_clock::now(); std::cout << "runtime: " << std::chrono::duration_cast(t2 - t1).count() << " μs" @@ -203,7 +209,9 @@ int main() /* * cleanup */ - rg::emplace_task([](auto host_buffer) { cudaFreeHost(*host_buffer); }, host_buffer.write()); + rg.emplace_task([](auto host_buffer) { cudaFreeHost(*host_buffer); }, host_buffer.write()) + .cuda_stream_index(0u); - rg::emplace_task([](auto device_buffer) { cudaFree(*device_buffer); }, device_buffer.write()); + rg.emplace_task([](auto device_buffer) { cudaFree(*device_buffer); }, device_buffer.write()) + .cuda_stream_index(0u); } diff --git a/examples/game_of_life.cpp b/examples/game_of_life.cpp index 592bb0b0..5d42da76 100644 --- a/examples/game_of_life.cpp +++ b/examples/game_of_life.cpp @@ -1,4 +1,4 @@ -/* Copyright 2019 Michael Sippel, Sergei Bastrakov +/* Copyright 2019-2024 Michael Sippel, Sergei Bastrakov, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -9,17 +9,14 @@ * @file examples/game_of_life.cpp */ +#include "redGrapes/resource/fieldresource.hpp" + #include -#include -#include -#include -#include #include #include #include #include -#include struct Vec2 { @@ -52,11 +49,12 @@ int main(int, char*[]) spdlog::set_level(spdlog::level::trace); spdlog::set_pattern("[thread %t] %^[%l]%$ %v"); - redGrapes::init(4); + auto rg = redGrapes::init(4); using Buffer = std::array, size.y + 2>; - std::vector> buffers; + using TaskType = decltype(rg)::RGTask; + std::vector> buffers; for(size_t i = 0; i < 4; ++i) buffers.emplace_back(new Buffer()); @@ -64,7 +62,7 @@ int main(int, char*[]) int current = 0; // initialization - redGrapes::emplace_task( + rg.emplace_task( [](auto buf) { std::default_random_engine generator; @@ -81,7 +79,7 @@ int main(int, char*[]) int next = (current + 1) % buffers.size(); // copy borders - redGrapes::emplace_task( + rg.emplace_task( [](auto buf) { for(size_t x = 0; x < size.x + 2; ++x) @@ -99,26 +97,26 @@ int main(int, char*[]) buffers[current].write()); // print buffer - redGrapes::emplace_task( - [](auto buf) - { - for(size_t x = 1; x < size.x; ++x) - { - for(size_t y = 1; y < size.y; ++y) - { - std::cout << ((buf[{x, y}] == ALIVE) ? "" : "") << " "; - } - std::cout << "" << std::endl; - } - std::cout << std::endl; - }, - buffers[current].read()) + rg.emplace_task( + [](auto buf) + { + for(size_t x = 1; x < size.x; ++x) + { + for(size_t y = 1; y < size.y; ++y) + { + std::cout << ((buf[{x, y}] == ALIVE) ? "" : "") << " "; + } + std::cout << "" << std::endl; + } + std::cout << std::endl; + }, + buffers[current].read()) .get(); // calculate next step for(size_t x = 1; x <= size.x; x += chunk_size.x) for(size_t y = 1; y <= size.y; y += chunk_size.y) - redGrapes::emplace_task( + rg.emplace_task( [x, y](auto dst, auto src) { for(int xi = 0; xi < chunk_size.x; ++xi) @@ -132,8 +130,6 @@ int main(int, char*[]) current = next; } - redGrapes::finalize(); - SPDLOG_DEBUG("END!!!!"); return 0; diff --git a/examples/mpi.cpp b/examples/mpi.cpp index 94867e2c..8712c924 100644 --- a/examples/mpi.cpp +++ b/examples/mpi.cpp @@ -1,12 +1,21 @@ -#include -#include +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#define ENABLE_WORKSTEALING 1 + +#include +#include #include #include #include -#include -#include +#include +#include +#include -namespace rg = redGrapes; +#include /** * This example shows how to use MPI with redGrapes. @@ -28,78 +37,80 @@ namespace rg = redGrapes; * For the iteration, double buffering is used. */ +enum SchedulerTags +{ + SCHED_MPI, + SCHED_CUDA +}; + struct MPIConfig { int world_rank; int world_size; }; +struct MPITag +{ +}; + +struct UselessWorkers +{ +}; + int main() { spdlog::set_pattern("[thread %t] %^[%l]%$ %v"); spdlog::set_level(spdlog::level::trace); + using RGTask = redGrapes::Task<>; - /* - int prov; - MPI_Init_thread( nullptr, nullptr, MPI_THREAD_MULTIPLE, &prov ); - assert( prov == MPI_THREAD_MULTIPLE ); - */ - - MPI_Init(nullptr, nullptr); - - auto default_scheduler = std::make_shared(); - auto mpi_request_pool = std::make_shared(); - - hwloc_obj_t obj = hwloc_get_obj_by_type(redGrapes::SingletonContext::get().hwloc_ctx.topology, HWLOC_OBJ_PU, 1); - rg::memory::ChunkedBumpAlloc mpi_alloc( - rg::memory::HwlocAlloc(redGrapes::SingletonContext::get().hwloc_ctx, obj)); - auto mpi_worker = std::make_shared( - mpi_alloc, - redGrapes::SingletonContext::get().hwloc_ctx, - obj, - 4); - - // initialize main thread to execute tasks from the mpi-queue and poll - rg::SingletonContext::get().idle = [mpi_worker, mpi_request_pool] - { - mpi_request_pool->poll(); + auto rg = redGrapes::init( + redGrapes::SchedulerDescription( + std::make_shared>>( + 17), + UselessWorkers{}), + redGrapes::SchedulerDescription( + std::make_shared>>( + 4), + redGrapes::DefaultTag{}), + redGrapes::SchedulerDescription( + std::make_shared>(), + MPITag{})); - redGrapes::Task* task; - if(task = mpi_worker->ready_queue.pop()) - redGrapes::SingletonContext::get().execute_task(*task); + auto& mpiSched = rg.getScheduler(); - while(mpi_worker->init_dependencies(task, true)) - if(task) - { - redGrapes::SingletonContext::get().execute_task(*task); - break; - } - }; + auto mpi_request_pool = mpiSched.getRequestPool(); - rg::init(4, rg::scheduler::make_tag_match_scheduler().add({}, default_scheduler).add({SCHED_MPI}, mpi_worker)); + int prov; + + // initialize MPI + rg.emplace_task( + [&prov]() + { + MPI_Init_thread(nullptr, nullptr, MPI_THREAD_FUNNELED, &prov); + assert(prov == MPI_THREAD_FUNNELED); + }); // initialize MPI config - rg::IOResource mpi_config; - rg::emplace_task( + redGrapes::IOResource mpi_config; + rg.emplace_task( [](auto config) { MPI_Comm_rank(MPI_COMM_WORLD, &config->world_rank); MPI_Comm_size(MPI_COMM_WORLD, &config->world_size); }, - mpi_config.write()) - .scheduling_tags(std::bitset<64>().set(SCHED_MPI)); + mpi_config.write()); // main loop - rg::FieldResource> field[2] = { - rg::FieldResource>(new std::array()), - rg::FieldResource>(new std::array()), + redGrapes::FieldResource, RGTask> field[2] = { + redGrapes::FieldResource, RGTask>(new std::array()), + redGrapes::FieldResource, RGTask>(new std::array()), }; int current = 0; // initialize - rg::emplace_task( + rg.emplace_task( [](auto buf, auto mpi_config) { int offset = 3 * mpi_config->world_rank; @@ -109,7 +120,7 @@ int main() field[current].write(), mpi_config.read()); - for(size_t i = 0; i < 1; ++i) + for(size_t j = 0; j < 4; ++j) { int next = (current + 1) % 2; @@ -118,45 +129,43 @@ int main() */ // Send - rg::emplace_task( - [i, current, mpi_request_pool](auto field, auto mpi_config) - { - int dst = (mpi_config->world_rank + 1) % mpi_config->world_size; - - MPI_Request request; - MPI_Isend(&field[{3}], sizeof(int), MPI_CHAR, dst, current, MPI_COMM_WORLD, &request); - - mpi_request_pool->get_status(request); - }, - field[current].at({3}).read(), - mpi_config.read()) - .scheduling_tags({SCHED_MPI}) + rg.emplace_task( + [current, mpi_request_pool](auto field, auto mpi_config) + { + int dst = (mpi_config->world_rank + 1) % mpi_config->world_size; + + MPI_Request request; + MPI_Isend(&field[{3}], sizeof(int), MPI_CHAR, dst, current, MPI_COMM_WORLD, &request); + + mpi_request_pool->get_status(request); + }, + field[current].at({3}).read(), + mpi_config.read()) .enable_stack_switching(); // Receive - rg::emplace_task( - [i, current, mpi_request_pool](auto field, auto mpi_config) - { - int src = (mpi_config->world_rank - 1) % mpi_config->world_size; + rg.emplace_task( + [current, mpi_request_pool](auto field, auto mpi_config) + { + int src = (mpi_config->world_rank - 1) % mpi_config->world_size; - MPI_Request request; - MPI_Irecv(&field[{0}], sizeof(int), MPI_CHAR, src, current, MPI_COMM_WORLD, &request); + MPI_Request request; + MPI_Irecv(&field[{0}], sizeof(int), MPI_CHAR, src, current, MPI_COMM_WORLD, &request); - MPI_Status status = mpi_request_pool->get_status(request); + MPI_Status status = mpi_request_pool->get_status(request); - int recv_data_count; - MPI_Get_count(&status, MPI_CHAR, &recv_data_count); - }, - field[current].at({0}).write(), - mpi_config.read()) - .scheduling_tags({SCHED_MPI}) + int recv_data_count; + MPI_Get_count(&status, MPI_CHAR, &recv_data_count); + }, + field[current].at({0}).write(), + mpi_config.read()) .enable_stack_switching(); /* * Compute iteration */ for(size_t i = 1; i < field[current]->size(); ++i) - rg::emplace_task( + rg.emplace_task( [i](auto dst, auto src) { dst[{i}] = src[{i - 1}]; }, field[next].at({i}).write(), field[current].at({i - 1}).read()); @@ -164,10 +173,10 @@ int main() /* * Write Output */ - rg::emplace_task( - [i](auto buf, auto mpi_config) + rg.emplace_task( + [j](auto buf, auto mpi_config) { - std::cout << "Step[" << i << "], rank[" << mpi_config->world_rank << "] :: "; + std::cout << "Step[" << j << "], rank[" << mpi_config->world_rank << "] :: "; for(size_t i = 0; i < buf->size(); ++i) std::cout << buf[{i}] << "; "; std::cout << std::endl; @@ -178,7 +187,5 @@ int main() current = next; } - rg::emplace_task([](auto m) { MPI_Finalize(); }, mpi_config.write()).scheduling_tags({SCHED_MPI}); - - rg::finalize(); + rg.emplace_task([]([[maybe_unused]] auto m) { MPI_Finalize(); }, mpi_config.write()); } diff --git a/redGrapes/SchedulerDescription.hpp b/redGrapes/SchedulerDescription.hpp new file mode 100644 index 00000000..279330e3 --- /dev/null +++ b/redGrapes/SchedulerDescription.hpp @@ -0,0 +1,40 @@ +/* Copyright 2024 Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include + +namespace redGrapes +{ + + struct DefaultTag + { + }; + + template + concept C_Exec = requires(T execDesc) + { + typename T::Key; + typename T::ValueType; + {execDesc.scheduler}; + }; + + template + struct SchedulerDescription + { + using Key = TTag; + using ValueType = TScheduler; + + SchedulerDescription(std::shared_ptr scheduler, TTag = DefaultTag{}) : scheduler{scheduler} + { + } + + std::shared_ptr scheduler; + }; + +} // namespace redGrapes diff --git a/redGrapes/TaskCtx.hpp b/redGrapes/TaskCtx.hpp new file mode 100644 index 00000000..42e5a34c --- /dev/null +++ b/redGrapes/TaskCtx.hpp @@ -0,0 +1,84 @@ +/* Copyright 2024 Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "redGrapes/TaskFreeCtx.hpp" +#include "redGrapes/scheduler/event.hpp" +#include "redGrapes/task/task_space.hpp" + +#include + +#include + +namespace redGrapes +{ + template + struct TaskCtx + { + //! pause the currently running task at least until event is reached + // else is supposed to be called when .get() is called on emplace task, which calls the future .get(), so there + // is no current task at that time, unless this is in a child task space. we can assert(event.task != 0); + static void yield(scheduler::EventPtr event) + { + if(current_task) + { + while(!event->is_reached()) + current_task->yield(event); + } + else + { + event->waker_id = event.task->scheduler_p->getNextWorkerID() + 1; + while(!event->is_reached()) + TaskFreeCtx::idle(); + } + } + + /*! Create an event on which the termination of the current task depends. + * A task must currently be running. + * + * @return Handle to flag the event with `reach_event` later. + * nullopt if there is no task running currently + */ + static std::optional> create_event() + { + if(current_task) + return current_task->make_event(); + else + return std::nullopt; + } + + static std::shared_ptr> current_task_space() + { + if(current_task) + { + if(!current_task->children) + { + auto task_space = std::make_shared>(current_task); + SPDLOG_TRACE("create child space = {}", (void*) task_space.get()); + current_task->children = task_space; + } + + return current_task->children; + } + else + return root_space; + } + + static unsigned scope_depth() + { + if(auto ts = current_task_space()) + return ts->depth; + else + return 0; + } + + static inline thread_local TTask* current_task; + static inline std::shared_ptr> root_space; + }; + +} // namespace redGrapes diff --git a/redGrapes/TaskFreeCtx.hpp b/redGrapes/TaskFreeCtx.hpp new file mode 100644 index 00000000..d32bd212 --- /dev/null +++ b/redGrapes/TaskFreeCtx.hpp @@ -0,0 +1,46 @@ +/* Copyright 2024 Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "redGrapes/memory/chunked_bump_alloc.hpp" +#include "redGrapes/memory/hwloc_alloc.hpp" + +#include +#include +#include +#include + +namespace redGrapes +{ + + using WorkerId = unsigned; + + // seperated to not templatize allocators with Task type + struct WorkerAllocPool + { + public: + inline memory::ChunkedBumpAlloc& get_alloc(WorkerId worker_id) + { + assert(worker_id < allocs.size()); + return allocs[worker_id]; + } + + std::vector> allocs; + }; + + struct TaskFreeCtx + { + static inline unsigned n_workers; + static inline unsigned n_pus; + static inline HwlocContext hwloc_ctx; + static inline std::shared_ptr worker_alloc_pool; + + static inline thread_local std::function idle = [] {}; + static inline thread_local std::optional current_worker_id; + }; +} // namespace redGrapes diff --git a/redGrapes/dispatch/cuda/task_properties.hpp b/redGrapes/dispatch/cuda/cuda_task_properties.hpp similarity index 76% rename from redGrapes/dispatch/cuda/task_properties.hpp rename to redGrapes/dispatch/cuda/cuda_task_properties.hpp index e8532fc0..f20634e6 100644 --- a/redGrapes/dispatch/cuda/task_properties.hpp +++ b/redGrapes/dispatch/cuda/cuda_task_properties.hpp @@ -1,4 +1,4 @@ -/* Copyright 2020 Michael Sippel +/* Copyright 2024 Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,6 +7,8 @@ #pragma once +#include + namespace redGrapes { namespace dispatch @@ -16,7 +18,7 @@ namespace redGrapes struct CudaTaskProperties { - std::optional cuda_event; + std::optional m_cuda_stream_idx; CudaTaskProperties() { @@ -30,6 +32,12 @@ namespace redGrapes Builder(PropertiesBuilder& b) : builder(b) { } + + PropertiesBuilder& cuda_stream_index(unsigned cuda_stream_idx) + { + *(builder.task->m_cuda_stream_idx) = cuda_stream_idx; + return builder; + } }; struct Patch diff --git a/redGrapes/dispatch/cuda/cuda_worker.hpp b/redGrapes/dispatch/cuda/cuda_worker.hpp new file mode 100644 index 00000000..ee861848 --- /dev/null +++ b/redGrapes/dispatch/cuda/cuda_worker.hpp @@ -0,0 +1,267 @@ +/* Copyright 2024 Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "redGrapes/TaskCtx.hpp" +#include "redGrapes/dispatch/cuda/cuda_task_properties.hpp" +#include "redGrapes/dispatch/cuda/event_pool.hpp" +#include "redGrapes/scheduler/event.hpp" +#include "redGrapes/sync/cv.hpp" +#include "redGrapes/task/queue.hpp" + +#include +#include +#include + +#include + +namespace redGrapes::dispatch::cuda +{ + struct CudaStreamWrapper + { + cudaStream_t cuda_stream; + + CudaStreamWrapper() + { + cudaStreamCreate(&cuda_stream); + } + + CudaStreamWrapper(CudaStreamWrapper const& other) + { + spdlog::warn("CudaStreamWrapper copy constructor called!"); + } + + ~CudaStreamWrapper() + { + cudaStreamDestroy(cuda_stream); + } + }; + + // this class is not thread safe + // Stream dispatcher + template + struct CudaWorker + + { + using task_type = TTask; + + WorkerId id; + std::vector streams; + EventPool event_pool; + + /*! if true, the thread shall stop + * instead of waiting when it is out of jobs + */ + std::atomic_bool m_stop{false}; + std::atomic task_count{0}; + + + std::queue>> events; + std::recursive_mutex mutex; + + //! condition variable for waiting if queue is empty + CondVar cv; + + static constexpr size_t queue_capacity = 128; + task::Queue emplacement_queue{queue_capacity}; + task::Queue ready_queue{queue_capacity}; + + CudaWorker(WorkerId worker_id) : id(worker_id) + { + } + + CudaWorker(WorkerId worker_id, unsigned num_streams) : id{worker_id}, streams{num_streams} + { + } + + inline scheduler::WakerId get_waker_id() + { + return id + 1; + } + + inline bool wake() + { + return cv.notify(); + } + + void stop() + { + SPDLOG_TRACE("Worker::stop()"); + m_stop.store(true, std::memory_order_release); + wake(); + } + + /* adds a new task to the emplacement queue + * and wakes up thread to kickstart execution + */ + inline void dispatch_task(TTask& task) + { + emplacement_queue.push(&task); + wake(); + } + + inline void execute_task(TTask& task) + { + TRACE_EVENT("Worker", "dispatch task"); + + SPDLOG_DEBUG("cuda thread dispatch: execute task {}", task.task_id); + assert(task.is_ready()); + std::lock_guard lock(mutex); + + TaskCtx::current_task = &task; + + // run the code that calls the CUDA API and submits work to *task->m_cuda_stream_idx + auto event = task(); + + cudaEvent_t cuda_event = event_pool.alloc(); + cudaEventRecord(cuda_event, streams[*(task->m_cuda_stream_idx)].cuda_stream); + auto my_event = TaskCtx::create_event(); + events.push(std::make_pair(cuda_event, *my_event)); + SPDLOG_TRACE( + "CudaStreamDispatcher {}: recorded event {}", + streams[*(task->m_cuda_stream_idx)].cuda_stream, + cuda_event); + + // TODO figure out the correct position for this + task.get_pre_event().notify(); + + if(event) + { + event->get_event().waker_id = get_waker_id(); + task.sg_pause(*event); + + task.pre_event.up(); + task.get_pre_event().notify(); + } + else + task.get_post_event().notify(); + + TaskCtx::current_task = nullptr; + } + + /* repeatedly try to find and execute tasks + * until stop-flag is triggered by stop() + */ + void work_loop() + { + SPDLOG_TRACE("Worker {} start work_loop()", this->id); + while(!this->m_stop.load(std::memory_order_consume)) + { + // this->cv.wait(); // TODO fix this by fixing event_ptr notify to wake + + while(TTask* task = this->gather_task()) + { + execute_task(*task); + poll(); // TODO fix where to poll + } + poll(); + } + SPDLOG_TRACE("Worker {} end work_loop()", this->id); + } + + /* find a task that shall be executed next + */ + TTask* gather_task() + { + { + TRACE_EVENT("Worker", "gather_task()"); + TTask* task = nullptr; + + /* STAGE 1: + * + * first, execute all tasks in the ready queue + */ + SPDLOG_TRACE("Worker {}: consume ready queue", id); + if((task = ready_queue.pop())) + return task; + + /* STAGE 2: + * + * after the ready queue is fully consumed, + * try initializing new tasks until one + * of them is found to be ready + */ + SPDLOG_TRACE("Worker {}: try init new tasks", id); + while(this->init_dependencies(task, true)) + if(task) + return task; + + return task; + } + } + + /*! take a task from the emplacement queue and initialize it, + * @param t is set to the task if the new task is ready, + * @param t is set to nullptr if the new task is blocked. + * @param claimed if set, the new task will not be actiated, + * if it is false, activate_task will be called by notify_event + * + * @return false if queue is empty + */ + bool init_dependencies(TTask*& t, bool claimed = true) + { + { + TRACE_EVENT("Worker", "init_dependencies()"); + if(TTask* task = emplacement_queue.pop()) + { + SPDLOG_DEBUG("init task {}", task->task_id); + + task->pre_event.up(); + task->init_graph(); + + if(task->get_pre_event().notify(claimed)) + t = task; + else + { + t = nullptr; + } + + return true; + } + else + return false; + } + } + + //! checks if some cuda calls finished and notify the redGrapes manager + void poll() + { + std::lock_guard lock(mutex); + if(!events.empty()) + { + auto& cuda_event = events.front().first; + auto& event = events.front().second; + + if(cudaEventQuery(cuda_event) == cudaSuccess) + { + SPDLOG_TRACE("cuda event {} ready", cuda_event); + event_pool.free(cuda_event); + event.notify(); + + events.pop(); + } + } + } + }; + +} // namespace redGrapes::dispatch::cuda + +template<> +struct fmt::formatter +{ + constexpr auto parse(format_parse_context& ctx) + { + return ctx.begin(); + } + + template + auto format(redGrapes::dispatch::cuda::CudaTaskProperties const& prop, FormatContext& ctx) + { + return fmt::format_to(ctx.out(), "\"cuda_stream_idx\" : {}", *(prop.m_cuda_stream_idx)); + } +}; diff --git a/redGrapes/dispatch/cuda/event_pool.hpp b/redGrapes/dispatch/cuda/event_pool.hpp index a8123117..87aabec1 100644 --- a/redGrapes/dispatch/cuda/event_pool.hpp +++ b/redGrapes/dispatch/cuda/event_pool.hpp @@ -1,4 +1,4 @@ -/* Copyright 2020 Michael Sippel +/* Copyright 2020-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,6 +7,8 @@ #pragma once +#include + #include #include @@ -28,12 +30,6 @@ namespace redGrapes { } - static EventPool& get() - { - static EventPool singleton; - return singleton; - } - ~EventPool() { std::lock_guard lock(mutex); diff --git a/redGrapes/dispatch/cuda/scheduler.hpp b/redGrapes/dispatch/cuda/scheduler.hpp deleted file mode 100644 index 20d8f4a4..00000000 --- a/redGrapes/dispatch/cuda/scheduler.hpp +++ /dev/null @@ -1,194 +0,0 @@ -/* Copyright 2020 Michael Sippel - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -#pragma once - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -namespace redGrapes -{ - namespace dispatch - { - namespace cuda - { - - thread_local cudaStream_t current_stream; - - // this class is not thread safe - template - struct CudaStreamDispatcher - { - cudaStream_t cuda_stream; - std::recursive_mutex mutex; - std::queue> events; - - CudaStreamDispatcher() - { - cudaStreamCreate(&cuda_stream); - } - - CudaStreamDispatcher(CudaStreamDispatcher const& other) - { - spdlog::warn("CudaStreamDispatcher copy constructor called!"); - } - - ~CudaStreamDispatcher() - { - cudaStreamDestroy(cuda_stream); - } - - void poll() - { - std::lock_guard lock(mutex); - if(!events.empty()) - { - auto& cuda_event = events.front().first; - auto& event = events.front().second; - - if(cudaEventQuery(cuda_event) == cudaSuccess) - { - SPDLOG_TRACE("cuda event {} ready", cuda_event); - EventPool::get().free(cuda_event); - event.notify(); - - events.pop(); - } - } - } - - void dispatch_task(Task& task) - { - std::lock_guard lock(mutex); - - for(auto predecessor : task.in_edges) - { - SPDLOG_TRACE("cudaDispatcher: consider predecessor \"{}\"", predecessor->label); - - if(auto cuda_event = predecessor->cuda_event) - { - SPDLOG_TRACE( - "cudaDispatcher: task {} \"{}\" wait for {}", - task.task_id, - task.label, - *cuda_event); - - cudaStreamWaitEvent(cuda_stream, *cuda_event, 0); - } - } - - SPDLOG_TRACE("CudaScheduler: start {}", task_id); - - current_stream = cuda_stream; - - // run the code that calls the CUDA API and submits work to current_stream - task->run(); - - cudaEvent_t cuda_event = EventPool::get().alloc(); - cudaEventRecord(cuda_event, cuda_stream); - task->cuda_event = cuda_event; - - task->get_pre_event().notify(); - - SPDLOG_TRACE("CudaStreamDispatcher {}: recorded event {}", cuda_stream, cuda_event); - events.push(std::make_pair(cuda_event, task->get_post_event())); - } - }; - - struct CudaScheduler : redGrapes::scheduler::IScheduler - { - private: - bool recording; - bool cuda_graph_enabled; - - std::recursive_mutex mutex; - unsigned int current_stream; - std::vector> streams; - - std::function is_cuda_task; - - public: - CudaScheduler( - std::function is_cuda_task, - size_t stream_count = 1, - bool cuda_graph_enabled = false) - : is_cuda_task(is_cuda_task) - , current_stream(0) - , cuda_graph_enabled(cuda_graph_enabled) - { - // reserve to avoid copy constructor of CudaStreamDispatcher - streams.reserve(stream_count); - - for(size_t i = 0; i < stream_count; ++i) - streams.emplace_back(); - - SPDLOG_TRACE("CudaScheduler: use {} streams", streams.size()); - } - - //! submits the call to the cuda runtime - void activate_task(Task& task) - { - unsigned int stream_id = current_stream; - current_stream = (current_stream + 1) % streams.size(); - - SPDLOG_TRACE("Dispatch Cuda task {} \"{}\" on stream {}", task.task_id, task.label, stream_id); - streams[stream_id].dispatch_task(task); - } - - //! checks if some cuda calls finished and notify the redGrapes manager - void poll() - { - for(size_t stream_id = 0; stream_id < streams.size(); ++stream_id) - streams[stream_id].poll(); - } - - /*! whats the task dependency type for the edge a -> b (task a precedes task b) - * @return true if task b depends on the pre event of task a, false if task b depends on the post event - * of task b. - */ - bool task_dependency_type(Task const& a, Task const& b) - { - assert(is_cuda_task(b)); - return is_cuda_task(a); - } - }; - - } // namespace cuda - - } // namespace dispatch - -} // namespace redGrapes - -template<> -struct fmt::formatter -{ - constexpr auto parse(format_parse_context& ctx) - { - return ctx.begin(); - } - - template - auto format(redGrapes::dispatch::cuda::CudaTaskProperties const& prop, FormatContext& ctx) - { - if(auto e = prop.cuda_event) - return fmt::format_to(ctx.out(), "\"cuda_event\" : {}", *e); - else - return fmt::format_to(ctx.out(), "\"cuda_event\" : null"); - } -}; diff --git a/redGrapes/dispatch/cupla/scheduler.hpp b/redGrapes/dispatch/cupla/scheduler.hpp index 7491567c..17bb36df 100644 --- a/redGrapes/dispatch/cupla/scheduler.hpp +++ b/redGrapes/dispatch/cupla/scheduler.hpp @@ -1,4 +1,4 @@ -/* Copyright 2020 Michael Sippel +/* Copyright 2020-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,11 +7,9 @@ #pragma once -#include -#include -#include -#include -#include +#include "redGrapes/dispatch/cupla/event_pool.hpp" +#include "redGrapes/dispatch/cupla/task_properties.hpp" +#include "redGrapes/scheduler/scheduler.hpp" #include #include @@ -111,7 +109,8 @@ namespace redGrapes } }; - struct CuplaScheduler : redGrapes::scheduler::IScheduler + template + struct CuplaScheduler : redGrapes::scheduler::IScheduler { private: bool recording; @@ -119,13 +118,13 @@ namespace redGrapes std::recursive_mutex mutex; unsigned int current_stream; - std::vector> streams; + std::vector> streams; - std::function is_cupla_task; + std::function is_cupla_task; public: CuplaScheduler( - std::function is_cupla_task, + std::function is_cupla_task, size_t stream_count = 1, bool cupla_graph_enabled = false) : is_cupla_task(is_cupla_task) @@ -142,7 +141,7 @@ namespace redGrapes } //! submits the call to the cupla runtime - void activate_task(Task& task) + void activate_task(TTask& task) { unsigned int stream_id = current_stream; current_stream = (current_stream + 1) % streams.size(); @@ -162,7 +161,7 @@ namespace redGrapes * @return true if task b depends on the pre event of task a, false if task b depends on the post event * of task b. */ - bool task_dependency_type(Task const& a, Task const& b) + bool task_dependency_type(TTask const& a, TTask const& b) { assert(is_cupla_task(b)); return is_cupla_task(a); diff --git a/redGrapes/dispatch/dispatcher.hpp b/redGrapes/dispatch/dispatcher.hpp deleted file mode 100644 index 5896a741..00000000 --- a/redGrapes/dispatch/dispatcher.hpp +++ /dev/null @@ -1,9 +0,0 @@ -/* -struct IDispatcher -{ - virtual ~IDispatcher() = 0; - - virtual void dispatch_task( TaskVertexPtr task_vertex ) = 0; - virtual void notify() = 0; -}; -*/ diff --git a/redGrapes/dispatch/mpi/mpiWorker.hpp b/redGrapes/dispatch/mpi/mpiWorker.hpp new file mode 100644 index 00000000..7230161a --- /dev/null +++ b/redGrapes/dispatch/mpi/mpiWorker.hpp @@ -0,0 +1,191 @@ +/* Copyright 2024 Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once +#include "redGrapes/TaskFreeCtx.hpp" +#include "redGrapes/dispatch/mpi/request_pool.hpp" +#include "redGrapes/sync/cv.hpp" +#include "redGrapes/task/queue.hpp" + +#include + +namespace redGrapes +{ + namespace dispatch + { + namespace mpi + { + + template + struct MPIWorker + { + using task_type = TTask; + std::shared_ptr> requestPool; + WorkerId id; + + /*! if true, the thread shall stop + * instead of waiting when it is out of jobs + */ + std::atomic_bool m_stop{false}; + std::atomic task_count{0}; + + //! condition variable for waiting if queue is empty + CondVar cv; + + static constexpr size_t queue_capacity = 128; + task::Queue emplacement_queue{queue_capacity}; + task::Queue ready_queue{queue_capacity}; + + MPIWorker(WorkerId worker_id) : id(worker_id) + { + requestPool = std::make_shared>(); + } + + ~MPIWorker() + { + } + + inline scheduler::WakerId get_waker_id() + { + return id + 1; + } + + inline bool wake() + { + return cv.notify(); + } + + void stop() + { + SPDLOG_TRACE("Worker::stop()"); + m_stop.store(true, std::memory_order_release); + wake(); + } + + /* adds a new task to the emplacement queue + * and wakes up thread to kickstart execution + */ + inline void dispatch_task(TTask& task) + { + emplacement_queue.push(&task); + wake(); + } + + inline void execute_task(TTask& task) + { + TRACE_EVENT("Worker", "dispatch task"); + + SPDLOG_DEBUG("thread dispatch: execute task {}", task.task_id); + assert(task.is_ready()); + + task.get_pre_event().notify(); + TaskCtx::current_task = &task; + + auto event = task(); + + if(event) + { + event->get_event().waker_id = get_waker_id(); + task.sg_pause(*event); + + task.pre_event.up(); + task.get_pre_event().notify(); + } + else + task.get_post_event().notify(); + + TaskCtx::current_task = nullptr; + } + + /* find a task that shall be executed next + */ + TTask* gather_task() + { + { + TRACE_EVENT("Worker", "gather_task()"); + TTask* task = nullptr; + + /* STAGE 1: + * + * first, execute all tasks in the ready queue + */ + SPDLOG_TRACE("Worker {}: consume ready queue", id); + if((task = ready_queue.pop())) + return task; + + /* STAGE 2: + * + * after the ready queue is fully consumed, + * try initializing new tasks until one + * of them is found to be ready + */ + SPDLOG_TRACE("Worker {}: try init new tasks", id); + while(this->init_dependencies(task, true)) + if(task) + return task; + + return task; + } + } + + /*! take a task from the emplacement queue and initialize it, + * @param t is set to the task if the new task is ready, + * @param t is set to nullptr if the new task is blocked. + * @param claimed if set, the new task will not be actiated, + * if it is false, activate_task will be called by notify_event + * + * @return false if queue is empty + */ + bool init_dependencies(TTask*& t, bool claimed = true) + { + { + TRACE_EVENT("Worker", "init_dependencies()"); + if(TTask* task = emplacement_queue.pop()) + { + SPDLOG_DEBUG("init task {}", task->task_id); + + task->pre_event.up(); + task->init_graph(); + + if(task->get_pre_event().notify(claimed)) + t = task; + else + { + t = nullptr; + } + + return true; + } + else + return false; + } + } + + /* repeatedly try to find and execute tasks + * until stop-flag is triggered by stop() + */ + void work_loop() + { + SPDLOG_TRACE("Worker {} start work_loop()", this->id); + while(!this->m_stop.load(std::memory_order_consume)) + { + // this->cv.wait(); // TODO fix this by fixing event_ptr notify to wake + + while(TTask* task = this->gather_task()) + { + execute_task(*task); + requestPool->poll(); // TODO fix where to poll + } + requestPool->poll(); + } + SPDLOG_TRACE("Worker {} end work_loop()", this->id); + } + }; + + } // namespace mpi + } // namespace dispatch +} // namespace redGrapes diff --git a/redGrapes/dispatch/mpi/request_pool.hpp b/redGrapes/dispatch/mpi/request_pool.hpp index 390ed7bc..b11792bd 100644 --- a/redGrapes/dispatch/mpi/request_pool.hpp +++ b/redGrapes/dispatch/mpi/request_pool.hpp @@ -1,4 +1,4 @@ -/* Copyright 2019-2020 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,30 +7,28 @@ #pragma once -#include +#include "redGrapes/TaskCtx.hpp" +#include "redGrapes/scheduler/event.hpp" #include -#include #include #include namespace redGrapes { - void yield(scheduler::EventPtr event); - std::optional create_event(); - namespace dispatch { namespace mpi { + template struct RequestPool { std::mutex mutex; std::vector requests; - std::vector events; + std::vector> events; std::vector> statuses; RequestPool() @@ -85,7 +83,7 @@ namespace redGrapes MPI_Status get_status(MPI_Request request) { auto status = memory::alloc_shared(); - auto event = *create_event(); + auto event = *TaskCtx::create_event(); // SPDLOG_TRACE("MPI RequestPool: status event = {}", (void*)event.get()); @@ -96,7 +94,7 @@ namespace redGrapes statuses.push_back(status); } - yield(event); + TaskCtx::yield(event); return *status; } diff --git a/redGrapes/dispatch/thread/worker.hpp b/redGrapes/dispatch/thread/DefaultWorker.hpp similarity index 55% rename from redGrapes/dispatch/thread/worker.hpp rename to redGrapes/dispatch/thread/DefaultWorker.hpp index 09137df2..848641a4 100644 --- a/redGrapes/dispatch/thread/worker.hpp +++ b/redGrapes/dispatch/thread/DefaultWorker.hpp @@ -1,4 +1,4 @@ -/* Copyright 2020-2023 Michael Sippel +/* Copyright 2020-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,20 +7,15 @@ #pragma once -#include -#include -#include -#include -#include +#include "redGrapes/scheduler/scheduler.hpp" +#include "redGrapes/sync/cv.hpp" +#include "redGrapes/task/queue.hpp" +#include "redGrapes/util/bitfield.hpp" #include #include #include -#include -#include -#include -#include namespace redGrapes { @@ -30,7 +25,8 @@ namespace redGrapes namespace thread { - struct WorkerThread; + template + struct WorkerPool; /*! * Creates a thread which repeatedly calls consume() @@ -38,17 +34,19 @@ namespace redGrapes * * Sleeps when no jobs are available. */ - struct Worker : redGrapes::scheduler::IScheduler + template + struct DefaultWorker { + using task_type = TTask; // private: WorkerId id; + AtomicBitfield* worker_state_p; + WorkerPool* worker_pool_p; /*! if true, the thread shall stop * instead of waiting when it is out of jobs */ std::atomic_bool m_stop{false}; - - std::atomic task_count{0}; //! condition variable for waiting if queue is empty @@ -57,24 +55,18 @@ namespace redGrapes static constexpr size_t queue_capacity = 128; public: - memory::ChunkedBumpAlloc& alloc; - HwlocContext& hwloc_ctx; - - task::Queue emplacement_queue{queue_capacity}; - task::Queue ready_queue{queue_capacity}; + task::Queue emplacement_queue{queue_capacity}; + task::Queue ready_queue{queue_capacity}; - Worker( - memory::ChunkedBumpAlloc& alloc, - HwlocContext& hwloc_ctx, - hwloc_obj_t const& obj, - WorkerId id); - virtual ~Worker(); - - inline WorkerId get_worker_id() + DefaultWorker(WorkerId worker_id, AtomicBitfield& worker_state, WorkerPool& worker_pool) + : id(worker_id) + , worker_state_p(&worker_state) + , worker_pool_p(&worker_pool) { - return id; } + ~DefaultWorker(); + inline scheduler::WakerId get_waker_id() { return id + 1; @@ -85,22 +77,18 @@ namespace redGrapes return cv.notify(); } - virtual void stop(); + void stop(); /* adds a new task to the emplacement queue * and wakes up thread to kickstart execution */ - inline void emplace_task(Task& task) + inline void dispatch_task(TTask& task) { emplacement_queue.push(&task); wake(); } - inline void activate_task(Task& task) - { - ready_queue.push(&task); - wake(); - } + inline void execute_task(TTask& task); // private: @@ -111,7 +99,7 @@ namespace redGrapes /* find a task that shall be executed next */ - Task* gather_task(); + TTask* gather_task(); /*! take a task from the emplacement queue and initialize it, * @param t is set to the task if the new task is ready, @@ -121,33 +109,11 @@ namespace redGrapes * * @return false if queue is empty */ - bool init_dependencies(Task*& t, bool claimed = true); - }; - - struct WorkerThread - : Worker - , std::enable_shared_from_this - { - std::thread thread; - - WorkerThread( - memory::ChunkedBumpAlloc& alloc, - HwlocContext& hwloc_ctx, - hwloc_obj_t const& obj, - WorkerId worker_id); - ~WorkerThread(); - - void start(); - void stop(); - - /* function the thread will execute - */ - void run(); - - void cpubind(); - void membind(); + bool init_dependencies(TTask*& t, bool claimed = true); }; } // namespace thread } // namespace dispatch } // namespace redGrapes + +#include "redGrapes/dispatch/thread/DefaultWorker.tpp" diff --git a/redGrapes/dispatch/thread/DefaultWorker.tpp b/redGrapes/dispatch/thread/DefaultWorker.tpp new file mode 100644 index 00000000..0419313b --- /dev/null +++ b/redGrapes/dispatch/thread/DefaultWorker.tpp @@ -0,0 +1,157 @@ +/* Copyright 2020-2024 Michael Sippel, Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include "redGrapes/TaskCtx.hpp" +#include "redGrapes/dispatch/thread/DefaultWorker.hpp" +#include "redGrapes/dispatch/thread/worker_pool.hpp" +#include "redGrapes/util/bitfield.hpp" +#include "redGrapes/util/trace.hpp" + +#include + +#include + +namespace redGrapes +{ + namespace dispatch + { + namespace thread + { + + template + DefaultWorker::~DefaultWorker() + { + } + + template + void DefaultWorker::stop() + { + SPDLOG_TRACE("Worker::stop()"); + m_stop.store(true, std::memory_order_release); + wake(); + } + + template + void DefaultWorker::work_loop() + { + SPDLOG_TRACE("Worker {} start work_loop()", id); + while(!m_stop.load(std::memory_order_consume)) + { + worker_state_p->set(id, dispatch::thread::WorkerState::AVAILABLE); + cv.wait(); + + while(TTask* task = this->gather_task()) + { + worker_state_p->set(id, dispatch::thread::WorkerState::BUSY); + execute_task(*task); + } + } + SPDLOG_TRACE("Worker {} end work_loop()", id); + } + + template + void DefaultWorker::execute_task(TTask& task) + { + TRACE_EVENT("Worker", "dispatch task"); + + SPDLOG_DEBUG("thread dispatch: execute task {}", task.task_id); + assert(task.is_ready()); + + task.get_pre_event().notify(); + TaskCtx::current_task = &task; + + auto event = task(); + + if(event) + { + event->get_event().waker_id = get_waker_id(); + task.sg_pause(*event); + + task.pre_event.up(); + task.get_pre_event().notify(); + } + else + task.get_post_event().notify(); + + TaskCtx::current_task = nullptr; + } + + template + TTask* DefaultWorker::gather_task() + { + TRACE_EVENT("Worker", "gather_task()"); + TTask* task = nullptr; + + /* STAGE 1: + * + * first, execute all tasks in the ready queue + */ + SPDLOG_TRACE("Worker {}: consume ready queue", id); + if((task = ready_queue.pop())) + return task; + + /* STAGE 2: + * + * after the ready queue is fully consumed, + * try initializing new tasks until one + * of them is found to be ready + */ + SPDLOG_TRACE("Worker {}: try init new tasks", id); + while(this->init_dependencies(task, true)) + if(task) + return task; + + /* set worker state to signal that we are requesting tasks + */ + worker_state_p->set(id, dispatch::thread::WorkerState::AVAILABLE); + +#ifndef ENABLE_WORKSTEALING +# define ENABLE_WORKSTEALING 1 +#endif + +#if ENABLE_WORKSTEALING + + /* STAGE 3: + * + * after all tasks from own queues are consumed, try to steal tasks + */ + SPDLOG_TRACE("Worker {}: try to steal tasks", id); + task = worker_pool_p->steal_task(*this); + +#endif + + return task; + } + + template // task with graphable + bool DefaultWorker::init_dependencies(TTask*& t, bool claimed) + { + TRACE_EVENT("Worker", "init_dependencies()"); + if(TTask* task = emplacement_queue.pop()) + { + SPDLOG_DEBUG("init task {}", task->task_id); + + task->pre_event.up(); + task->init_graph(); + + if(task->get_pre_event().notify(claimed)) + t = task; + else + { + t = nullptr; + } + + return true; + } + else + return false; + } + + } // namespace thread + } // namespace dispatch +} // namespace redGrapes diff --git a/redGrapes/dispatch/thread/WorkerThread.hpp b/redGrapes/dispatch/thread/WorkerThread.hpp new file mode 100644 index 00000000..50721a31 --- /dev/null +++ b/redGrapes/dispatch/thread/WorkerThread.hpp @@ -0,0 +1,105 @@ +/* Copyright 2024 Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "redGrapes/TaskFreeCtx.hpp" +#include "redGrapes/dispatch/thread/DefaultWorker.hpp" +#include "redGrapes/memory/chunked_bump_alloc.hpp" +#include "redGrapes/memory/hwloc_alloc.hpp" + +#include +#include +#include + +namespace redGrapes::dispatch::thread +{ + template + struct WorkerThread + { + std::thread thread; + hwloc_obj_t const obj; // storing this vs calculating this as + // hwloc_obj_t obj + // = hwloc_get_obj_by_type(TaskFreeCtx::hwloc_ctx.topology, HWLOC_OBJ_PU, this->id % TaskFreeCtx::n_pus); + std::shared_ptr worker; + + template + WorkerThread(hwloc_obj_t const obj, Args&&... args) : obj{obj} + { + worker = std::make_shared(std::forward(args)...); + } + + ~WorkerThread() + { + } + + void start() + { + thread = std::thread([this] { this->run(); }); + } + + void stop() + { + worker->stop(); + thread.join(); + } + + /* function the thread will execute + */ + void run() + { + /* setup membind- & cpubind policies using hwloc + */ + this->cpubind(); + this->membind(); + + /* initialize thread-local variables + */ + *TaskFreeCtx::current_worker_id = worker->id; + + /* execute tasks until stop() + */ + worker->work_loop(); + + TaskFreeCtx::current_worker_id = std::nullopt; + + SPDLOG_TRACE("Worker Finished!"); + } + + void cpubind() + { + if(hwloc_set_cpubind( + TaskFreeCtx::hwloc_ctx.topology, + obj->cpuset, + HWLOC_CPUBIND_THREAD | HWLOC_CPUBIND_STRICT)) + { + char* str; + int error = errno; + hwloc_bitmap_asprintf(&str, obj->cpuset); + spdlog::warn("Couldn't cpubind to cpuset {}: {}\n", str, strerror(error)); + free(str); + } + } + + void membind() + { + if(hwloc_set_membind( + TaskFreeCtx::hwloc_ctx.topology, + obj->cpuset, + HWLOC_MEMBIND_BIND, + HWLOC_MEMBIND_THREAD | HWLOC_MEMBIND_STRICT)) + { + char* str; + int error = errno; + hwloc_bitmap_asprintf(&str, obj->cpuset); + spdlog::warn("Couldn't membind to cpuset {}: {}\n", str, strerror(error)); + free(str); + } + } + }; + +} // namespace redGrapes::dispatch::thread diff --git a/redGrapes/dispatch/thread/cpuset.cpp b/redGrapes/dispatch/thread/cpuset.cpp deleted file mode 100644 index a3d86116..00000000 --- a/redGrapes/dispatch/thread/cpuset.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* Copyright 2023 Michael Sippel - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -#include - -#include - -namespace redGrapes -{ - namespace dispatch - { - namespace thread - { - - void pin_cpu(unsigned cpuidx) - { - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - CPU_SET(cpuidx % CPU_SETSIZE, &cpuset); - - int rc = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); - if(rc != 0) - spdlog::error("cannot set thread affinity ({})", rc); - } - - void unpin_cpu() - { - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - for(int j = 0; j < 64; ++j) - CPU_SET(j, &cpuset); - - int rc = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); - if(rc != 0) - spdlog::error("cannot set thread affinity ({})", rc); - } - - } // namespace thread - } // namespace dispatch -} // namespace redGrapes diff --git a/redGrapes/dispatch/thread/cpuset.hpp b/redGrapes/dispatch/thread/cpuset.hpp deleted file mode 100644 index 29714f98..00000000 --- a/redGrapes/dispatch/thread/cpuset.hpp +++ /dev/null @@ -1,22 +0,0 @@ -/* Copyright 2023 Michael Sippel - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -#pragma once - -namespace redGrapes -{ - namespace dispatch - { - namespace thread - { - - void pin_cpu(unsigned); - void unpin_cpu(); - - } // namespace thread - } // namespace dispatch -} // namespace redGrapes diff --git a/redGrapes/dispatch/thread/execute.cpp b/redGrapes/dispatch/thread/execute.cpp deleted file mode 100644 index 2e2dc254..00000000 --- a/redGrapes/dispatch/thread/execute.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright 2022-2023 Michael Sippel - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -#include -#include -#include -#include -#include - -#include -#include - -#include - -namespace redGrapes -{ - /* -namespace dispatch -{ -namespace thread -{*/ - - void Context::execute_task(Task& task) - { - TRACE_EVENT("Worker", "dispatch task"); - - SPDLOG_DEBUG("thread dispatch: execute task {}", task.task_id); - assert(task.is_ready()); - - task.get_pre_event().notify(); - current_task = &task; - - auto event = task(); - - if(event) - { - event->get_event().waker_id = current_worker->get_waker_id(); - task.sg_pause(*event); - - task.pre_event.up(); - task.get_pre_event().notify(); - } - else - task.get_post_event().notify(); - - current_task = nullptr; - } - - //} // namespace thread - //} // namespace dispatch -} // namespace redGrapes diff --git a/redGrapes/dispatch/thread/worker.cpp b/redGrapes/dispatch/thread/worker.cpp deleted file mode 100644 index e25c5d20..00000000 --- a/redGrapes/dispatch/thread/worker.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* Copyright 2020-2023 Michael Sippel - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -namespace redGrapes -{ - namespace dispatch - { - namespace thread - { - WorkerThread::WorkerThread( - memory::ChunkedBumpAlloc& alloc, - HwlocContext& hwloc_ctx, - hwloc_obj_t const& obj, - WorkerId worker_id) - : Worker(alloc, hwloc_ctx, obj, worker_id) - { - } - - WorkerThread::~WorkerThread() - { - } - - void WorkerThread::start() - { - thread = std::thread([this] { this->run(); }); - } - - Worker::Worker( - memory::ChunkedBumpAlloc& alloc, - HwlocContext& hwloc_ctx, - hwloc_obj_t const& obj, - WorkerId worker_id) - : alloc(alloc) - , hwloc_ctx(hwloc_ctx) - , id(worker_id) - { - } - - Worker::~Worker() - { - } - - void Worker::stop() - { - SPDLOG_TRACE("Worker::stop()"); - m_stop.store(true, std::memory_order_release); - wake(); - } - - void WorkerThread::stop() - { - Worker::stop(); - thread.join(); - } - - void WorkerThread::run() - { - /* setup membind- & cpubind policies using hwloc - */ - this->cpubind(); - this->membind(); - - /* since we are in a worker, there should always - * be a task running (we always have a parent task - * and therefore yield() guarantees to do - * a context-switch instead of idling - */ - /* - idle = [this] { - throw std::runtime_error("idle in worker thread!"); - }; - */ - - /* initialize thread-local variables - */ - SingletonContext::get().current_worker = this->shared_from_this(); - SingletonContext::get().current_waker_id = this->get_waker_id(); - SingletonContext::get().current_arena = this->get_worker_id(); - - /* execute tasks until stop() - */ - this->work_loop(); - - SingletonContext::get().current_worker.reset(); - - SPDLOG_TRACE("Worker Finished!"); - } - - void WorkerThread::cpubind() - { - size_t n_pus = hwloc_get_nbobjs_by_type(hwloc_ctx.topology, HWLOC_OBJ_PU); - hwloc_obj_t obj = hwloc_get_obj_by_type(hwloc_ctx.topology, HWLOC_OBJ_PU, id % n_pus); - - if(hwloc_set_cpubind(hwloc_ctx.topology, obj->cpuset, HWLOC_CPUBIND_THREAD | HWLOC_CPUBIND_STRICT)) - { - char* str; - int error = errno; - hwloc_bitmap_asprintf(&str, obj->cpuset); - spdlog::warn("Couldn't cpubind to cpuset {}: {}\n", str, strerror(error)); - free(str); - } - } - - void WorkerThread::membind() - { - size_t n_pus = hwloc_get_nbobjs_by_type(hwloc_ctx.topology, HWLOC_OBJ_PU); - hwloc_obj_t obj = hwloc_get_obj_by_type(hwloc_ctx.topology, HWLOC_OBJ_PU, id % n_pus); - if(hwloc_set_membind( - hwloc_ctx.topology, - obj->cpuset, - HWLOC_MEMBIND_BIND, - HWLOC_MEMBIND_THREAD | HWLOC_MEMBIND_STRICT)) - { - char* str; - int error = errno; - hwloc_bitmap_asprintf(&str, obj->cpuset); - spdlog::warn("Couldn't membind to cpuset {}: {}\n", str, strerror(error)); - free(str); - } - } - - void Worker::work_loop() - { - SPDLOG_TRACE("Worker {} start work_loop()", id); - while(!m_stop.load(std::memory_order_consume)) - { - SingletonContext::get().worker_pool->set_worker_state( - id, - dispatch::thread::WorkerState::AVAILABLE); - cv.wait(); - - while(Task* task = this->gather_task()) - { - SingletonContext::get().worker_pool->set_worker_state(id, dispatch::thread::WorkerState::BUSY); - SingletonContext::get().execute_task(*task); - } - } - SPDLOG_TRACE("Worker {} end work_loop()", id); - } - - Task* Worker::gather_task() - { - TRACE_EVENT("Worker", "gather_task()"); - Task* task = nullptr; - - /* STAGE 1: - * - * first, execute all tasks in the ready queue - */ - SPDLOG_TRACE("Worker {}: consume ready queue", id); - if(task = ready_queue.pop()) - return task; - - /* STAGE 2: - * - * after the ready queue is fully consumed, - * try initializing new tasks until one - * of them is found to be ready - */ - SPDLOG_TRACE("Worker {}: try init new tasks", id); - while(this->init_dependencies(task, true)) - if(task) - return task; - - /* set worker state to signal that we are requesting tasks - */ - SingletonContext::get().worker_pool->set_worker_state(id, dispatch::thread::WorkerState::AVAILABLE); - -#ifndef ENABLE_WORKSTEALING -# define ENABLE_WORKSTEALING 1 -#endif - -#if ENABLE_WORKSTEALING - - /* STAGE 3: - * - * after all tasks from own queues are consumed, try to steal tasks - */ - SPDLOG_TRACE("Worker {}: try to steal tasks", id); - task = SingletonContext::get().scheduler->steal_task(*this); - -#endif - - return task; - } - - bool Worker::init_dependencies(Task*& t, bool claimed) - { - TRACE_EVENT("Worker", "init_dependencies()"); - if(Task* task = emplacement_queue.pop()) - { - SPDLOG_DEBUG("init task {}", task->task_id); - - task->pre_event.up(); - task->init_graph(); - - if(task->get_pre_event().notify(claimed)) - t = task; - else - { - t = nullptr; - } - - return true; - } - else - return false; - } - - } // namespace thread - } // namespace dispatch -} // namespace redGrapes diff --git a/redGrapes/dispatch/thread/worker_pool.cpp b/redGrapes/dispatch/thread/worker_pool.cpp deleted file mode 100644 index 194df68c..00000000 --- a/redGrapes/dispatch/thread/worker_pool.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* Copyright 2022-2023 Michael Sippel - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -#include -#include -#include -#include -#include -#include -#include - -// #include - -namespace redGrapes -{ - namespace dispatch - { - namespace thread - { - - WorkerPool::WorkerPool(HwlocContext& hwloc_ctx, size_t n_workers) - : hwloc_ctx(hwloc_ctx) - , worker_state(n_workers) - { - Context::current_waker_id = 0; - } - - void WorkerPool::emplace_workers(size_t n_workers) - { - unsigned n_pus = hwloc_get_nbobjs_by_type(hwloc_ctx.topology, HWLOC_OBJ_PU); - if(n_workers > n_pus) - spdlog::warn("{} worker-threads requested, but only {} PUs available!", n_workers, n_pus); - - allocs.reserve(n_workers); - workers.reserve(n_workers); - - SPDLOG_INFO("populate WorkerPool with {} workers", n_workers); - for(size_t worker_id = 0; worker_id < n_workers; ++worker_id) - { - unsigned pu_id = worker_id % n_pus; - // allocate worker with id `i` on arena `i`, - hwloc_obj_t obj = hwloc_get_obj_by_type(hwloc_ctx.topology, HWLOC_OBJ_PU, pu_id); - allocs.emplace_back(memory::HwlocAlloc(hwloc_ctx, obj), REDGRAPES_ALLOC_CHUNKSIZE); - - SingletonContext::get().current_arena = pu_id; - auto worker - = memory::alloc_shared_bind(pu_id, get_alloc(pu_id), hwloc_ctx, obj, worker_id); - // auto worker = std::make_shared< WorkerThread >( get_alloc(i), hwloc_ctx, obj, i ); - workers.emplace_back(worker); - } - } - - WorkerPool::~WorkerPool() - { - } - - void WorkerPool::start() - { - for(auto& worker : workers) - worker->start(); - } - - void WorkerPool::stop() - { - for(auto& worker : workers) - worker->stop(); - - workers.clear(); - } - - int WorkerPool::find_free_worker() - { - TRACE_EVENT("Scheduler", "find_worker"); - - SPDLOG_TRACE("find worker..."); - - unsigned start_idx = 0; - if(auto w = SingletonContext::get().current_worker) - start_idx = w->get_worker_id(); - - std::optional idx = this->probe_worker_by_state( - [this](unsigned idx) -> std::optional - { - if(set_worker_state(idx, WorkerState::BUSY)) - return idx; - else - return std::nullopt; - }, - dispatch::thread::WorkerState::AVAILABLE, // find a free worker - start_idx, - false); - - if(idx) - return *idx; - else - // no free worker found, - return -1; - } - - } // namespace thread - } // namespace dispatch -} // namespace redGrapes diff --git a/redGrapes/dispatch/thread/worker_pool.hpp b/redGrapes/dispatch/thread/worker_pool.hpp index d8acefa1..dfd5d9a4 100644 --- a/redGrapes/dispatch/thread/worker_pool.hpp +++ b/redGrapes/dispatch/thread/worker_pool.hpp @@ -1,4 +1,4 @@ -/* Copyright 2022-2023 Michael Sippel +/* Copyright 2022-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -6,9 +6,9 @@ */ #pragma once -#include -#include -#include +#include "redGrapes/TaskFreeCtx.hpp" +#include "redGrapes/memory/hwloc_alloc.hpp" +#include "redGrapes/util/bitfield.hpp" #include @@ -20,23 +20,25 @@ namespace redGrapes { namespace thread { - - using WorkerId = unsigned; - enum WorkerState { BUSY = 0, AVAILABLE = 1 }; + template struct WorkerThread; + template struct WorkerPool { - WorkerPool(HwlocContext& hwloc_ctx, size_t n_workers = 1); + using task_type = Worker::task_type; + using TTask = task_type; + + WorkerPool(size_t n_workers); ~WorkerPool(); - void emplace_workers(size_t n_workers); + void emplace_workers(WorkerId base_id); /* get the number of workers in this pool */ @@ -53,28 +55,22 @@ namespace redGrapes */ void stop(); - inline memory::ChunkedBumpAlloc& get_alloc(WorkerId worker_id) + inline WorkerThread& get_worker_thread(WorkerId local_worker_id) { - assert(worker_id < allocs.size()); - return allocs[worker_id]; + assert(local_worker_id < size()); + return *workers[local_worker_id]; } - inline WorkerThread& get_worker(WorkerId worker_id) + inline WorkerState get_worker_state(WorkerId local_worker_id) { - assert(worker_id < size()); - return *workers[worker_id]; - } - - inline WorkerState get_worker_state(WorkerId worker_id) - { - return worker_state.get(worker_id) ? WorkerState::AVAILABLE : WorkerState::BUSY; + return worker_state.get(local_worker_id) ? WorkerState::AVAILABLE : WorkerState::BUSY; } /* return true on success */ - inline bool set_worker_state(WorkerId worker_id, WorkerState state) + inline bool set_worker_state(WorkerId local_worker_id, WorkerState state) { - return worker_state.set(worker_id, state) != state; + return worker_state.set(local_worker_id, state) != state; } template @@ -94,18 +90,114 @@ namespace redGrapes * tries to find an available worker, but potentially * returns a busy worker if no free worker is available * - * @return worker_id + * @return local_worker_id */ int find_free_worker(); - private: - HwlocContext& hwloc_ctx; + /* tries to find a task with uninialized dependency edges in the + * task-graph in the emplacement queues of other workers + * and removes it from there + */ + TTask* steal_new_task(Worker& worker) + { + std::optional task = probe_worker_by_state( + [&worker, this](unsigned idx) -> std::optional + { + // we have a candidate of a busy worker, + // now check its queue + if(TTask* t = get_worker_thread(idx).worker->emplacement_queue.pop()) + return t; + + // otherwise check own queue again + else if(TTask* t = worker.emplacement_queue.pop()) + return t; + + // else continue search + else + return std::nullopt; + }, + + // find a busy worker + dispatch::thread::WorkerState::BUSY, + + // start next to current worker + worker.id - m_base_id); + + return task ? *task : nullptr; + } + + /* tries to find a ready task in any queue of other workers + * and removes it from the queue + */ + TTask* steal_ready_task(Worker& worker) + { + std::optional task = probe_worker_by_state( + [&worker, this](unsigned idx) -> std::optional + { + // we have a candidate of a busy worker, + // now check its queue + if(TTask* t = get_worker_thread(idx).worker->ready_queue.pop()) + return t; + + // otherwise check own queue again + else if(TTask* t = worker.ready_queue.pop()) + return t; + + // else continue search + else + return std::nullopt; + }, + + // find a busy worker + dispatch::thread::WorkerState::BUSY, + + // start next to current worker + worker.id - m_base_id); + + return task ? *task : nullptr; + } + + // give worker a ready task if available + // @return task if a new task was found, nullptr otherwise + TTask* steal_task(Worker& worker) + { + unsigned local_worker_id = worker.id - m_base_id; + + spdlog::debug("steal task for worker {}", local_worker_id); + + if(TTask* task = steal_ready_task(worker)) + { + set_worker_state(local_worker_id, dispatch::thread::WorkerState::BUSY); + return task; + } + + if(TTask* task = steal_new_task(worker)) + { + task->pre_event.up(); + task->init_graph(); + + if(task->get_pre_event().notify(true)) + { + set_worker_state(local_worker_id, dispatch::thread::WorkerState::BUSY); + return task; + } + } - std::vector> allocs; - std::vector> workers; + return nullptr; + } + + + private: + std::vector>> workers; + HwlocContext* hwloc_ctx_p; AtomicBitfield worker_state; + unsigned int num_workers; + WorkerId m_base_id; }; + } // namespace thread } // namespace dispatch } // namespace redGrapes + +#include "redGrapes/dispatch/thread/worker_pool.tpp" diff --git a/redGrapes/dispatch/thread/worker_pool.tpp b/redGrapes/dispatch/thread/worker_pool.tpp new file mode 100644 index 00000000..6b40c965 --- /dev/null +++ b/redGrapes/dispatch/thread/worker_pool.tpp @@ -0,0 +1,115 @@ +/* Copyright 2022-2024 Michael Sippel, Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include "redGrapes/TaskFreeCtx.hpp" +#include "redGrapes/dispatch/thread/DefaultWorker.hpp" +#include "redGrapes/dispatch/thread/WorkerThread.hpp" +#include "redGrapes/dispatch/thread/worker_pool.hpp" +#include "redGrapes/memory/allocator.hpp" +#include "redGrapes/memory/chunked_bump_alloc.hpp" +#include "redGrapes/memory/hwloc_alloc.hpp" +#include "redGrapes/util/trace.hpp" + +namespace redGrapes +{ + namespace dispatch + { + namespace thread + { + template + WorkerPool::WorkerPool(size_t n_workers) : worker_state(n_workers) + , num_workers(n_workers) + { + } + + template + void WorkerPool::emplace_workers(WorkerId base_id) + { + m_base_id = base_id; + if(num_workers > TaskFreeCtx::n_pus) + spdlog::warn( + "{} worker-threads requested, but only {} PUs available!", + num_workers, + TaskFreeCtx::n_pus); + + workers.reserve(num_workers); + + spdlog::debug("populate WorkerPool with {} workers", num_workers); + for(size_t worker_id = base_id; worker_id < base_id + num_workers; ++worker_id) + { + unsigned pu_id = worker_id % TaskFreeCtx::n_pus; + // allocate worker with id `i` on arena `i`, + hwloc_obj_t obj = hwloc_get_obj_by_type(TaskFreeCtx::hwloc_ctx.topology, HWLOC_OBJ_PU, pu_id); + TaskFreeCtx::worker_alloc_pool->allocs.emplace_back( + memory::HwlocAlloc(TaskFreeCtx::hwloc_ctx, obj), + REDGRAPES_ALLOC_CHUNKSIZE); + + auto worker = memory::alloc_shared_bind>( + worker_id, + obj, + worker_id, + worker_state, + *this); + workers.emplace_back(worker); + } + } + + template + WorkerPool::~WorkerPool() + { + } + + template + void WorkerPool::start() + { + for(auto& worker : workers) + worker->start(); + } + + template + void WorkerPool::stop() + { + for(auto& worker : workers) + worker->stop(); + + workers.clear(); + } + + template + int WorkerPool::find_free_worker() + { + TRACE_EVENT("Scheduler", "find_worker"); + + SPDLOG_TRACE("find worker..."); + + unsigned start_idx = 0; + if(TaskFreeCtx::current_worker_id) + start_idx = *TaskFreeCtx::current_worker_id - m_base_id; + + std::optional idx = this->probe_worker_by_state( + [this](unsigned idx) -> std::optional + { + if(set_worker_state(idx, WorkerState::BUSY)) + return idx; + else + return std::nullopt; + }, + dispatch::thread::WorkerState::AVAILABLE, // find a free worker + start_idx, + false); + + if(idx) + return *idx; + else + // no free worker found, + return -1; + } + + } // namespace thread + } // namespace dispatch +} // namespace redGrapes diff --git a/redGrapes/memory/allocator.cpp b/redGrapes/memory/allocator.cpp deleted file mode 100644 index 09f867ae..00000000 --- a/redGrapes/memory/allocator.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include -#include -#include - -#include - -namespace redGrapes -{ - namespace memory - { - - Allocator::Allocator() : Allocator(SingletonContext::get().current_arena) - { - } - - Allocator::Allocator(dispatch::thread::WorkerId worker_id) - : worker_id(worker_id % SingletonContext::get().n_workers) - { - } - - Block Allocator::allocate(size_t n_bytes) - { - return SingletonContext::get().worker_pool->get_alloc(worker_id).allocate(n_bytes); - } - - void Allocator::deallocate(Block blk) - { - SingletonContext::get().worker_pool->get_alloc(worker_id).deallocate(blk); - } - - } // namespace memory -} // namespace redGrapes diff --git a/redGrapes/memory/allocator.hpp b/redGrapes/memory/allocator.hpp index 5167b260..ffd83e27 100644 --- a/redGrapes/memory/allocator.hpp +++ b/redGrapes/memory/allocator.hpp @@ -1,7 +1,14 @@ +/* Copyright 2023-2024 Michael Sippel, Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + #pragma once -#include -#include +#include "redGrapes/TaskFreeCtx.hpp" +#include "redGrapes/memory/block.hpp" #include #include @@ -10,33 +17,30 @@ namespace redGrapes { - - namespace dispatch - { - namespace thread - { - using WorkerId = unsigned; - struct WorkerPool; - } // namespace thread - } // namespace dispatch - - extern std::shared_ptr worker_pool; - namespace memory { - struct Allocator { - dispatch::thread::WorkerId worker_id; + WorkerId worker_id; - // allocate on `current_arena` given by `SingletonContext` - Allocator(); + Allocator() : Allocator(*TaskFreeCtx::current_worker_id) + { + } // allocate on arena for specific worker - Allocator(dispatch::thread::WorkerId worker_id); + Allocator(WorkerId worker_id) : worker_id(worker_id) + { + } + + Block allocate(size_t n_bytes) + { + return TaskFreeCtx::worker_alloc_pool->get_alloc(worker_id).allocate(n_bytes); + } - Block allocate(size_t n_bytes); - void deallocate(Block blk); + void deallocate(Block blk) + { + TaskFreeCtx::worker_alloc_pool->get_alloc(worker_id).deallocate(blk); + } }; template @@ -49,7 +53,7 @@ namespace redGrapes { } - StdAllocator(dispatch::thread::WorkerId worker_id) : alloc(worker_id) + StdAllocator(WorkerId worker_id) : alloc(worker_id) { } @@ -103,9 +107,9 @@ namespace redGrapes /* allocates a shared_ptr in the memory pool of a given worker */ template - std::shared_ptr alloc_shared_bind(dispatch::thread::WorkerId worker_id, Args&&... args) + std::shared_ptr alloc_shared_bind(WorkerId worker_id, Args&&... args) { - return std::allocate_shared>(StdAllocator(worker_id), std::forward(args)...); + return std::allocate_shared(StdAllocator(worker_id), std::forward(args)...); } /* allocates a shared_ptr in the memory pool of the current worker @@ -113,7 +117,7 @@ namespace redGrapes template std::shared_ptr alloc_shared(Args&&... args) { - return std::allocate_shared>(StdAllocator(), std::forward(args)...); + return std::allocate_shared(StdAllocator(), std::forward(args)...); } } // namespace memory diff --git a/redGrapes/memory/bump_allocator.cpp b/redGrapes/memory/bump_allocator.cpp deleted file mode 100644 index ba77e2ae..00000000 --- a/redGrapes/memory/bump_allocator.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* Copyright 2022 Michael Sippel - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -#include -#include - -#include - -#include -#include - -namespace redGrapes -{ - namespace memory - { - - BumpAllocator::BumpAllocator(Block blk) : BumpAllocator((uintptr_t) blk.ptr, (uintptr_t) blk.ptr + blk.len) - { - } - - BumpAllocator::BumpAllocator(uintptr_t lower_limit, uintptr_t upper_limit) - : lower_limit(lower_limit) - , upper_limit(upper_limit) - , count(0) - { - SPDLOG_INFO("bumpallochunk: lower={}, upper={}", lower_limit, upper_limit); - next_addr = upper_limit; - } - - BumpAllocator::~BumpAllocator() - { -#ifndef NDEBUG - if(!empty()) - spdlog::warn("BumpAllocChunk: {} allocations remaining not deallocated.", count.load()); -#endif - } - - bool BumpAllocator::empty() const - { - return (count == 0); - } - - bool BumpAllocator::full() const - { - return next_addr <= lower_limit; - } - - void BumpAllocator::reset() - { - next_addr = upper_limit; - count = 0; - } - - Block BumpAllocator::allocate(size_t n_bytes) - { - uintptr_t addr = next_addr.fetch_sub(n_bytes) - n_bytes; - if(addr >= lower_limit) - { - count++; - return Block{addr, n_bytes}; - } - else - return Block::null(); - } - - uint16_t BumpAllocator::deallocate(Block blk) - { - assert(owns(blk)); - return count.fetch_sub(1); - } - - bool BumpAllocator::owns(Block const& blk) const - { - return blk.ptr >= lower_limit && blk.ptr < upper_limit; - } - - } // namespace memory -} // namespace redGrapes diff --git a/redGrapes/memory/bump_allocator.hpp b/redGrapes/memory/bump_allocator.hpp index f6aa9397..fe95fe9e 100644 --- a/redGrapes/memory/bump_allocator.hpp +++ b/redGrapes/memory/bump_allocator.hpp @@ -1,18 +1,20 @@ -/* Copyright 2023 The RedGrapes Community - * - * Authors: Michael Sippel +/* Copyright 2023-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + #pragma once -#include +#include "redGrapes/memory/block.hpp" + +#include #include #include +#include namespace redGrapes { @@ -27,44 +29,88 @@ namespace redGrapes */ struct BumpAllocator { - BumpAllocator(Block blk); - BumpAllocator(uintptr_t lower_limit, uintptr_t upper_limit); + BumpAllocator(Block blk) : BumpAllocator((uintptr_t) blk.ptr, (uintptr_t) blk.ptr + blk.len) + { + } + + BumpAllocator(uintptr_t lower_limit, uintptr_t upper_limit) + : count(0) + , lower_limit(lower_limit) + , upper_limit(upper_limit) + { + spdlog::debug("bumpallochunk: lower={}, upper={}", lower_limit, upper_limit); + next_addr = upper_limit; + } + BumpAllocator(BumpAllocator const&) = delete; BumpAllocator(BumpAllocator&) = delete; - ~BumpAllocator(); - - void reset(); - bool empty() const; + ~BumpAllocator() + { +#ifndef NDEBUG + if(!empty()) + spdlog::warn("BumpAllocChunk: {} allocations remaining not deallocated.", count.load()); +#endif + } + + void reset() + { + next_addr = upper_limit; + count = 0; + } + + bool empty() const + { + return (count == 0); + } /* check whether this allocator is exhausted already. * @return true if no free space remains */ - bool full() const; + bool full() const + { + return next_addr <= lower_limit; + } /*! checks whether this block is managed by this allocator */ - bool owns(Block const&) const; + bool owns(Block const& blk) const + { + return blk.ptr >= lower_limit && blk.ptr < upper_limit; + } /*! @param n_bytes size of requested memory block * @return Block with len = n_bytes and some non-nullpointer * if successful, return Block::null() on exhaustion. */ - Block allocate(size_t n_bytes); + Block allocate(size_t n_bytes) + { + uintptr_t addr = next_addr.fetch_sub(n_bytes) - n_bytes; + if(addr >= lower_limit) + { + count++; + return Block{addr, n_bytes}; + } + else + return Block::null(); + } /*! @return how many active allocations remain, * if it returns 0, this allocator needs to be reset. */ - uint16_t deallocate(Block blk); + uint16_t deallocate(Block blk) + { + assert(owns(blk)); + return count.fetch_sub(1); + } - private: - //! number of active allocations - std::atomic count; + private: //! pointer to the upper-limit of the next allocation std::atomic next_addr; - + //! number of active allocations + std::atomic count; uintptr_t const lower_limit; uintptr_t const upper_limit; }; diff --git a/redGrapes/memory/chunked_bump_alloc.hpp b/redGrapes/memory/chunked_bump_alloc.hpp index 20a6e876..e9f2acf1 100644 --- a/redGrapes/memory/chunked_bump_alloc.hpp +++ b/redGrapes/memory/chunked_bump_alloc.hpp @@ -1,4 +1,4 @@ -/* Copyright 2022-2023 Michael Sippel +/* Copyright 2022-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,27 +7,18 @@ #pragma once -#include -#include -#include -#include -#include -#include +#include "redGrapes/memory/bump_allocator.hpp" +#include "redGrapes/memory/hwloc_alloc.hpp" +#include "redGrapes/util/atomic_list.hpp" +#include "redGrapes/util/trace.hpp" #include #include -#include #include -#include -#include -#include -#include - -// #include #if REDGRAPES_ENABLE_BACKWARDCPP -# include +# include -#include +#include "redGrapes/memory/block.hpp" +#include "redGrapes/util/trace.hpp" #include #include #include -// #include - namespace redGrapes { diff --git a/redGrapes/redGrapes.cpp b/redGrapes/redGrapes.cpp deleted file mode 100644 index 9d1e5025..00000000 --- a/redGrapes/redGrapes.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* Copyright 2019-2022 Michael Sippel - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#if REDGRAPES_ENABLE_TRACE -PERFETTO_TRACK_EVENT_STATIC_STORAGE(); -#endif - -namespace redGrapes -{ - - thread_local Task* Context::current_task; - thread_local std::function Context::idle; - thread_local unsigned Context::next_worker; - thread_local unsigned Context::current_arena; - thread_local scheduler::WakerId Context::current_waker_id; - thread_local std::shared_ptr Context::current_worker; - - Context::Context() - { - idle = [this] { this->scheduler->idle(); }; - } - - Context::~Context() - { - } - - std::shared_ptr Context::current_task_space() const - { - if(current_task) - { - if(!current_task->children) - { - auto task_space = std::make_shared(current_task); - SPDLOG_TRACE("create child space = {}", (void*) task_space.get()); - current_task->children = task_space; - - std::unique_lock wr_lock(current_task->space->active_child_spaces_mutex); - current_task->space->active_child_spaces.push_back(task_space); - } - - return current_task->children; - } - else - return root_space; - } - - unsigned Context::scope_depth() const - { - if(auto ts = current_task_space()) - return ts->depth; - else - return 0; - } - - /*! Create an event on which the termination of the current task depends. - * A task must currently be running. - * - * @return Handle to flag the event with `reach_event` later. - * nullopt if there is no task running currently - */ - std::optional Context::create_event() - { - if(current_task) - return current_task->make_event(); - else - return std::nullopt; - } - - //! get backtrace from currently running task - std::vector> Context::backtrace() - { - std::vector> bt; - for(Task* task = current_task; task != nullptr; task = task->space->parent) - bt.push_back(*task); - - return bt; - } - - void Context::init_tracing() - { -#if REDGRAPES_ENABLE_TRACE - perfetto::TracingInitArgs args; - args.backends |= perfetto::kInProcessBackend; - perfetto::Tracing::Initialize(args); - perfetto::TrackEvent::Register(); - - tracing_session = StartTracing(); -#endif - } - - void Context::finalize_tracing() - { -#if REDGRAPES_ENABLE_TRACE - StopTracing(tracing_session); -#endif - } - - void Context::init(size_t n_workers, std::shared_ptr scheduler) - { - init_tracing(); - - this->n_workers = n_workers; - worker_pool = std::make_shared(hwloc_ctx, n_workers); - worker_pool->emplace_workers(n_workers); - - root_space = std::make_shared(); - this->scheduler = scheduler; - - worker_pool->start(); - } - - void Context::init(size_t n_workers) - { - init(n_workers, std::make_shared()); - } - - /*! wait until all tasks in the current task space finished - */ - void Context::barrier() - { - SPDLOG_TRACE("barrier"); - - while(!root_space->empty()) - idle(); - } - - void Context::finalize() - { - barrier(); - - worker_pool->stop(); - - scheduler.reset(); - root_space.reset(); - - finalize_tracing(); - } - - //! pause the currently running task at least until event is reached - void Context::yield(scheduler::EventPtr event) - { - if(current_task) - { - while(!event->is_reached()) - current_task->yield(event); - } - else - { - event->waker_id = Context::current_waker_id; - while(!event->is_reached()) - idle(); - } - } - - //! apply a patch to the properties of the currently running task - void Context::update_properties(typename TaskProperties::Patch const& patch) - { - if(current_task) - { - current_task->apply_patch(patch); - current_task->update_graph(); - } - else - throw std::runtime_error("update_properties: currently no task running"); - } - -} // namespace redGrapes diff --git a/redGrapes/redGrapes.hpp b/redGrapes/redGrapes.hpp index 0fc5a5cf..7952d33f 100644 --- a/redGrapes/redGrapes.hpp +++ b/redGrapes/redGrapes.hpp @@ -1,6 +1,4 @@ -/* Copyright 2022-2023 The RedGrapes Community. - * - * Authors: Michael Sippel +/* Copyright 2022-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -9,45 +7,105 @@ #pragma once -#include -#include - +#include "redGrapes/SchedulerDescription.hpp" +#include "redGrapes/TaskCtx.hpp" +#include "redGrapes/TaskFreeCtx.hpp" +#include "redGrapes/memory/hwloc_alloc.hpp" +#include "redGrapes/resource/fieldresource.hpp" +#include "redGrapes/resource/ioresource.hpp" +#include "redGrapes/scheduler/event.hpp" +#include "redGrapes/scheduler/pool_scheduler.hpp" +#include "redGrapes/task/task.hpp" +#include "redGrapes/task/task_builder.hpp" +#include "redGrapes/task/task_space.hpp" +#include "redGrapes/util/bind_args.hpp" +#include "redGrapes/util/tuple_map.hpp" + +#include #include -#include // std::shared_ptr - -// #include -#include -#include -#include -#include +#include +#include namespace redGrapes { - struct Context + template + struct RedGrapes { - Context(); - ~Context(); + public: + using RGTask = Task; + + template + RedGrapes(TSchedulerDesc... execDescs) + { + init_tracing(); + + (..., (scheduler_map[(typename TSchedulerDesc::Key{})] = execDescs.scheduler)); + + // TODO find n_workers without making a tuple + auto execDescTuple = std::make_tuple(execDescs...); + TaskFreeCtx::n_workers + = std::apply([](auto... args) { return (args.scheduler->n_workers + ...); }, execDescTuple); + + TaskFreeCtx::n_pus = hwloc_get_nbobjs_by_type(TaskFreeCtx::hwloc_ctx.topology, HWLOC_OBJ_PU); + if(TaskFreeCtx::n_workers > TaskFreeCtx::n_pus) + spdlog::warn( + "{} worker-threads requested, but only {} PUs available!", + TaskFreeCtx::n_workers, + TaskFreeCtx::n_pus); + + TaskFreeCtx::worker_alloc_pool = std::make_shared(); + TaskFreeCtx::worker_alloc_pool->allocs.reserve(TaskFreeCtx::n_workers); + + TaskCtx::root_space = std::make_shared>(); + + auto initAdd = [](auto scheduler, auto& base_worker_id) + { + scheduler->init(base_worker_id); + base_worker_id = base_worker_id + scheduler->n_workers; + }; + unsigned base_worker_id = 0; + std::apply( + [&base_worker_id, initAdd](auto... args) { ((initAdd(args.scheduler, base_worker_id)), ...); }, + execDescTuple); + + boost::mp11::mp_for_each( + [&](auto pair) { scheduler_map[boost::mp11::mp_first{}]->startExecution(); }); + } + + ~RedGrapes() + { + barrier(); + + boost::mp11::mp_for_each( + [&](auto pair) { scheduler_map[boost::mp11::mp_first{}]->stopExecution(); }); + boost::mp11::mp_for_each([&](auto pair) + { scheduler_map[boost::mp11::mp_first{}].reset(); }); + TaskCtx::root_space.reset(); + + finalize_tracing(); + } void init_tracing(); void finalize_tracing(); - void init(size_t n_workers, std::shared_ptr scheduler); - void init(size_t n_workers = std::thread::hardware_concurrency()); - void finalize(); - //! wait until all tasks in the current task space finished void barrier(); //! pause the currently running task at least until event is reached - void yield(scheduler::EventPtr event); + // TODO make this generic template + void yield(scheduler::EventPtr event) + { + TaskCtx::yield(event); + } //! apply a patch to the properties of the currently running task - void update_properties(typename TaskProperties::Patch const& patch); + void update_properties( + typename RGTask::TaskProperties::Patch const& patch); // TODO ensure TaskProperties is a TaskProperties1 //! get backtrace from currently running task - std::vector> backtrace(); + std::vector> backtrace() const; /*! Create an event on which the termination of the current task depends. * A task must currently be running. @@ -55,12 +113,20 @@ namespace redGrapes * @return Handle to flag the event with `reach_event` later. * nullopt if there is no task running currently */ - std::optional create_event(); + std::optional> create_event() + { + return TaskCtx::create_event(); + } - unsigned scope_depth() const; - std::shared_ptr current_task_space() const; + unsigned scope_depth() const + { + return TaskCtx::scope_depth(); + } - void execute_task(Task& task); + std::shared_ptr> current_task_space() const + { + return TaskCtx::current_task_space(); + } /*! create a new task, as child of the currently running task (if there is one) * @@ -74,121 +140,110 @@ namespace redGrapes * * @return future from f's result */ - template - auto emplace_task(Callable&& f, Args&&... args); + template + auto emplace_task(Callable&& f, Args&&... args) + { + WorkerId worker_id = scheduler_map[TSchedTag{}]->getNextWorkerID(); - static thread_local Task* current_task; - static thread_local std::function idle; - static thread_local unsigned next_worker; + SPDLOG_TRACE("emplace task to worker {} next_worker={}", worker_id, TaskFreeCtx::next_worker); - static thread_local scheduler::WakerId current_waker_id; - static thread_local std::shared_ptr current_worker; + using Impl = typename std::invoke_result_t, Callable, Args...>; + // this is not set to nullptr. But it goes out of scope. Memory is managed by allocate + FunTask* task; + memory::Allocator alloc(worker_id); + memory::Block blk = alloc.allocate(sizeof(FunTask)); + task = (FunTask*) blk.ptr; - unsigned n_workers; - static thread_local unsigned current_arena; - HwlocContext hwloc_ctx; - std::shared_ptr worker_pool; + if(!task) + throw std::bad_alloc(); - std::shared_ptr root_space; - std::shared_ptr scheduler; + // construct task in-place + new(task) FunTask(*scheduler_map[TSchedTag{}]); -#if REDGRAPES_ENABLE_TRACE - std::shared_ptr tracing_session; -#endif - }; + task->worker_id = worker_id; - /* ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ - * S I N G L E T O N - * ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ - */ + return std::move(TaskBuilder(task, std::move(f), std::forward(args)...)); + } - struct SingletonContext - { - static inline Context& get() + template + auto emplace_task(Callable&& f, Args&&... args) { - static Context ctx; - return ctx; + return emplace_task(std::forward(f), std::forward(args)...); } - }; - inline void init(size_t n_workers, std::shared_ptr scheduler) - { - SingletonContext::get().init(n_workers, scheduler); - } + template + auto& getScheduler() + { + return *scheduler_map[TSchedTag{}]; + } - inline void init(size_t n_workers = std::thread::hardware_concurrency()) - { - SingletonContext::get().init(n_workers); - } + auto& getScheduler() + { + return getScheduler(); + } - inline void finalize() - { - SingletonContext::get().finalize(); - } + template + auto createFieldResource(Container* c) -> FieldResource + { + return FieldResource(c); + } - inline void barrier() - { - SingletonContext::get().barrier(); - } + template + auto createFieldResource(Args&&... args) -> FieldResource + { + return FieldResource(args...); + } - inline void yield(scheduler::EventPtr event) - { - SingletonContext::get().yield(event); - } + template + auto createIOResource(std::shared_ptr o) -> IOResource + { + return IOResource(o); + } - inline void update_properties(typename TaskProperties::Patch const& patch) - { - SingletonContext::get().update_properties(patch); - } + template + auto createIOResource(Args&&... args) -> IOResource + { + return IOResource(args...); + } - inline std::vector> backtrace() - { - return SingletonContext::get().backtrace(); - } + template + auto createResource() -> Resource + { + return Resource(); + } - inline std::optional create_event() - { - return SingletonContext::get().create_event(); - } + private: + MapTuple scheduler_map; +#if REDGRAPES_ENABLE_TRACE + std::shared_ptr tracing_session; +#endif + }; - inline unsigned scope_depth() + // TODO make sure init can only be called once + // require atleast one T execDesc + template + [[nodiscard]] inline auto init(T execDesc, Ts... execDescs) { - return SingletonContext::get().scope_depth(); - } + using DescType = boost::mp11::mp_list; + using DescMap = boost::mp11::mp_transform; - inline std::shared_ptr current_task_space() - { - return SingletonContext::get().current_task_space(); + return RedGrapes(execDesc, execDescs...); } - template - inline auto emplace_task(Callable&& f, Args&&... args) + template + [[nodiscard]] inline auto init(size_t n_workers = std::thread::hardware_concurrency()) { - return std::move(SingletonContext::get().emplace_task(std::move(f), std::forward(args)...)); + auto execDesc = SchedulerDescription( + std::make_shared>>>( + n_workers), + DefaultTag{}); + using DescType = boost::mp11::mp_list; + using DescMap = boost::mp11::mp_transform; + + return RedGrapes(execDesc); } -} // namespace redGrapes - -// `TaskBuilder` needs "Context`, so can only include here after definiton -#include -namespace redGrapes -{ - template - auto Context::emplace_task(Callable&& f, Args&&... args) - { - dispatch::thread::WorkerId worker_id = - // linear - next_worker % worker_pool->size(); - - // interleaved - // 2*next_worker % worker_pool->size() + ((2*next_worker) / worker_pool->size())%2; - - next_worker++; - current_arena = worker_id; - - SPDLOG_TRACE("emplace task to worker {} next_worker={}", worker_id, next_worker); - - return std::move(TaskBuilder(std::move(f), std::forward(args)...)); - } } // namespace redGrapes + +#include "redGrapes/redGrapes.tpp" diff --git a/redGrapes/redGrapes.tpp b/redGrapes/redGrapes.tpp new file mode 100644 index 00000000..039ead88 --- /dev/null +++ b/redGrapes/redGrapes.tpp @@ -0,0 +1,84 @@ +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "redGrapes/TaskCtx.hpp" +#include "redGrapes/TaskFreeCtx.hpp" +#include "redGrapes/redGrapes.hpp" +#include "redGrapes/util/trace.hpp" + +#include + +#include + +#if REDGRAPES_ENABLE_TRACE +PERFETTO_TRACK_EVENT_STATIC_STORAGE(); +#endif + +namespace redGrapes +{ + + //! get backtrace from currently running task + template + std::vector>> RedGrapes:: + backtrace() const + { + std::vector> bt; + for(RGTask* task = TaskCtx::current_task; task != nullptr; task = task->space->parent) + bt.push_back(*task); + + return bt; + } + + template + void RedGrapes::init_tracing() + { +#if REDGRAPES_ENABLE_TRACE + perfetto::TracingInitArgs args; + args.backends |= perfetto::kInProcessBackend; + perfetto::Tracing::Initialize(args); + perfetto::TrackEvent::Register(); + + tracing_session = StartTracing(); +#endif + } + + template + void RedGrapes::finalize_tracing() + { +#if REDGRAPES_ENABLE_TRACE + StopTracing(tracing_session); +#endif + } + + /*! wait until all tasks in the current task space finished + */ + template + void RedGrapes::barrier() + { + SPDLOG_TRACE("barrier"); + + while(!TaskCtx::root_space->empty()) + TaskFreeCtx::idle(); + } + + //! apply a patch to the properties of the currently running task + template + void RedGrapes::update_properties( + typename Task::TaskProperties::Patch const& patch) + { + if(TaskCtx::current_task) + { + TaskCtx::current_task->apply_patch(patch); + TaskCtx::current_task->update_graph(); + } + else + throw std::runtime_error("update_properties: currently no task running"); + } + +} // namespace redGrapes diff --git a/redGrapes/resource/access/area.hpp b/redGrapes/resource/access/area.hpp index 521b353c..b1e2062b 100644 --- a/redGrapes/resource/access/area.hpp +++ b/redGrapes/resource/access/area.hpp @@ -72,6 +72,6 @@ struct fmt::formatter template auto format(redGrapes::access::AreaAccess const& acc, FormatContext& ctx) { - return format_to(ctx.out(), "{{ \"area\" : {{ \"begin\" : {}, \"end\" : {} }} }}", acc[0], acc[1]); + return fmt::format_to(ctx.out(), "{{ \"area\" : {{ \"begin\" : {}, \"end\" : {} }} }}", acc[0], acc[1]); } }; diff --git a/redGrapes/resource/access/combine.hpp b/redGrapes/resource/access/combine.hpp index 85617c60..12387cf4 100644 --- a/redGrapes/resource/access/combine.hpp +++ b/redGrapes/resource/access/combine.hpp @@ -14,7 +14,6 @@ #include #include -#include #include namespace redGrapes diff --git a/redGrapes/resource/access/field.hpp b/redGrapes/resource/access/field.hpp index 798e1e0a..ffbfb704 100644 --- a/redGrapes/resource/access/field.hpp +++ b/redGrapes/resource/access/field.hpp @@ -1,4 +1,4 @@ -/* Copyright 2019 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -11,9 +11,9 @@ #pragma once -#include -#include -#include +#include "redGrapes/resource/access/area.hpp" +#include "redGrapes/resource/access/combine.hpp" +#include "redGrapes/resource/access/io.hpp" namespace redGrapes { diff --git a/redGrapes/resource/access/io.hpp b/redGrapes/resource/access/io.hpp index 8dc5ec4d..ea07dfba 100644 --- a/redGrapes/resource/access/io.hpp +++ b/redGrapes/resource/access/io.hpp @@ -1,4 +1,4 @@ -/* Copyright 2019 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -11,7 +11,6 @@ #pragma once -#include #include namespace redGrapes diff --git a/redGrapes/resource/fieldresource.hpp b/redGrapes/resource/fieldresource.hpp index 9e8793bf..45e2598a 100644 --- a/redGrapes/resource/fieldresource.hpp +++ b/redGrapes/resource/fieldresource.hpp @@ -1,4 +1,4 @@ -/* Copyright 2019 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -11,12 +11,8 @@ #pragma once -#include -#include -#include -#include - -#include +#include "redGrapes/resource/access/field.hpp" +#include "redGrapes/resource/resource.hpp" namespace redGrapes { @@ -75,8 +71,8 @@ namespace redGrapes namespace fieldresource { - template - struct AreaGuard : SharedResourceObject::dim>> + template + struct AreaGuard : SharedResourceObject::dim>> { static constexpr size_t dim = trait::Field::dim; using Item = typename trait::Field::Item; @@ -93,12 +89,13 @@ namespace redGrapes } protected: - AreaGuard(std::shared_ptr obj) : SharedResourceObject>(obj) + AreaGuard(std::shared_ptr obj) + : SharedResourceObject>(obj) { } AreaGuard(AreaGuard const& other, Index begin, Index end) - : SharedResourceObject>(other) + : SharedResourceObject>(other) , m_area(other.make_area(begin, end)) { } @@ -126,12 +123,12 @@ namespace redGrapes access::ArrayAccess m_area; }; - template - struct ReadGuard : AreaGuard + template + struct ReadGuard : AreaGuard { static constexpr size_t dim = trait::Field::dim; - using typename AreaGuard::Index; - using typename AreaGuard::Item; + using typename AreaGuard::Index; + using typename AreaGuard::Item; ReadGuard read() const noexcept { @@ -161,27 +158,27 @@ namespace redGrapes return this->obj.get(); } - operator ResourceAccess() const noexcept + operator ResourceAccess() const noexcept { return this->make_access(access::FieldAccess(access::IOAccess::read, this->m_area)); } protected: - ReadGuard(ReadGuard const& other, Index begin, Index end) : AreaGuard(other, begin, end) + ReadGuard(ReadGuard const& other, Index begin, Index end) : AreaGuard(other, begin, end) { } - ReadGuard(std::shared_ptr obj) : AreaGuard(obj) + ReadGuard(std::shared_ptr obj) : AreaGuard(obj) { } }; - template - struct WriteGuard : ReadGuard + template + struct WriteGuard : ReadGuard { static constexpr size_t dim = trait::Field::dim; - using typename ReadGuard::Index; - using typename ReadGuard::Item; + using typename ReadGuard::Index; + using typename ReadGuard::Item; WriteGuard write() const noexcept { @@ -211,35 +208,36 @@ namespace redGrapes return this->obj.get(); } - operator ResourceAccess() const noexcept + operator ResourceAccess() const noexcept { return this->make_access(access::FieldAccess(access::IOAccess::write, this->m_area)); } protected: - WriteGuard(WriteGuard const& other, Index begin, Index end) : ReadGuard(other, begin, end) + WriteGuard(WriteGuard const& other, Index begin, Index end) + : ReadGuard(other, begin, end) { } - WriteGuard(std::shared_ptr obj) : ReadGuard(obj) + WriteGuard(std::shared_ptr obj) : ReadGuard(obj) { } }; } // namespace fieldresource - template - struct FieldResource : fieldresource::WriteGuard + template + struct FieldResource : fieldresource::WriteGuard { static constexpr size_t dim = trait::Field::dim; - FieldResource(Container* c) : fieldresource::WriteGuard(std::shared_ptr(c)) + FieldResource(Container* c) : fieldresource::WriteGuard(std::shared_ptr(c)) { } template FieldResource(Args&&... args) - : fieldresource::WriteGuard(memory::alloc_shared(std::forward(args)...)) + : fieldresource::WriteGuard(memory::alloc_shared(std::forward(args)...)) { } }; diff --git a/redGrapes/resource/ioresource.hpp b/redGrapes/resource/ioresource.hpp index 07efea4a..d1371b1f 100644 --- a/redGrapes/resource/ioresource.hpp +++ b/redGrapes/resource/ioresource.hpp @@ -1,4 +1,4 @@ -/* Copyright 2019 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -11,20 +11,18 @@ #pragma once -#include -#include -#include -#include +#include "redGrapes/resource/access/io.hpp" +#include "redGrapes/resource/resource.hpp" namespace redGrapes { namespace ioresource { - template - struct ReadGuard : public SharedResourceObject + template + struct ReadGuard : public SharedResourceObject { - operator ResourceAccess() const noexcept + operator ResourceAccess() const noexcept { return this->make_access(access::IOAccess::read); } @@ -50,15 +48,15 @@ namespace redGrapes } protected: - ReadGuard(std::shared_ptr obj) : SharedResourceObject(obj) + ReadGuard(std::shared_ptr obj) : SharedResourceObject(obj) { } }; - template - struct WriteGuard : public ReadGuard + template + struct WriteGuard : public ReadGuard { - operator ResourceAccess() const noexcept + operator ResourceAccess() const noexcept { return this->make_access(access::IOAccess::write); } @@ -84,22 +82,23 @@ namespace redGrapes } protected: - WriteGuard(std::shared_ptr obj) : ReadGuard(obj) + WriteGuard(std::shared_ptr obj) : ReadGuard(obj) { } }; } // namespace ioresource - template - struct IOResource : public ioresource::WriteGuard + template + struct IOResource : public ioresource::WriteGuard { template - IOResource(Args&&... args) : ioresource::WriteGuard(memory::alloc_shared(std::forward(args)...)) + IOResource(Args&&... args) + : ioresource::WriteGuard(memory::alloc_shared(std::forward(args)...)) { } - IOResource(std::shared_ptr o) : ioresource::WriteGuard(o) + IOResource(std::shared_ptr o) : ioresource::WriteGuard(o) { } diff --git a/redGrapes/resource/resource.cpp b/redGrapes/resource/resource.cpp deleted file mode 100644 index 3fc1d6ec..00000000 --- a/redGrapes/resource/resource.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* Copyright 2022 Michael Sippel - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -#include -#include - -#include - -namespace redGrapes -{ - struct Task; - - unsigned int ResourceBase::generateID() - { - static std::atomic id_counter; - return id_counter.fetch_add(1); - } - - ResourceBase::ResourceBase() - : id(generateID()) - , scope_level(scope_depth()) - , users(memory::Allocator(get_arena_id())) - { - } - - unsigned ResourceBase::get_arena_id() const - { - return id % SingletonContext::get().worker_pool->size(); - } - -} // namespace redGrapes diff --git a/redGrapes/resource/resource.hpp b/redGrapes/resource/resource.hpp index 52b23265..0e8a74eb 100644 --- a/redGrapes/resource/resource.hpp +++ b/redGrapes/resource/resource.hpp @@ -1,4 +1,4 @@ -/* Copyright 2019-2022 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -11,23 +11,21 @@ #pragma once -#include -#include -#include -#include +#include "redGrapes/TaskCtx.hpp" +#include "redGrapes/TaskFreeCtx.hpp" +#include "redGrapes/memory/allocator.hpp" +#include "redGrapes/sync/spinlock.hpp" +#include "redGrapes/task/property/trait.hpp" +#include "redGrapes/util/chunked_list.hpp" #include +#include -#include -#include -#include // std::unique_ptr<> -#include -#include -// #include -#include -// #include +#include +#include +#include +#include -#include #ifndef REDGRAPES_RUL_CHUNKSIZE # define REDGRAPES_RUL_CHUNKSIZE 128 @@ -36,43 +34,53 @@ namespace redGrapes { - template + template class Resource; - struct Task; - + template class ResourceBase { protected: - static unsigned int generateID(); + static unsigned int generateID() + { + static std::atomic id_counter; + return id_counter.fetch_add(1); + } public: unsigned int id; unsigned int scope_level; SpinLock users_mutex; - ChunkedList users; + ChunkedList users; /** * Create a new resource with an unused ID. */ - ResourceBase(); + ResourceBase() + : id(generateID()) + , scope_level(TaskCtx::scope_depth()) + , users(memory::Allocator(get_arena_id())) + { + } - unsigned get_arena_id() const; + unsigned get_arena_id() const + { + return id % TaskFreeCtx::n_workers; + } }; - template - class Resource; - + template class ResourceAccess { - template + // https://stackoverflow.com/questions/16567212/why-does-the-standard-prohibit-friend-declarations-of-partial-specializations + template friend class Resource; private: struct AccessBase { - AccessBase(boost::typeindex::type_index access_type, std::shared_ptr resource) + AccessBase(boost::typeindex::type_index access_type, std::shared_ptr> resource) : access_type(access_type) , resource(resource) { @@ -85,7 +93,7 @@ namespace redGrapes virtual ~AccessBase(){}; virtual bool operator==(AccessBase const& r) const = 0; - bool is_same_resource(ResourceAccess::AccessBase const& a) const + bool is_same_resource(ResourceAccess::AccessBase const& a) const { return this->resource == a.resource; } @@ -96,7 +104,7 @@ namespace redGrapes virtual std::string mode_format() const = 0; boost::typeindex::type_index access_type; - std::shared_ptr resource; + std::shared_ptr> resource; }; // AccessBase // todo use allocator!! @@ -107,22 +115,22 @@ namespace redGrapes { } - ResourceAccess(ResourceAccess const& other) : obj(other.obj) + ResourceAccess(ResourceAccess const& other) : obj(other.obj) { } - ResourceAccess(ResourceAccess&& other) : obj(std::move(other.obj)) + ResourceAccess(ResourceAccess&& other) : obj(std::move(other.obj)) { other.obj.reset(); } - ResourceAccess& operator=(ResourceAccess const& other) + ResourceAccess& operator=(ResourceAccess const& other) { this->obj = other.obj; return *this; } - static bool is_serial(ResourceAccess const& a, ResourceAccess const& b) + static bool is_serial(ResourceAccess const& a, ResourceAccess const& b) { if(a.obj->access_type == b.obj->access_type) return a.obj->is_serial(*b.obj); @@ -130,7 +138,7 @@ namespace redGrapes return false; } - bool is_superset_of(ResourceAccess const& a) const + bool is_superset_of(ResourceAccess const& a) const { // if ( this->obj->resource.scope_level < a.obj->resource.scope_level ) // return true; @@ -160,7 +168,7 @@ namespace redGrapes return this->obj->mode_format(); } - std::shared_ptr get_resource() + std::shared_ptr> get_resource() { return obj->resource; } @@ -171,14 +179,14 @@ namespace redGrapes * @param a another ResourceAccess * @return true if `a` is associated with the same resource as `this` */ - bool is_same_resource(ResourceAccess const& a) const + bool is_same_resource(ResourceAccess const& a) const { if(this->obj->access_type == a.obj->access_type) return this->obj->is_same_resource(*a.obj); return false; } - bool operator==(ResourceAccess const& a) const + bool operator==(ResourceAccess const& a) const { if(this->obj->access_type == a.obj->access_type) return *(this->obj) == *(a.obj); @@ -193,8 +201,11 @@ namespace redGrapes * implements BuildProperties for any type which * can be casted to a ResourceAccess */ - template - struct BuildProperties::value>::type> + template + struct BuildProperties< + T, + TTask, + typename std::enable_if>::value>::type> { template static inline void build(Builder& builder, T const& obj) @@ -204,13 +215,16 @@ namespace redGrapes }; } // namespace trait - struct DefaultAccessPolicy + namespace access { - static bool is_serial(DefaultAccessPolicy, DefaultAccessPolicy) + struct DefaultAccessPolicy { - return true; - } - }; + static bool is_serial(DefaultAccessPolicy, DefaultAccessPolicy) + { + return true; + } + }; + } // namespace access /** * @defgroup AccessPolicy @@ -239,19 +253,22 @@ namespace redGrapes * Represents a concrete resource. * Copied objects represent the same resource. */ - template + template class Resource { protected: - struct Access : public ResourceAccess::AccessBase + struct Access : public ResourceAccess::AccessBase { - Access(std::shared_ptr resource, AccessPolicy policy) - : ResourceAccess::AccessBase(boost::typeindex::type_id(), resource) + Access(std::shared_ptr> resource, AccessPolicy policy) + : ResourceAccess::AccessBase(boost::typeindex::type_id(), resource) , policy(policy) { } - Access(Access&& other) : AccessBase(std::move((AccessBase&&) other)), policy(std::move(other.policy)) + Access(Access&& other) + : ResourceAccess::AccessBase( + std::move(std::forward::AccessBase>(other))) // TODO check this + , policy(std::move(other.policy)) { } @@ -264,21 +281,21 @@ namespace redGrapes return policy.is_synchronizing(); } - bool is_serial(ResourceAccess::AccessBase const& a_) const + bool is_serial(typename ResourceAccess::AccessBase const& a_) const { Access const& a = *static_cast(&a_); // no dynamic cast needed, type checked in ResourceAccess return this->is_same_resource(a) && AccessPolicy::is_serial(this->policy, a.policy); } - bool is_superset_of(ResourceAccess::AccessBase const& a_) const + bool is_superset_of(typename ResourceAccess::AccessBase const& a_) const { Access const& a = *static_cast(&a_); // no dynamic cast needed, type checked in ResourceAccess return this->is_same_resource(a) && this->policy.is_superset_of(a.policy); } - bool operator==(ResourceAccess::AccessBase const& a_) const + bool operator==(typename ResourceAccess::AccessBase const& a_) const { Access const& a = *static_cast(&a_); // no dynamic cast needed, type checked in ResourceAccess @@ -294,11 +311,11 @@ namespace redGrapes AccessPolicy policy; }; // struct ThisResourceAccess - friend class ResourceBase; + friend class ResourceBase; - std::shared_ptr base; + std::shared_ptr> base; - Resource(std::shared_ptr base) : base(base) + Resource(std::shared_ptr> base) : base(base) { } @@ -307,11 +324,8 @@ namespace redGrapes { static unsigned i = 0; - /* NOTE: Because of #include loops we cannot access Context and thus not worker_pool->size(). - * for this reason the modulo is done in constructor of Allocator() - */ - dispatch::thread::WorkerId worker_id = i++; // % SingletonContext::get().worker_pool->size(); - base = redGrapes::memory::alloc_shared_bind(worker_id); + WorkerId worker_id = i++ % TaskFreeCtx::n_workers; + base = redGrapes::memory::alloc_shared_bind>(worker_id); } /** @@ -321,15 +335,15 @@ namespace redGrapes * @param pol AccessPolicy object, containing all access information * @return ResourceAccess on this resource */ - ResourceAccess make_access(AccessPolicy pol) const + ResourceAccess make_access(AccessPolicy pol) const { auto a = redGrapes::memory::alloc_shared_bind(base->get_arena_id(), base, pol); - return ResourceAccess(a); + return ResourceAccess(a); } }; // class Resource - template - struct SharedResourceObject : Resource + template + struct SharedResourceObject : Resource { // protected: std::shared_ptr obj; @@ -338,15 +352,15 @@ namespace redGrapes { } - SharedResourceObject(SharedResourceObject const& other) : Resource(other), obj(other.obj) + SharedResourceObject(SharedResourceObject const& other) : Resource(other), obj(other.obj) { } }; // struct SharedResourceObject } // namespace redGrapes -template<> -struct fmt::formatter +template +struct fmt::formatter> { constexpr auto parse(format_parse_context& ctx) { @@ -354,7 +368,7 @@ struct fmt::formatter } template - auto format(redGrapes::ResourceAccess const& acc, FormatContext& ctx) + auto format(redGrapes::ResourceAccess const& acc, FormatContext& ctx) { return fmt::format_to( ctx.out(), diff --git a/redGrapes/resource/resource_user.cpp b/redGrapes/resource/resource_user.cpp deleted file mode 100644 index 489bd05e..00000000 --- a/redGrapes/resource/resource_user.cpp +++ /dev/null @@ -1,106 +0,0 @@ - -#include -#include -#include -#include -#include - -namespace redGrapes -{ - bool ResourceUsageEntry::operator==(ResourceUsageEntry const& other) const - { - return resource == other.resource; - } - - ResourceUser::ResourceUser() - : scope_level(SingletonContext::get().scope_depth()) - , access_list(memory::Allocator()) - , unique_resources(memory::Allocator()) - { - } - - ResourceUser::ResourceUser(ResourceUser const& other) - : scope_level(other.scope_level) - , access_list(memory::Allocator(), other.access_list) - , unique_resources(memory::Allocator(), other.unique_resources) - { - } - - ResourceUser::ResourceUser(std::initializer_list list) - : scope_level(scope_depth()) - , access_list(memory::Allocator()) - , unique_resources(memory::Allocator()) - { - for(auto& ra : list) - add_resource_access(ra); - } - - void ResourceUser::add_resource_access(ResourceAccess ra) - { - this->access_list.push(ra); - std::shared_ptr r = ra.get_resource(); - // unique_resources.erase(ResourceEntry{ r, r->users.end() }); - unique_resources.push(ResourceUsageEntry{r, r->users.rend()}); - } - - void ResourceUser::rm_resource_access(ResourceAccess ra) - { - this->access_list.erase(ra); - } - - void ResourceUser::build_unique_resource_list() - { - for(auto ra = access_list.rbegin(); ra != access_list.rend(); ++ra) - { - std::shared_ptr r = ra->get_resource(); - unique_resources.erase(ResourceUsageEntry{r, r->users.rend()}); - unique_resources.push(ResourceUsageEntry{r, r->users.rend()}); - } - } - - bool ResourceUser::has_sync_access(std::shared_ptr res) - { - for(auto ra = access_list.rbegin(); ra != access_list.rend(); ++ra) - { - if(ra->get_resource() == res && ra->is_synchronizing()) - return true; - } - return false; - } - - bool ResourceUser::is_serial(ResourceUser const& a, ResourceUser const& b) - { - TRACE_EVENT("ResourceUser", "is_serial"); - for(auto ra = a.access_list.crbegin(); ra != a.access_list.crend(); ++ra) - for(auto rb = b.access_list.crbegin(); rb != b.access_list.crend(); ++rb) - { - TRACE_EVENT("ResourceUser", "RA::is_serial"); - if(ResourceAccess::is_serial(*ra, *rb)) - return true; - } - return false; - } - - bool ResourceUser::is_superset_of(ResourceUser const& a) const - { - TRACE_EVENT("ResourceUser", "is_superset"); - for(auto ra = a.access_list.rbegin(); ra != a.access_list.rend(); ++ra) - { - bool found = false; - for(auto r = access_list.rbegin(); r != access_list.rend(); ++r) - if(r->is_superset_of(*ra)) - found = true; - - if(!found && ra->scope_level() <= scope_level) - // a introduced a new resource - return false; - } - return true; - } - - bool ResourceUser::is_superset(ResourceUser const& a, ResourceUser const& b) - { - return a.is_superset_of(b); - } - -} // namespace redGrapes diff --git a/redGrapes/resource/resource_user.hpp b/redGrapes/resource/resource_user.hpp index 3edf6082..46f8e6cf 100644 --- a/redGrapes/resource/resource_user.hpp +++ b/redGrapes/resource/resource_user.hpp @@ -1,4 +1,4 @@ -/* Copyright 2019 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -11,56 +11,73 @@ #pragma once -#include -#include -#include +#include "redGrapes/util/chunked_list.hpp" #include -#include +#include +#include namespace redGrapes { +#ifndef REDGRAPES_RUL_CHUNKSIZE +# define REDGRAPES_RUL_CHUNKSIZE 128 +#endif + unsigned scope_depth(); - struct Task; + template struct ResourceBase; + + template struct ResourceAccess; + template struct ResourceUsageEntry { - std::shared_ptr resource; - typename ChunkedList::MutBackwardIterator task_entry; + std::shared_ptr> resource; + typename ChunkedList::MutBackwardIterator task_entry; - bool operator==(ResourceUsageEntry const& other) const; + bool operator==(ResourceUsageEntry const& other) const; }; - class ResourceUser + template + struct ResourceUser { - public: ResourceUser(); ResourceUser(ResourceUser const& other); - ResourceUser(std::initializer_list list); + ResourceUser(std::initializer_list> list); - void add_resource_access(ResourceAccess ra); - void rm_resource_access(ResourceAccess ra); + void add_resource_access(ResourceAccess ra); + void rm_resource_access(ResourceAccess ra); void build_unique_resource_list(); - bool has_sync_access(std::shared_ptr res); + bool has_sync_access(std::shared_ptr> const& res); bool is_superset_of(ResourceUser const& a) const; - static bool is_superset(ResourceUser const& a, ResourceUser const& b); - static bool is_serial(ResourceUser const& a, ResourceUser const& b); + + friend bool is_serial(ResourceUser const& a, ResourceUser const& b) + { + TRACE_EVENT("ResourceUser", "is_serial"); + for(auto ra = a.access_list.crbegin(); ra != a.access_list.crend(); ++ra) + for(auto rb = b.access_list.crbegin(); rb != b.access_list.crend(); ++rb) + { + TRACE_EVENT("ResourceUser", "RA::is_serial"); + if(ResourceAccess::is_serial(*ra, *rb)) + return true; + } + return false; + } uint8_t scope_level; - ChunkedList access_list; - ChunkedList unique_resources; - }; // class ResourceUser + ChunkedList, 8> access_list; + ChunkedList, 8> unique_resources; + }; // struct ResourceUser } // namespace redGrapes -template<> -struct fmt::formatter +template +struct fmt::formatter> { constexpr auto parse(format_parse_context& ctx) { @@ -68,7 +85,7 @@ struct fmt::formatter } template - auto format(redGrapes::ResourceUser const& r, FormatContext& ctx) + auto format(redGrapes::ResourceUser const& r, FormatContext& ctx) { auto out = ctx.out(); out = fmt::format_to(out, "["); @@ -84,3 +101,5 @@ struct fmt::formatter return out; } }; + +#include "redGrapes/resource/resource_user.tpp" diff --git a/redGrapes/resource/resource_user.tpp b/redGrapes/resource/resource_user.tpp new file mode 100644 index 00000000..82f45fd5 --- /dev/null +++ b/redGrapes/resource/resource_user.tpp @@ -0,0 +1,103 @@ +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "redGrapes/TaskCtx.hpp" +#include "redGrapes/resource/resource.hpp" +#include "redGrapes/resource/resource_user.hpp" +#include "redGrapes/util/trace.hpp" + +namespace redGrapes +{ + template + bool ResourceUsageEntry::operator==(ResourceUsageEntry const& other) const + { + return resource == other.resource; + } + + template + ResourceUser::ResourceUser() + : scope_level(TaskCtx::scope_depth()) + , access_list(memory::Allocator()) + , unique_resources(memory::Allocator()) + { + } + + template + ResourceUser::ResourceUser(ResourceUser const& other) + : scope_level(other.scope_level) + , access_list(memory::Allocator(), other.access_list) + , unique_resources(memory::Allocator(), other.unique_resources) + { + } + + template + ResourceUser::ResourceUser(std::initializer_list> list) + : scope_level(TaskCtx::scope_depth()) + , access_list(memory::Allocator()) + , unique_resources(memory::Allocator()) + { + for(auto& ra : list) + add_resource_access(ra); + } + + template + void ResourceUser::add_resource_access(ResourceAccess ra) + { + this->access_list.push(ra); + std::shared_ptr> r = ra.get_resource(); + unique_resources.push(ResourceUsageEntry{r, r->users.rend()}); + } + + template + void ResourceUser::rm_resource_access(ResourceAccess ra) + { + this->access_list.erase(ra); + } + + template + void ResourceUser::build_unique_resource_list() + { + for(auto ra = access_list.rbegin(); ra != access_list.rend(); ++ra) + { + std::shared_ptr> r = ra->get_resource(); + unique_resources.erase(ResourceUsageEntry{r, r->users.rend()}); + unique_resources.push(ResourceUsageEntry{r, r->users.rend()}); + } + } + + template + bool ResourceUser::has_sync_access(std::shared_ptr> const& res) + { + for(auto ra = access_list.rbegin(); ra != access_list.rend(); ++ra) + { + if(ra->get_resource() == res && ra->is_synchronizing()) + return true; + } + return false; + } + + template + bool ResourceUser::is_superset_of(ResourceUser const& a) const + { + TRACE_EVENT("ResourceUser", "is_superset"); + for(auto ra = a.access_list.rbegin(); ra != a.access_list.rend(); ++ra) + { + bool found = false; + for(auto r = access_list.rbegin(); r != access_list.rend(); ++r) + if(r->is_superset_of(*ra)) + found = true; + + if(!found && ra->scope_level() <= scope_level) + // a introduced a new resource + return false; + } + return true; + } + +} // namespace redGrapes diff --git a/redGrapes/scheduler/cuda_thread_scheduler.hpp b/redGrapes/scheduler/cuda_thread_scheduler.hpp new file mode 100644 index 00000000..46bc552f --- /dev/null +++ b/redGrapes/scheduler/cuda_thread_scheduler.hpp @@ -0,0 +1,73 @@ +/* Copyright 2024 Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "redGrapes/TaskFreeCtx.hpp" +#include "redGrapes/dispatch/cuda/cuda_worker.hpp" +#include "redGrapes/scheduler/thread_scheduler.hpp" + +#include + +namespace redGrapes +{ + namespace scheduler + { + template + class CudaThreadScheduler : public ThreadScheduler> + { + private: + unsigned num_streams; + + public: + CudaThreadScheduler(unsigned num_streams = 1) : num_streams{num_streams} + { + } + + void init(WorkerId base_id) override + { + this->m_base_id = base_id; + // TODO check if it was already initalized + if(!this->m_worker_thread) + { + unsigned pu_id = base_id % TaskFreeCtx::n_pus; + // allocate worker with id `i` on arena `i`, + hwloc_obj_t obj = hwloc_get_obj_by_type(TaskFreeCtx::hwloc_ctx.topology, HWLOC_OBJ_PU, pu_id); + TaskFreeCtx::worker_alloc_pool->allocs.emplace_back( + memory::HwlocAlloc(TaskFreeCtx::hwloc_ctx, obj), + REDGRAPES_ALLOC_CHUNKSIZE); + + this->m_worker_thread + = memory::alloc_shared_bind>>( + this->m_base_id, + obj, + this->m_base_id, + num_streams); + } + } + + /*! whats the task dependency type for the edge a -> b (task a precedes task b) + * @return true if task b depends on the pre event of task a, false if task b depends on the post event + * of task b. + */ + bool task_dependency_type(TTask const& a, TTask const& b) + { + if(a.m_cuda_stream_idx) + return true; + else + return false; + ; + } + + cudaStream_t getCudaStream(unsigned idx) const + { + assert(idx < num_streams); + return this->m_worker_thread->worker->streams[idx].cuda_stream; + } + }; + } // namespace scheduler +} // namespace redGrapes diff --git a/redGrapes/scheduler/default_scheduler.cpp b/redGrapes/scheduler/default_scheduler.cpp deleted file mode 100644 index bf2366fe..00000000 --- a/redGrapes/scheduler/default_scheduler.cpp +++ /dev/null @@ -1,211 +0,0 @@ - -#include -#include -#include -#include - -#include - -namespace redGrapes -{ - namespace scheduler - { - - DefaultScheduler::DefaultScheduler() - { - } - - void DefaultScheduler::idle() - { - SPDLOG_TRACE("DefaultScheduler::idle()"); - - /* the main thread shall not do any busy waiting - * and always sleep right away in order to - * not block any worker threads (those however should - * busy-wait to improve latency) - */ - cv.timeout = 0; - cv.wait(); - } - - /* send the new task to a worker - */ - void DefaultScheduler::emplace_task(Task& task) - { - // todo: properly store affinity information in task - dispatch::thread::WorkerId worker_id = task.arena_id % SingletonContext::get().worker_pool->size(); - - SingletonContext::get().worker_pool->get_worker(worker_id).emplace_task(task); - - - /* hack as of 2023/11/17 - * - * Additionally to the worker who got the new task above, - * we will now notify another, available (idling) worker, - * in trying to avoid stale tasks in cases where new tasks - * are assigned to an already busy worker. - */ -#ifndef REDGRAPES_EMPLACE_NOTIFY_NEXT -# define REDGRAPES_EMPLACE_NOTIFY_NEXT 0 -#endif - -#if REDGRAPES_EMPLACE_NOTIFY_NEXT - auto id = SingletonContext::get().worker_pool->probe_worker_by_state( - [](unsigned idx) - { - SingletonContext::get().worker_pool->get_worker(idx).wake(); - return idx; - }, - dispatch::thread::WorkerState::AVAILABLE, - worker_id, - true); -#endif - } - - /* send this already existing task to a worker, - * but only through follower-list so it is not assigned to a worker yet. - * since this task is now ready, send find a worker for it - */ - void DefaultScheduler::activate_task(Task& task) - { - //! worker id to use in case all workers are busy - static thread_local std::atomic next_worker( - SingletonContext::get().current_worker ? SingletonContext::get().current_worker->get_worker_id() + 1 - : 0); - TRACE_EVENT("Scheduler", "activate_task"); - SPDLOG_TRACE("DefaultScheduler::activate_task({})", task.task_id); - - int worker_id = SingletonContext::get().worker_pool->find_free_worker(); - if(worker_id < 0) - { - worker_id = next_worker.fetch_add(1) % SingletonContext::get().worker_pool->size(); - if(worker_id == SingletonContext::get().current_worker->get_worker_id()) - worker_id = next_worker.fetch_add(1) % SingletonContext::get().worker_pool->size(); - } - - SingletonContext::get().worker_pool->get_worker(worker_id).ready_queue.push(&task); - SingletonContext::get().worker_pool->set_worker_state(worker_id, dispatch::thread::WorkerState::BUSY); - SingletonContext::get().worker_pool->get_worker(worker_id).wake(); - } - - /* tries to find a task with uninialized dependency edges in the - * task-graph in the emplacement queues of other workers - * and removes it from there - */ - Task* DefaultScheduler::steal_new_task(dispatch::thread::Worker& worker) - { - std::optional task = SingletonContext::get().worker_pool->probe_worker_by_state( - [&worker](unsigned idx) -> std::optional - { - // we have a candidate of a busy worker, - // now check its queue - if(Task* t = SingletonContext::get().worker_pool->get_worker(idx).emplacement_queue.pop()) - return t; - - // otherwise check own queue again - else if(Task* t = worker.emplacement_queue.pop()) - return t; - - // else continue search - else - return std::nullopt; - }, - - // find a busy worker - dispatch::thread::WorkerState::BUSY, - - // start next to current worker - worker.get_worker_id()); - - return task ? *task : nullptr; - } - - /* tries to find a ready task in any queue of other workers - * and removes it from the queue - */ - Task* DefaultScheduler::steal_ready_task(dispatch::thread::Worker& worker) - { - std::optional task = SingletonContext::get().worker_pool->probe_worker_by_state( - [&worker](unsigned idx) -> std::optional - { - // we have a candidate of a busy worker, - // now check its queue - if(Task* t = SingletonContext::get().worker_pool->get_worker(idx).ready_queue.pop()) - return t; - - // otherwise check own queue again - else if(Task* t = worker.ready_queue.pop()) - return t; - - // else continue search - else - return std::nullopt; - }, - - // find a busy worker - dispatch::thread::WorkerState::BUSY, - - // start next to current worker - worker.get_worker_id()); - - return task ? *task : nullptr; - } - - // give worker a ready task if available - // @return task if a new task was found, nullptr otherwise - Task* DefaultScheduler::steal_task(dispatch::thread::Worker& worker) - { - unsigned worker_id = worker.get_worker_id(); - - SPDLOG_INFO("steal task for worker {}", worker_id); - - if(Task* task = steal_ready_task(worker)) - { - SingletonContext::get().worker_pool->set_worker_state(worker_id, dispatch::thread::WorkerState::BUSY); - return task; - } - - if(Task* task = steal_new_task(worker)) - { - task->pre_event.up(); - task->init_graph(); - - if(task->get_pre_event().notify(true)) - { - SingletonContext::get().worker_pool->set_worker_state( - worker_id, - dispatch::thread::WorkerState::BUSY); - return task; - } - } - - return nullptr; - } - - /* Wakeup some worker or the main thread - * - * WakerId = 0 for main thread - * WakerId = WorkerId + 1 - * - * @return true if thread was indeed asleep - */ - bool DefaultScheduler::wake(WakerId id) - { - if(id == 0) - return cv.notify(); - else if(id > 0 && id <= SingletonContext::get().worker_pool->size()) - return SingletonContext::get().worker_pool->get_worker(id - 1).wake(); - else - return false; - } - - /* wakeup all wakers (workers + main thread) - */ - void DefaultScheduler::wake_all() - { - for(uint16_t i = 0; i <= SingletonContext::get().worker_pool->size(); ++i) - this->wake(i); - } - - } // namespace scheduler -} // namespace redGrapes diff --git a/redGrapes/scheduler/default_scheduler.hpp b/redGrapes/scheduler/default_scheduler.hpp deleted file mode 100644 index 02ddc1ca..00000000 --- a/redGrapes/scheduler/default_scheduler.hpp +++ /dev/null @@ -1,72 +0,0 @@ - -#pragma once - -#include -#include -#include -#include - -#include - -#include -#include - -namespace redGrapes -{ - namespace scheduler - { - - /* - * Uses simple round-robin algorithm to distribute tasks to workers - * and implements work-stealing - */ - struct DefaultScheduler : public IScheduler - { - CondVar cv; - - DefaultScheduler(); - - void idle(); - - /* send the new task to a worker - */ - void emplace_task(Task& task); - - /* send this already existing, - * but only through follower-list so it is not assigned to a worker yet. - * since this task is now ready, send find a worker for it - */ - void activate_task(Task& task); - - /* tries to find a task with uninialized dependency edges in the - * task-graph in the emplacement queues of other workers - * and removes it from there - */ - Task* steal_new_task(dispatch::thread::Worker& worker); - - /* tries to find a ready task in any queue of other workers - * and removes it from the queue - */ - Task* steal_ready_task(dispatch::thread::Worker& worker); - - // give worker a ready task if available - // @return task if a new task was found, nullptr otherwise - Task* steal_task(dispatch::thread::Worker& worker); - - /* Wakeup some worker or the main thread - * - * WakerId = 0 for main thread - * WakerId = WorkerId + 1 - * - * @return true if thread was indeed asleep - */ - bool wake(WakerId id = 0); - - /* wakeup all wakers (workers + main thread) - */ - void wake_all(); - }; - - } // namespace scheduler - -} // namespace redGrapes diff --git a/redGrapes/scheduler/event.hpp b/redGrapes/scheduler/event.hpp index 8ff2c5c3..239acf8c 100644 --- a/redGrapes/scheduler/event.hpp +++ b/redGrapes/scheduler/event.hpp @@ -1,4 +1,4 @@ -/* Copyright 2019-2020 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,16 +7,13 @@ #pragma once -#include -#include +#include "redGrapes/scheduler/scheduler.hpp" +#include "redGrapes/util/chunked_list.hpp" #include #include -#include #include -#include -#include #ifndef REDGRAPES_EVENT_FOLLOWER_LIST_CHUNKSIZE # define REDGRAPES_EVENT_FOLLOWER_LIST_CHUNKSIZE 16 @@ -25,12 +22,9 @@ namespace redGrapes { - - struct Task; - namespace scheduler { - + template struct Event; enum EventPtrTag @@ -43,25 +37,26 @@ namespace redGrapes T_EVT_EXT, }; + template struct EventPtr { enum EventPtrTag tag; - Task* task; - std::shared_ptr external_event; + TTask* task; + std::shared_ptr> external_event; - inline bool operator==(EventPtr const& other) const + inline bool operator==(EventPtr const& other) const { return this->tag == other.tag && this->task == other.task; } - Event& get_event() const; + Event& get_event() const; - inline Event& operator*() const + inline Event& operator*() const { return get_event(); } - inline Event* operator->() const + inline Event* operator->() const { return &get_event(); } @@ -86,6 +81,7 @@ namespace redGrapes * This order is an homomorphic image from the timeline of * execution states. */ + template struct Event { /*! number of incoming edges @@ -97,7 +93,7 @@ namespace redGrapes WakerId waker_id; //! the set of subsequent events - ChunkedList followers; + ChunkedList, REDGRAPES_EVENT_FOLLOWER_LIST_CHUNKSIZE> followers; Event(); Event(Event&); @@ -109,8 +105,8 @@ namespace redGrapes void dn(); //! note: follower has to be notified separately! - void remove_follower(EventPtr follower); - void add_follower(EventPtr follower); + void remove_follower(EventPtr follower); + void add_follower(EventPtr follower); void notify_followers(); }; @@ -118,3 +114,6 @@ namespace redGrapes } // namespace scheduler } // namespace redGrapes + +#include "redGrapes/scheduler/event.tpp" +#include "redGrapes/scheduler/event_ptr.tpp" diff --git a/redGrapes/scheduler/event.cpp b/redGrapes/scheduler/event.tpp similarity index 71% rename from redGrapes/scheduler/event.cpp rename to redGrapes/scheduler/event.tpp index 566c9c49..d8f69162 100644 --- a/redGrapes/scheduler/event.cpp +++ b/redGrapes/scheduler/event.tpp @@ -1,70 +1,72 @@ -/* Copyright 2022 Michael Sippel +/* Copyright 2022-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#pragma once -#include -#include -#include -#include -#include -#include -#include +#include "redGrapes/scheduler/event.hpp" +#include "redGrapes/util/trace.hpp" #include #include #include -#include -#include -#include namespace redGrapes { namespace scheduler { - - Event::Event() : state(1), waker_id(-1), followers(memory::Allocator()) + template + Event::Event() : state(1) + , waker_id(-1) + , followers(memory::Allocator()) { } - Event::Event(Event& other) + template + Event::Event(Event& other) : state((uint16_t) other.state) , waker_id(other.waker_id) , followers(memory::Allocator()) { } - Event::Event(Event&& other) + template + Event::Event(Event&& other) : state((uint16_t) other.state) , waker_id(other.waker_id) , followers(memory::Allocator()) { } - bool Event::is_reached() + template + bool Event::is_reached() { return state == 0; } - bool Event::is_ready() + template + bool Event::is_ready() { return state == 1; } - void Event::up() + template + void Event::up() { state++; } - void Event::dn() + template + void Event::dn() { state--; } - void Event::add_follower(EventPtr follower) + template + void Event::add_follower(EventPtr follower) { TRACE_EVENT("Event", "add_follower"); @@ -77,14 +79,16 @@ namespace redGrapes } //! note: follower has to be notified separately! - void Event::remove_follower(EventPtr follower) + template + void Event::remove_follower(EventPtr follower) { TRACE_EVENT("Event", "remove_follower"); followers.erase(follower); } - void Event::notify_followers() + template + void Event::notify_followers() { TRACE_EVENT("Event", "notify_followers"); @@ -101,7 +105,8 @@ namespace redGrapes * * @return true if event is ready */ - bool EventPtr::notify(bool claimed) + template + bool EventPtr::notify(bool claimed) { TRACE_EVENT("Event", "notify"); @@ -126,27 +131,28 @@ namespace redGrapes case EventPtrTag::T_EVT_EXT: tag_string = "external"; break; + case EventPtrTag::T_UNINITIALIZED: + tag_string = "uninitialized"; + break; } - if(this->task) + if(task) SPDLOG_TRACE( "notify event {} ({}-event of task {}) ~~> state = {}", - (void*) &this->get_event(), + (void*) &get_event(), tag_string, - this->task->task_id, + task->task_id, state); assert(old_state > 0); - bool remove_task = false; - if(task) { // pre event ready if(tag == scheduler::T_EVT_PRE && state == 1) { if(!claimed) - SingletonContext::get().scheduler->activate_task(*task); + task->scheduler_p->activate_task(*task); } // post event reached: @@ -155,14 +161,14 @@ namespace redGrapes if(state == 0 && tag == scheduler::T_EVT_POST) task->delete_from_resources(); } - + // TODO rework this to reduce if(task) checks // if event is ready or reached (state ∈ {0,1}) - if(state <= 1 && this->get_event().waker_id >= 0) - SingletonContext::get().scheduler->wake(this->get_event().waker_id); + if(state <= 1 && get_event().waker_id >= 0) + task->scheduler_p->wake(get_event().waker_id); if(state == 0) { - this->get_event().notify_followers(); + get_event().notify_followers(); // the second one of either post-event or result-get-event shall destroy the task if(task) diff --git a/redGrapes/scheduler/event_ptr.cpp b/redGrapes/scheduler/event_ptr.tpp similarity index 79% rename from redGrapes/scheduler/event_ptr.cpp rename to redGrapes/scheduler/event_ptr.tpp index fcad9710..2c1d0269 100644 --- a/redGrapes/scheduler/event_ptr.cpp +++ b/redGrapes/scheduler/event_ptr.tpp @@ -1,22 +1,18 @@ -/* Copyright 2022 Michael Sippel +/* Copyright 2022-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include -#include -#include - -#include +#pragma once +#include "redGrapes/scheduler/event.hpp" namespace redGrapes { namespace scheduler { - - Event& EventPtr::get_event() const + template + Event& EventPtr::get_event() const { switch(tag) { diff --git a/redGrapes/scheduler/mpi_thread_scheduler.hpp b/redGrapes/scheduler/mpi_thread_scheduler.hpp new file mode 100644 index 00000000..96d1d894 --- /dev/null +++ b/redGrapes/scheduler/mpi_thread_scheduler.hpp @@ -0,0 +1,34 @@ +/* Copyright 2024 Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "redGrapes/dispatch/mpi/mpiWorker.hpp" +#include "redGrapes/dispatch/mpi/request_pool.hpp" +#include "redGrapes/scheduler/thread_scheduler.hpp" + +#include + +namespace redGrapes +{ + namespace scheduler + { + + template + struct MPIThreadScheduler : public ThreadScheduler> + { + // if worker is MPI worker + std::shared_ptr> getRequestPool() + { + return this->m_worker_thread->worker->requestPool; + } + }; + + + } // namespace scheduler + +} // namespace redGrapes diff --git a/redGrapes/scheduler/pool_scheduler.hpp b/redGrapes/scheduler/pool_scheduler.hpp new file mode 100644 index 00000000..c5d13a95 --- /dev/null +++ b/redGrapes/scheduler/pool_scheduler.hpp @@ -0,0 +1,78 @@ +/* Copyright 2024 Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "redGrapes/dispatch/thread/worker_pool.hpp" +#include "redGrapes/scheduler/scheduler.hpp" +#include "redGrapes/sync/cv.hpp" + +#include + +#include + +namespace redGrapes +{ + namespace scheduler + { + + /* + * Uses simple round-robin algorithm to distribute tasks to workers + * and implements work-stealing + */ + template + struct PoolScheduler : public IScheduler + { + using TTask = Worker::task_type; + WorkerId m_base_id; + CondVar cv; + WorkerId local_next_worker_id = 0; + unsigned n_workers; + std::shared_ptr> m_worker_pool; + + PoolScheduler(unsigned num_workers); + PoolScheduler(std::shared_ptr> workerPool); + + void idle(); + + /* send the new task to a worker + */ + void emplace_task(TTask& task); + + /* send this already existing, + * but only through follower-list so it is not assigned to a worker yet. + * since this task is now ready, send find a worker for it + */ + void activate_task(TTask& task); + + /* Wakeup some worker or the main thread + * + * WakerId = 0 for main thread + * WakerId = WorkerId + 1 + * + * @return true if thread was indeed asleep + */ + bool wake(WakerId id = 0); + + /* wakeup all wakers (workers + main thread) + */ + void wake_all(); + + unsigned getNextWorkerID(); + + void init(WorkerId base_id); + + void startExecution(); + + void stopExecution(); + }; + + } // namespace scheduler + +} // namespace redGrapes + +#include "redGrapes/scheduler/pool_scheduler.tpp" diff --git a/redGrapes/scheduler/pool_scheduler.tpp b/redGrapes/scheduler/pool_scheduler.tpp new file mode 100644 index 00000000..fb89fd8e --- /dev/null +++ b/redGrapes/scheduler/pool_scheduler.tpp @@ -0,0 +1,172 @@ +/* Copyright 2024 Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "redGrapes/TaskFreeCtx.hpp" +#include "redGrapes/dispatch/thread/DefaultWorker.hpp" +#include "redGrapes/scheduler/pool_scheduler.hpp" +#include "redGrapes/util/trace.hpp" + +#include + +namespace redGrapes +{ + namespace scheduler + { + template + PoolScheduler::PoolScheduler(unsigned num_workers) + : n_workers(num_workers) + , m_worker_pool(std::make_shared>(num_workers)) + { + } + + template + PoolScheduler::PoolScheduler(std::shared_ptr> workerPool) + : m_worker_pool(workerPool) + { + } + + template + void PoolScheduler::idle() + { + SPDLOG_TRACE("PoolScheduler::idle()"); + + /* the main thread shall not do any busy waiting + * and always sleep right away in order to + * not block any worker threads (those however should + * busy-wait to improve latency) + */ + cv.timeout = 0; + cv.wait(); + } + + /* send the new task to a worker + */ + template + void PoolScheduler::emplace_task(TTask& task) + { + // TODO: properly store affinity information in task + WorkerId worker_id = task.worker_id - m_base_id; + + m_worker_pool->get_worker_thread(worker_id).worker->dispatch_task(task); + + /* hack as of 2023/11/17 + * + * Additionally to the worker who got the new task above, + * we will now notify another, available (idling) worker, + * in trying to avoid stale tasks in cases where new tasks + * are assigned to an already busy worker. + */ +#ifndef REDGRAPES_EMPLACE_NOTIFY_NEXT +# define REDGRAPES_EMPLACE_NOTIFY_NEXT 0 +#endif + +#if REDGRAPES_EMPLACE_NOTIFY_NEXT + auto id = m_worker_pool->probe_worker_by_state( + [&m_worker_pool](unsigned idx) + { + m_worker_pool->get_worker_thread(idx).worker->wake(); + return idx; + }, + dispatch::thread::WorkerState::AVAILABLE, + worker_id, + true); +#endif + } + + /* send this already existing task to a worker, + * but only through follower-list so it is not assigned to a worker yet. + * since this task is now ready, send find a worker for it + */ + template + void PoolScheduler::activate_task(TTask& task) + { + //! worker id to use in case all workers are busy + // TODO analyse and optimize + static thread_local std::atomic next_worker( + TaskFreeCtx::current_worker_id ? *TaskFreeCtx::current_worker_id + 1 - m_base_id : 0); + TRACE_EVENT("Scheduler", "activate_task"); + SPDLOG_TRACE("PoolScheduler::activate_task({})", task.task_id); + + int worker_id = m_worker_pool->find_free_worker(); + if(worker_id < 0) + { + worker_id = next_worker.fetch_add(1) % n_workers; + if(worker_id == *TaskFreeCtx::current_worker_id) + worker_id = next_worker.fetch_add(1) % n_workers; + } + + m_worker_pool->get_worker_thread(worker_id).worker->ready_queue.push(&task); + m_worker_pool->set_worker_state(worker_id, dispatch::thread::WorkerState::BUSY); + m_worker_pool->get_worker_thread(worker_id).worker->wake(); + } + + /* Wakeup some worker or the main thread + * + * WakerId = 0 for main thread + * WakerId = WorkerId + 1 + * + * @return true if thread was indeed asleep + */ + template + bool PoolScheduler::wake(WakerId id) + { + auto local_waker_id = id - m_base_id; + if(local_waker_id == 0) + return cv.notify(); + // TODO analyse and optimize + else if(local_waker_id > 0 && local_waker_id <= n_workers) + return m_worker_pool->get_worker_thread(local_waker_id - 1).worker->wake(); + else + return false; + } + + /* wakeup all wakers (workers + main thread) + */ + template + void PoolScheduler::wake_all() + { + wake(0); + for(uint16_t i = m_base_id; i < m_base_id + n_workers; ++i) + wake(i); + } + + template + unsigned PoolScheduler::getNextWorkerID() + { + // TODO make atomic + auto id = local_next_worker_id + m_base_id; + local_next_worker_id = (local_next_worker_id + 1) % n_workers; + return id; + } + + template + void PoolScheduler::init(WorkerId base_id) + { + // TODO check if it was already initalized + m_base_id = base_id; + m_worker_pool->emplace_workers(m_base_id); + } + + template + void PoolScheduler::startExecution() + { + // TODO check if it was already started + m_worker_pool->start(); + } + + template + void PoolScheduler::stopExecution() + { + // TODO check if it was already stopped + m_worker_pool->stop(); + } + + + } // namespace scheduler +} // namespace redGrapes diff --git a/redGrapes/scheduler/scheduler.hpp b/redGrapes/scheduler/scheduler.hpp index 68897639..fc1b3581 100644 --- a/redGrapes/scheduler/scheduler.hpp +++ b/redGrapes/scheduler/scheduler.hpp @@ -1,4 +1,4 @@ -/* Copyright 2020 Michael Sippel +/* Copyright 2020-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -9,21 +9,8 @@ #include -#include - namespace redGrapes { - - struct Task; - - namespace dispatch - { - namespace thread - { - struct Worker; - } // namespace thread - } // namespace dispatch - namespace scheduler { @@ -31,6 +18,7 @@ namespace redGrapes /*! Scheduler Interface */ + template struct IScheduler { virtual ~IScheduler() @@ -41,7 +29,7 @@ namespace redGrapes * @return true if task b depends on the pre event of task a, false if task b depends on the post event of * task b. */ - virtual bool task_dependency_type(Task const& a, Task const& b) + virtual bool task_dependency_type(TTask const& a, TTask const& b) { return false; } @@ -51,21 +39,15 @@ namespace redGrapes } //! add task to the set of to-initialize tasks - virtual void emplace_task(Task& task) + virtual void emplace_task(TTask& task) { } //! add task to ready set - virtual void activate_task(Task& task) + virtual void activate_task(TTask& task) { } - //! give worker work if available - virtual Task* steal_task(dispatch::thread::Worker& worker) - { - return nullptr; - } - virtual void wake_all() { } @@ -74,6 +56,26 @@ namespace redGrapes { return false; } + + virtual unsigned getNextWorkerID() + { + return 0; + } + + // initialize the execution context pointed to by the scheduler + virtual void init() + { + } + + // start the execution context pointed to by the scheduler + virtual void startExecution() + { + } + + // stop the execution context pointed to by the scheduler + virtual void stopExecution() + { + } }; } // namespace scheduler diff --git a/redGrapes/scheduler/tag_match.hpp b/redGrapes/scheduler/tag_match.hpp deleted file mode 100644 index 4fd95333..00000000 --- a/redGrapes/scheduler/tag_match.hpp +++ /dev/null @@ -1,137 +0,0 @@ -/* Copyright 2020 Michael Sippel - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -#pragma once - - -#include -#include -#include -#include - -#include - -#include - -namespace redGrapes -{ - namespace scheduler - { - - template - struct TagMatch : IScheduler - { - struct SubScheduler - { - std::bitset supported_tags; - std::shared_ptr s; - }; - - std::vector sub_schedulers; - - void add_scheduler(std::bitset supported_tags, std::shared_ptr s) - { - sub_schedulers.push_back(SubScheduler{supported_tags, s}); - } - - void add_scheduler(std::initializer_list tag_list, std::shared_ptr s) - { - std::bitset supported_tags; - for(auto tag : tag_list) - supported_tags.set(tag); - this->add_scheduler(supported_tags, s); - } - - Task* steal_task(dispatch::thread::Worker& worker) - { - for(auto& s : sub_schedulers) - if(Task* t = s.s->steal_task(worker)) - return t; - - return nullptr; - } - - void emplace_task(Task& task) - { - if(auto sub_scheduler = get_matching_scheduler(task.required_scheduler_tags)) - return (*sub_scheduler)->emplace_task(task); - else - throw std::runtime_error("no scheduler found for task"); - } - - void activate_task(Task& task) - { - if(auto sub_scheduler = get_matching_scheduler(task.required_scheduler_tags)) - return (*sub_scheduler)->activate_task(task); - else - throw std::runtime_error("no scheduler found for task"); - } - - std::optional> get_matching_scheduler( - std::bitset const& required_tags) - { - for(auto const& s : sub_schedulers) - if((s.supported_tags & required_tags) == required_tags) - return s.s; - - return std::nullopt; - } - - bool task_dependency_type(Task const& a, Task& b) - { - /// fixme: b or a ? - if(auto sub_scheduler = get_matching_scheduler(b.required_scheduler_tags)) - return (*sub_scheduler)->task_dependency_type(a, b); - else - throw std::runtime_error("no scheduler found for task"); - } - - void wake_all() - { - for(auto const& s : sub_schedulers) - s.s->wake_all(); - } - - bool wake(WakerId waker_id) - { - for(auto const& s : sub_schedulers) - if(s.s->wake(waker_id)) - return true; - - return false; - } - }; - - /*! Factory function to easily create a tag-match-scheduler object - */ - template - struct TagMatchBuilder - { - std::shared_ptr> tag_match; - - operator std::shared_ptr() const - { - return tag_match; - } - - TagMatchBuilder add(std::initializer_list tags, std::shared_ptr s) - { - tag_match->add_scheduler(tags, s); - return *this; - } - }; - - template - auto make_tag_match_scheduler() - { - return TagMatchBuilder{std::make_shared>()}; - } - - - } // namespace scheduler - -} // namespace redGrapes diff --git a/redGrapes/scheduler/tag_match_property.hpp b/redGrapes/scheduler/tag_match_property.hpp deleted file mode 100644 index bff1ae11..00000000 --- a/redGrapes/scheduler/tag_match_property.hpp +++ /dev/null @@ -1,92 +0,0 @@ - -#pragma once - -#include - -#include -#include -#include - -namespace redGrapes -{ - namespace scheduler - { - template - struct SchedulingTagProperties - { - std::bitset required_scheduler_tags; - - template - struct Builder - { - PropertiesBuilder& builder; - - Builder(PropertiesBuilder& b) : builder(b) - { - } - - PropertiesBuilder& scheduling_tags(std::initializer_list tags) - { - std::bitset tags_bitset; - for(auto tag : tags) - tags_bitset.set(tag); - return scheduling_tags(tags_bitset); - } - - PropertiesBuilder& scheduling_tags(std::bitset tags) - { - builder.task->required_scheduler_tags |= tags; - return builder; - } - }; - - struct Patch - { - template - struct Builder - { - Builder(PatchBuilder&) - { - } - }; - }; - - void apply_patch(Patch const&) - { - } - }; - } // namespace scheduler -} // namespace redGrapes - -template -struct fmt::formatter> -{ - constexpr auto parse(format_parse_context& ctx) - { - return ctx.begin(); - } - - template - auto format(redGrapes::scheduler::SchedulingTagProperties const& prop, FormatContext& ctx) - { - auto out = ctx.out(); - - out = fmt::format_to(out, "\"schedulingTags\" : ["); - - bool first = true; - for(size_t i = 0; i < T_tag_count; ++i) - { - if(prop.required_scheduler_tags.test(i)) - { - if(!first) - out = format_to(out, ", "); - - first = false; - out = format_to(out, "{}", (Tag) i); - } - } - - out = fmt::format_to(out, "]"); - return out; - } -}; diff --git a/redGrapes/scheduler/thread_scheduler.hpp b/redGrapes/scheduler/thread_scheduler.hpp new file mode 100644 index 00000000..50569eb6 --- /dev/null +++ b/redGrapes/scheduler/thread_scheduler.hpp @@ -0,0 +1,140 @@ +/* Copyright 2024 Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "redGrapes/TaskFreeCtx.hpp" +#include "redGrapes/dispatch/thread/WorkerThread.hpp" +#include "redGrapes/scheduler/scheduler.hpp" +#include "redGrapes/sync/cv.hpp" + +#include + +#include + +namespace redGrapes +{ + namespace scheduler + { + + template + struct ThreadScheduler : public IScheduler + { + using TTask = Worker::task_type; + + WorkerId m_base_id; + CondVar cv; + std::shared_ptr> m_worker_thread; + static constexpr unsigned n_workers = 1; + + ThreadScheduler() + { + } + + ThreadScheduler(std::shared_ptr> workerThread) + : m_worker_thread(workerThread) + { + } + + void idle() + { + SPDLOG_TRACE("ThreadScheduler::idle()"); + + /* the main thread shall not do any busy waiting + * and always sleep right away in order to + * not block any worker threads (those however should + * busy-wait to improve latency) + */ + cv.timeout = 0; + cv.wait(); + } + + /* send the new task to a worker + */ + void emplace_task(TTask& task) + { + // todo: properly store affinity information in task + m_worker_thread->worker->dispatch_task(task); + } + + /* send this already existing, + * but only through follower-list so it is not assigned to a worker yet. + * since this task is now ready, send find a worker for it + */ + void activate_task(TTask& task) + { + //! worker id to use in case all workers are busy + TRACE_EVENT("Scheduler", "activate_task"); + SPDLOG_TRACE("ThreadScheduler::activate_task({})", task.task_id); + + m_worker_thread->worker->ready_queue.push(&task); + m_worker_thread->worker->wake(); + } + + /* Wakeup some worker or the main thread + * + * WakerId = 0 for main thread + * WakerId = WorkerId + 1 + * + * @return true if thread was indeed asleep + */ + bool wake(WakerId id = 0) + { + if(id == 0) + return cv.notify(); + else if(id > 0 && id <= 1) + return m_worker_thread->worker->wake(); + else + return false; + } + + /* wakeup all wakers (workers + main thread) + */ + void wake_all() + { + cv.notify(); + m_worker_thread->worker->wake(); + } + + unsigned getNextWorkerID() + { + return m_base_id; + } + + virtual void init(WorkerId base_id) + { + m_base_id = base_id; + // TODO check if it was already initalized + if(!m_worker_thread) + { + unsigned pu_id = base_id % TaskFreeCtx::n_pus; + // allocate worker with id `i` on arena `i`, + hwloc_obj_t obj = hwloc_get_obj_by_type(TaskFreeCtx::hwloc_ctx.topology, HWLOC_OBJ_PU, pu_id); + TaskFreeCtx::worker_alloc_pool->allocs.emplace_back( + memory::HwlocAlloc(TaskFreeCtx::hwloc_ctx, obj), + REDGRAPES_ALLOC_CHUNKSIZE); + + m_worker_thread + = memory::alloc_shared_bind>(m_base_id, obj, m_base_id); + } + } + + void startExecution() + { + m_worker_thread->start(); + } + + void stopExecution() + { + m_worker_thread->stop(); + } + }; + + + } // namespace scheduler + +} // namespace redGrapes diff --git a/redGrapes/sync/cv.cpp b/redGrapes/sync/cv.cpp deleted file mode 100644 index 7f4cf90f..00000000 --- a/redGrapes/sync/cv.cpp +++ /dev/null @@ -1,55 +0,0 @@ - -#include - -#include -#ifndef REDGRAPES_CONDVAR_TIMEOUT -# define REDGRAPES_CONDVAR_TIMEOUT 0x20'0000 -#endif - -namespace redGrapes -{ - CondVar::CondVar() : CondVar(REDGRAPES_CONDVAR_TIMEOUT) - { - } - - CondVar::CondVar(unsigned timeout) : should_wait(true), timeout(timeout) - { - } - - void CondVar::wait() - { - unsigned count = 0; - while(should_wait.load(std::memory_order_acquire)) - { - if(++count > timeout) - { - // TODO: check this opmitization - // busy.clear(std::memory_order_release); - - if(should_wait.load(std::memory_order_acquire)) - { - std::unique_lock l(m); - cv.wait(l, [this] { return !should_wait.load(std::memory_order_acquire); }); - } - } - } - - should_wait.store(true); - } - - bool CondVar::notify() - { - bool w = true; - should_wait.compare_exchange_strong(w, false, std::memory_order_release); - - // TODO: check this optimization - // if( ! busy.test_and_set(std::memory_order_acquire) ) - { - std::unique_lock l(m); - cv.notify_all(); - } - - return w; - } - -} // namespace redGrapes diff --git a/redGrapes/sync/cv.hpp b/redGrapes/sync/cv.hpp index 3621ee42..06662ef1 100644 --- a/redGrapes/sync/cv.hpp +++ b/redGrapes/sync/cv.hpp @@ -1,11 +1,19 @@ +/* Copyright 2023-2024 Michael Sippel, Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ #pragma once -#include - #include #include +#ifndef REDGRAPES_CONDVAR_TIMEOUT +# define REDGRAPES_CONDVAR_TIMEOUT 0x20'0000 +#endif + namespace redGrapes { @@ -24,18 +32,54 @@ namespace redGrapes { std::atomic should_wait; std::condition_variable cv; - std::atomic_flag busy; - using CVMutex = std::mutex; CVMutex m; unsigned timeout; - CondVar(); - CondVar(unsigned timeout); + CondVar() : CondVar(REDGRAPES_CONDVAR_TIMEOUT) + { + } - void wait(); - bool notify(); + CondVar(unsigned timeout) : should_wait(true), timeout(timeout) + { + } + + void wait() + { + unsigned count = 0; + while(should_wait.load(std::memory_order_acquire)) + { + if(++count > timeout) + { + // TODO: check this opmitization + // busy.clear(std::memory_order_release); + + if(should_wait.load(std::memory_order_acquire)) + { + std::unique_lock l(m); + cv.wait(l, [this] { return !should_wait.load(std::memory_order_acquire); }); + } + } + } + + should_wait.store(true); + } + + bool notify() + { + bool w = true; + should_wait.compare_exchange_strong(w, false, std::memory_order_release); + + // TODO: check this optimization + // if( ! busy.test_and_set(std::memory_order_acquire) ) + { + std::unique_lock l(m); + cv.notify_all(); + } + + return w; + } }; } // namespace redGrapes diff --git a/redGrapes/task/future.hpp b/redGrapes/task/future.hpp index 653d7206..5784f55a 100644 --- a/redGrapes/task/future.hpp +++ b/redGrapes/task/future.hpp @@ -1,4 +1,4 @@ -/* Copyright 2019-2021 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -10,22 +10,20 @@ */ #pragma once -#include -#include +#include "redGrapes/TaskCtx.hpp" namespace redGrapes { - void yield(scheduler::EventPtr event); /*! * Wrapper for std::future which consumes jobs * instead of waiting in get() */ - template + template struct Future { - Future(Task& task) : task(task), taken(false) + Future(TTask& task) : task(task), taken(false) { } @@ -53,7 +51,7 @@ namespace redGrapes T get(void) { // wait until result is set - yield(task.get_result_set_event()); + TaskCtx::yield(task.get_result_set_event()); // take result T result = std::move(*reinterpret_cast(task.get_result_data())); @@ -72,13 +70,13 @@ namespace redGrapes private: bool taken; - Task& task; + TTask& task; }; // struct Future - template<> - struct Future + template + struct Future { - Future(Task& task) : task(task), taken(false) + Future(TTask& task) : task(task), taken(false) { } @@ -106,7 +104,7 @@ namespace redGrapes void get(void) { // wait until result is set - yield(task.get_result_set_event()); + TaskCtx::yield(task.get_result_set_event()); // take result taken = true; @@ -122,7 +120,7 @@ namespace redGrapes private: bool taken; - Task& task; + TTask& task; }; } // namespace redGrapes diff --git a/redGrapes/task/property/graph.hpp b/redGrapes/task/property/graph.hpp index d7d7cadc..684d0606 100644 --- a/redGrapes/task/property/graph.hpp +++ b/redGrapes/task/property/graph.hpp @@ -1,4 +1,4 @@ -/* Copyright 2019-2020 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,23 +7,17 @@ #pragma once +#include "redGrapes/scheduler/event.hpp" + #include -#include -#include #include -#include -#include -#include - -// #include -#include -#include namespace redGrapes { - struct Task; + // struct Task; + template struct TaskSpace; /*! @@ -45,28 +39,29 @@ namespace redGrapes * With child-tasks, the post-event of the child task * precedes the parent tasks post-event. */ + template struct GraphProperty { - Task& operator*() + TTask& operator*() { return *task; } - Task* operator->() + TTask* operator->() { return task; } - Task* task; + TTask* task; //! number of parents uint8_t scope_depth; //! task space that contains this task, must not be null - std::shared_ptr space; + std::shared_ptr> space; //! task space for children, may be null - std::shared_ptr children; + std::shared_ptr> children; /* // in edges dont need a mutex because they are initialized @@ -75,29 +70,29 @@ namespace redGrapes std::vector in_edges; */ - scheduler::Event pre_event; - scheduler::Event post_event; - scheduler::Event result_set_event; - scheduler::Event result_get_event; + scheduler::Event pre_event; + scheduler::Event post_event; + scheduler::Event result_set_event; + scheduler::Event result_get_event; - inline scheduler::EventPtr get_pre_event() + inline scheduler::EventPtr get_pre_event() { - return scheduler::EventPtr{scheduler::T_EVT_PRE, this->task}; + return scheduler::EventPtr{scheduler::T_EVT_PRE, this->task}; } - inline scheduler::EventPtr get_post_event() + inline scheduler::EventPtr get_post_event() { - return scheduler::EventPtr{scheduler::T_EVT_POST, this->task}; + return scheduler::EventPtr{scheduler::T_EVT_POST, this->task}; } - inline scheduler::EventPtr get_result_set_event() + inline scheduler::EventPtr get_result_set_event() { - return scheduler::EventPtr{scheduler::T_EVT_RES_SET, this->task}; + return scheduler::EventPtr{scheduler::T_EVT_RES_SET, this->task}; } - inline scheduler::EventPtr get_result_get_event() + inline scheduler::EventPtr get_result_get_event() { - return scheduler::EventPtr{scheduler::T_EVT_RES_GET, this->task}; + return scheduler::EventPtr{scheduler::T_EVT_RES_GET, this->task}; } inline bool is_ready() @@ -122,13 +117,13 @@ namespace redGrapes /*! create a new event which precedes the tasks post-event */ - scheduler::EventPtr make_event(); + scheduler::EventPtr make_event(); /*! * represent ›pausation of the task until event is reached‹ * in the scheduling graph */ - inline void sg_pause(scheduler::EventPtr event) + inline void sg_pause(scheduler::EventPtr event) { pre_event.state = 1; event->add_follower(get_pre_event()); @@ -148,7 +143,7 @@ namespace redGrapes * preceding task to the pre-event of this task. * Additionally, an edge to the post-event of the parent is added. */ - void add_dependency(Task& preceding_task); + void add_dependency(TTask& preceding_task); /*! * checks all incoming edges if they are still required and @@ -188,8 +183,8 @@ namespace redGrapes } // namespace redGrapes -template<> -struct fmt::formatter +template +struct fmt::formatter> { constexpr auto parse(format_parse_context& ctx) { @@ -197,8 +192,10 @@ struct fmt::formatter } template - auto format(redGrapes::GraphProperty const& sg_prop, FormatContext& ctx) + auto format(redGrapes::GraphProperty const& sg_prop, FormatContext& ctx) { return ctx.out(); } }; + +#include "redGrapes/task/property/graph.tpp" diff --git a/redGrapes/task/property/graph.cpp b/redGrapes/task/property/graph.tpp similarity index 70% rename from redGrapes/task/property/graph.cpp rename to redGrapes/task/property/graph.tpp index 3680c795..83089a2f 100644 --- a/redGrapes/task/property/graph.cpp +++ b/redGrapes/task/property/graph.tpp @@ -1,31 +1,28 @@ -/* Copyright 2019-2022 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include -#include -#include -#include -#include -#include -#include +#pragma once +#include "redGrapes/scheduler/event.hpp" +#include "redGrapes/sync/spinlock.hpp" +#include "redGrapes/task/property/graph.hpp" +#include "redGrapes/util/trace.hpp" #include -#include namespace redGrapes { /*! create a new (external) event which precedes the tasks post-event */ - scheduler::EventPtr GraphProperty::make_event() + template + scheduler::EventPtr GraphProperty::make_event() { - auto event = memory::alloc_shared(); + auto event = memory::alloc_shared>(); event->add_follower(get_post_event()); - return scheduler::EventPtr{scheduler::T_EVT_EXT, nullptr, event}; + return scheduler::EventPtr{scheduler::T_EVT_EXT, task, event}; } /*! @@ -34,10 +31,11 @@ namespace redGrapes * * The precedence graph containing the task is assumed to be locked. */ - void GraphProperty::init_graph() + template + void GraphProperty::init_graph() { TRACE_EVENT("Graph", "init_graph"); - for(auto r = this->task->unique_resources.rbegin(); r != this->task->unique_resources.rend(); ++r) + for(auto r = task->unique_resources.rbegin(); r != task->unique_resources.rend(); ++r) { if(r->task_entry != r->resource->users.rend()) { @@ -58,12 +56,12 @@ namespace redGrapes for(; it != r->resource->users.rend(); ++it) { TRACE_EVENT("Graph", "Check Pred"); - Task* preceding_task = *it; + TTask* preceding_task = *it; - if(preceding_task == this->space->parent) + if(preceding_task == space->parent) break; - if(preceding_task->space == this->space && this->space->is_serial(*preceding_task, *this->task)) + if(preceding_task->space == space && is_serial(*preceding_task, *task)) { add_dependency(*preceding_task); if(preceding_task->has_sync_access(r->resource)) @@ -81,10 +79,11 @@ namespace redGrapes } } - void GraphProperty::delete_from_resources() + template + void GraphProperty::delete_from_resources() { TRACE_EVENT("Graph", "delete_from_resources"); - for(auto r = this->task->unique_resources.rbegin(); r != this->task->unique_resources.rend(); ++r) + for(auto r = task->unique_resources.rbegin(); r != task->unique_resources.rend(); ++r) { // TODO: can this lock be avoided? // corresponding lock to init_graph() @@ -95,31 +94,33 @@ namespace redGrapes } } - void GraphProperty::add_dependency(Task& preceding_task) + template + void GraphProperty::add_dependency(TTask& preceding_task) { // precedence graph // in_edges.push_back(&preceding_task); // scheduling graph - auto preceding_event = SingletonContext::get().scheduler->task_dependency_type(preceding_task, *this->task) + auto preceding_event = task->scheduler_p->task_dependency_type(preceding_task, *task) ? preceding_task->get_pre_event() : preceding_task->get_post_event(); if(!preceding_event->is_reached()) - preceding_event->add_follower(this->get_pre_event()); + preceding_event->add_follower(get_pre_event()); } - void GraphProperty::update_graph() + template + void GraphProperty::update_graph() { // std::unique_lock< SpinLock > lock( post_event.followers_mutex ); // for( auto follower : post_event.followers ) for(auto it = post_event.followers.rbegin(); it != post_event.followers.rend(); ++it) { - scheduler::EventPtr follower = *it; + scheduler::EventPtr follower = *it; if(follower.task) { - if(!space->is_serial(*this->task, *follower.task)) + if(!is_serial(*task, *follower.task)) { // remove dependency // follower.task->in_edges.erase(std::find(std::begin(follower.task->in_edges), diff --git a/redGrapes/task/property/id.hpp b/redGrapes/task/property/id.hpp index f24f5c19..d18c9ebe 100644 --- a/redGrapes/task/property/id.hpp +++ b/redGrapes/task/property/id.hpp @@ -91,6 +91,6 @@ struct fmt::formatter template auto format(redGrapes::IDProperty const& id_prop, FormatContext& ctx) { - return format_to(ctx.out(), "\"id\" : {}", id_prop.task_id); + return fmt::format_to(ctx.out(), "\"id\" : {}", id_prop.task_id); } }; diff --git a/redGrapes/task/property/inherit.hpp b/redGrapes/task/property/inherit.hpp index fc277b4d..7f2b7c92 100644 --- a/redGrapes/task/property/inherit.hpp +++ b/redGrapes/task/property/inherit.hpp @@ -1,4 +1,4 @@ -/* Copyright 2019-2020 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -11,7 +11,7 @@ #pragma once -#include +#include "redGrapes/task/property/trait.hpp" #include @@ -20,7 +20,6 @@ namespace redGrapes { - struct Task; template struct TaskPropertiesInherit diff --git a/redGrapes/task/property/label.hpp b/redGrapes/task/property/label.hpp index 2c046585..18465716 100644 --- a/redGrapes/task/property/label.hpp +++ b/redGrapes/task/property/label.hpp @@ -1,4 +1,4 @@ -/* Copyright 2019 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -11,7 +11,7 @@ #pragma once -#include +#include "redGrapes/memory/allocator.hpp" #include @@ -71,6 +71,6 @@ struct fmt::formatter template auto format(redGrapes::LabelProperty const& label_prop, FormatContext& ctx) { - return format_to(ctx.out(), "\"label\" : \"{}\"", label_prop.label); + return fmt::format_to(ctx.out(), "\"label\" : \"{}\"", label_prop.label); } }; diff --git a/redGrapes/task/property/queue.hpp b/redGrapes/task/property/queue.hpp deleted file mode 100644 index d69d880a..00000000 --- a/redGrapes/task/property/queue.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright 2019-2020 Michael Sippel - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -#pragma once - -namespace redGrapes -{ - - struct Task; - - struct QueueProperty - { - // Task volatile * volatile next; - - template - struct Builder - { - PropertiesBuilder& builder; - - Builder(PropertiesBuilder& b) : builder(b) - { - } - }; - - struct Patch - { - template - struct Builder - { - Builder(PatchBuilder&) - { - } - }; - }; - - void apply_patch(Patch const&){}; - }; - -} // namespace redGrapes - -template<> -struct fmt::formatter -{ - constexpr auto parse(format_parse_context& ctx) - { - return ctx.begin(); - } - - template - auto format(redGrapes::QueueProperty const& label_prop, FormatContext& ctx) - { - return format_to(ctx.out(), ""); - } -}; diff --git a/redGrapes/task/property/resource.hpp b/redGrapes/task/property/resource.hpp index 9a2f57e8..f70c22c4 100644 --- a/redGrapes/task/property/resource.hpp +++ b/redGrapes/task/property/resource.hpp @@ -1,4 +1,4 @@ -/* Copyright 2019 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -11,19 +11,19 @@ #pragma once -#include -#include +#include "redGrapes/resource/resource_user.hpp" #include #include -#include +#include #include namespace redGrapes { - struct ResourceProperty : ResourceUser + template + struct ResourceProperty : ResourceUser { template struct Builder @@ -34,16 +34,16 @@ namespace redGrapes { } - PropertiesBuilder& resources(std::initializer_list list) + PropertiesBuilder& resources(std::initializer_list> list) { - for(ResourceAccess const& ra : list) + for(ResourceAccess const& ra : list) builder.task->access_list.push(ra); builder.task->build_unique_resource_list(); return builder; } - inline PropertiesBuilder& add_resource(ResourceAccess access) + inline PropertiesBuilder& add_resource(ResourceAccess access) { (*builder.task) += access; return builder; @@ -61,7 +61,7 @@ namespace redGrapes { } - PatchBuilder add_resources(std::initializer_list list) + PatchBuilder add_resources(std::initializer_list> list) { Patch& p = builder.patch; for(auto const& acc : list) @@ -69,7 +69,7 @@ namespace redGrapes return builder; } - PatchBuilder remove_resources(std::initializer_list list) + PatchBuilder remove_resources(std::initializer_list> list) { Patch& p = builder.patch; for(auto const& acc : list) @@ -84,30 +84,30 @@ namespace redGrapes REMOVE }; - std::list> diff; + std::list>> diff; void operator+=(Patch const& other) { this->diff.insert(std::end(this->diff), std::begin(other.diff), std::end(other.diff)); } - void operator+=(ResourceAccess const& ra) + void operator+=(ResourceAccess const& ra) { this->diff.push_back(std::make_pair(DiffType::ADD, ra)); } - void operator-=(ResourceAccess const& ra) + void operator-=(ResourceAccess const& ra) { this->diff.push_back(std::make_pair(DiffType::REMOVE, ra)); } }; - inline void operator+=(ResourceAccess const& ra) + inline void operator+=(ResourceAccess const& ra) { this->add_resource_access(ra); } - inline void operator-=(ResourceAccess const& ra) + inline void operator-=(ResourceAccess const& ra) { this->rm_resource_access(ra); } @@ -134,28 +134,10 @@ namespace redGrapes } }; - struct ResourcePrecedencePolicy - { - static bool is_serial(ResourceProperty const& a, ResourceProperty const& b) - { - return redGrapes::ResourceUser::is_serial(a, b); - } - - static void assert_superset(ResourceProperty const& super, ResourceProperty const& sub) - { - if(!redGrapes::ResourceUser::is_superset(super, sub)) - { - auto msg = fmt::format("Not allowed: {} is no superset of {}\n", super, sub); - spdlog::error(msg); - throw std::runtime_error(msg); - } - } - }; - } // namespace redGrapes -template<> -struct fmt::formatter +template +struct fmt::formatter> { constexpr auto parse(format_parse_context& ctx) { @@ -163,8 +145,8 @@ struct fmt::formatter } template - auto format(redGrapes::ResourceProperty const& label_prop, FormatContext& ctx) + auto format(redGrapes::ResourceProperty const& label_prop, FormatContext& ctx) { - return format_to(ctx.out(), "\"resources\" : {}", (redGrapes::ResourceUser const&) label_prop); + return fmt::format_to(ctx.out(), "\"resources\" : {}", (redGrapes::ResourceUser const&) label_prop); } }; diff --git a/redGrapes/task/property/trait.hpp b/redGrapes/task/property/trait.hpp index 292d7ffc..f29f47d6 100644 --- a/redGrapes/task/property/trait.hpp +++ b/redGrapes/task/property/trait.hpp @@ -1,4 +1,9 @@ - +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ #pragma once @@ -12,7 +17,7 @@ namespace redGrapes namespace trait { - template + template struct BuildProperties { template @@ -65,7 +70,7 @@ namespace redGrapes }; template<> - struct BuildProperties + struct BuildProperties { template static inline void build(Builder& builder, unsigned int const& t) diff --git a/redGrapes/task/queue.cpp b/redGrapes/task/queue.cpp deleted file mode 100644 index 7b148fa8..00000000 --- a/redGrapes/task/queue.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* Copyright 2022 Michael Sippel - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -#include - -namespace redGrapes -{ - namespace task - { - - Queue::Queue() - { - } - - /* - inline void Queue::push(Task* item) - { - item->next = nullptr; - Task * n = nullptr; - while(! tail->next.compare_exchange_weak(n, item)); - - if( tail ) - tail.load()->next = item; - tail = item; - - //std::lock_guard lock(m); - - item->next = nullptr; - - if(tail) - while(!__sync_bool_compare_and_swap(&(tail->next), nullptr, item)) - break; - - tail = item; - - __sync_bool_compare_and_swap(&head, 0, item); - - SPDLOG_TRACE("push: head = {}, tail = {}", (void*) head, (void*) tail); - } - - inline Task * Queue::pop() - { - std::lock_guard lock(m); - - while(Task * volatile t = head) - if(__sync_bool_compare_and_swap(&head, t, t->next)) - { - SPDLOG_TRACE("queue pop: item={}, new head = {}", (void*) t, (void*) t->next); - - if(t->next == nullptr) - tail = nullptr; - else - t->next = nullptr; - return t; - } - - SPDLOG_TRACE("pop: head = {}, tail = {}", (void*) head, (void*) tail); - , return nullptr; - } - */ - } // namespace task -} // namespace redGrapes diff --git a/redGrapes/task/queue.hpp b/redGrapes/task/queue.hpp index edb25e58..57b5192d 100644 --- a/redGrapes/task/queue.hpp +++ b/redGrapes/task/queue.hpp @@ -1,4 +1,4 @@ -/* Copyright 2020 Michael Sippel +/* Copyright 2020-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,19 +7,14 @@ #pragma once -#include -#include -#include +#include "redGrapes/memory/allocator.hpp" +#include "redGrapes/memory/block.hpp" +#include "redGrapes/util/trace.hpp" #include -#include - namespace redGrapes { - - struct Task; - namespace task { @@ -111,6 +106,7 @@ namespace redGrapes } }; + template struct Queue { /* @@ -119,7 +115,7 @@ namespace redGrapes std::mutex m; */ - moodycamel::ConcurrentQueue cq; + moodycamel::ConcurrentQueue cq; Queue(); @@ -127,16 +123,16 @@ namespace redGrapes { } - inline void push(Task* task) + inline void push(TTask* task) { TRACE_EVENT("Task", "TaskQueue::push()"); this->cq.enqueue(task); } - inline Task* pop() + inline TTask* pop() { TRACE_EVENT("Task", "TaskQueue::pop()"); - Task* t = nullptr; + TTask* t = nullptr; if(this->cq.try_dequeue(t)) return t; else diff --git a/redGrapes/task/task.hpp b/redGrapes/task/task.hpp index a6038877..135dd206 100644 --- a/redGrapes/task/task.hpp +++ b/redGrapes/task/task.hpp @@ -1,4 +1,4 @@ -/* Copyright 2022 Michael Sippel +/* Copyright 2022-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -6,45 +6,52 @@ */ #pragma once -#include -#include -#include -#include -#include -#include -#include +#include "redGrapes/task/property/graph.hpp" +#include "redGrapes/task/property/id.hpp" +#include "redGrapes/task/property/inherit.hpp" +#include "redGrapes/task/property/resource.hpp" +#include "redGrapes/task/task_base.hpp" #include -// defines REDGRAPES_TASK_PROPERTIES -#include - namespace redGrapes { - using TaskProperties = TaskProperties1< - IDProperty, - ResourceProperty, - QueueProperty, - GraphProperty -#ifdef REDGRAPES_TASK_PROPERTIES - , - REDGRAPES_TASK_PROPERTIES -#endif - >; + template + concept C_TaskProperty = requires(T taskProp, typename T::Patch patch) + { + { + taskProp.apply_patch(patch) + } -> std::same_as; + }; + template struct Task - : TaskBase - , TaskProperties + : TaskBase> + , TaskProperties1< + IDProperty, + ResourceProperty>, + GraphProperty>, + UserTaskProperties...> { + using TaskProperties = TaskProperties1< + IDProperty, + ResourceProperty>, + GraphProperty>, + UserTaskProperties...>; + virtual ~Task() { } - unsigned arena_id; + // worker id where task is first emplaced and task memory is located (may be stolen later) + unsigned worker_id; std::atomic_int removal_countdown; + scheduler::IScheduler>* scheduler_p; - Task() : removal_countdown(2) + Task(scheduler::IScheduler>& scheduler) + : removal_countdown(2) + , scheduler_p(&scheduler) { } @@ -57,11 +64,15 @@ namespace redGrapes // TODO: fuse ResultTask and FunTask into one template // ---> removes one layer of virtual function calls - template - struct ResultTask : Task + template + struct ResultTask : TTask { Result result_data; + ResultTask(scheduler::IScheduler& scheduler) : TTask(scheduler) + { + } + virtual ~ResultTask() { } @@ -76,13 +87,17 @@ namespace redGrapes void run() final { result_data = run_result(); - get_result_set_event().notify(); // result event now ready + this->get_result_set_event().notify(); // result event now ready } }; - template<> - struct ResultTask : Task + template + struct ResultTask : TTask { + ResultTask(scheduler::IScheduler& scheduler) : TTask(scheduler) + { + } + virtual ~ResultTask() { } @@ -94,13 +109,18 @@ namespace redGrapes void run() final { run_result(); - get_result_set_event().notify(); + this->get_result_set_event().notify(); } }; - template - struct FunTask : ResultTask::type> + template + struct FunTask : ResultTask::type, TTask> { + FunTask(scheduler::IScheduler& scheduler) + : ResultTask::type, TTask>(scheduler) + { + } + std::optional impl; virtual ~FunTask() diff --git a/redGrapes/task/task_base.hpp b/redGrapes/task/task_base.hpp index e9f40e5f..66d9149d 100644 --- a/redGrapes/task/task_base.hpp +++ b/redGrapes/task/task_base.hpp @@ -1,4 +1,4 @@ -/* Copyright 2019-2020 Michael Sippel +/* Copyright 2019-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,34 +7,32 @@ #pragma once -#include +#include "redGrapes/scheduler/event.hpp" #include -#include #include #include -#include namespace redGrapes { + template struct TaskBase { - bool finished; bool enable_stack_switching; virtual ~TaskBase() { } - TaskBase() : finished(false), enable_stack_switching(false) + TaskBase() : enable_stack_switching(false) { } virtual void run() = 0; - std::optional operator()() + std::optional> operator()() { if(enable_stack_switching) { @@ -70,7 +68,7 @@ namespace redGrapes return event; } - void yield(scheduler::EventPtr event) + void yield(scheduler::EventPtr event) { this->event = event; @@ -92,7 +90,7 @@ namespace redGrapes } } - std::optional event; + std::optional> event; private: std::mutex yield_cont_mutex; diff --git a/redGrapes/task/task_builder.hpp b/redGrapes/task/task_builder.hpp index 6f6af0c4..7781ab66 100644 --- a/redGrapes/task/task_builder.hpp +++ b/redGrapes/task/task_builder.hpp @@ -1,4 +1,4 @@ -/* Copyright 2023 Michael Sippel +/* Copyright 2023-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -6,12 +6,11 @@ */ #pragma once -#include -#include -#include -#include -#include -#include +#include "redGrapes/TaskCtx.hpp" +#include "redGrapes/task/future.hpp" +#include "redGrapes/task/task.hpp" +#include "redGrapes/task/task_space.hpp" +#include "redGrapes/util/bind_args.hpp" #include @@ -27,73 +26,48 @@ namespace redGrapes { } - template + template struct PropBuildHelper { - typename TaskProperties::Builder& builder; + typename TTask::TaskProperties::template Builder& builder; template inline int build(T const& x) { - trait::BuildProperties::build(builder, x); + trait::BuildProperties::build(builder, x); return 0; } - - void foo() - { - } }; /* TASK BUILDER */ - template - struct TaskBuilder : TaskProperties::Builder> + template + struct TaskBuilder : TTask::TaskProperties::template Builder> { - struct BindArgs - { - inline auto operator()(Callable&& f, Args&&... args) - { - return std::move([f = std::move(f), args...]() mutable { return f(std::forward(args)...); }); - } - }; - - using Impl = typename std::result_of::type; + using Impl = typename std::result_of(Callable, Args...)>::type; using Result = typename std::result_of::type; - std::shared_ptr space; - FunTask* task; + std::shared_ptr> space; + FunTask* task; - TaskBuilder(Callable&& f, Args&&... args) - : TaskProperties::Builder(*this) - , space(current_task_space()) + TaskBuilder(FunTask* task, Callable&& f, Args&&... args) + : TTask::TaskProperties::template Builder(*this) + , space(TaskCtx::current_task_space()) + , task{task} { - // allocate - redGrapes::memory::Allocator alloc; - memory::Block blk = alloc.allocate(sizeof(FunTask)); - task = (FunTask*) blk.ptr; - - if(!task) - throw std::runtime_error("out of memory"); - - // construct task in-place - new(task) FunTask(); - - task->arena_id = SingletonContext::get().current_arena; - // init properties from args - PropBuildHelper build_helper{*this}; + PropBuildHelper build_helper{*this}; pass(build_helper.template build(std::forward(args))...); - build_helper.foo(); // init id this->init_id(); // set impl - task->impl.emplace(BindArgs{}(std::move(f), std::forward(args)...)); + task->impl.emplace(BindArgs{}(std::move(f), std::forward(args)...)); } TaskBuilder(TaskBuilder& other) - : TaskProperties::Builder(*this) + : TTask::TaskProperties::template Builder(*this) , space(other.space) , task(other.task) { @@ -101,7 +75,7 @@ namespace redGrapes } TaskBuilder(TaskBuilder&& other) - : TaskProperties::Builder(*this) + : TTask::TaskProperties::template Builder(*this) , space(std::move(other.space)) , task(std::move(other.task)) { @@ -122,13 +96,14 @@ namespace redGrapes auto submit() { - Task* t = task; + TTask* t = task; task = nullptr; - SPDLOG_TRACE("submit task {}", (TaskProperties const&) *t); + SPDLOG_TRACE("submit task {}", (TTask::TaskProperties const&) *t); space->submit(t); + t->scheduler_p->emplace_task(*t); - return std::move(Future(*t)); + return std::move(Future(*t)); } auto get() diff --git a/redGrapes/task/task_space.cpp b/redGrapes/task/task_space.cpp deleted file mode 100644 index c889d539..00000000 --- a/redGrapes/task/task_space.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* Copyright 2021-2023 Michael Sippel - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace redGrapes -{ - TaskSpace::~TaskSpace() - { - } - - TaskSpace::TaskSpace() : depth(0), parent(nullptr) - { - task_count = 0; - } - - // sub space - TaskSpace::TaskSpace(Task* parent) : depth(parent->space->depth + 1), parent(parent) - { - task_count = 0; - } - - bool TaskSpace::is_serial(Task& a, Task& b) - { - return ResourceUser::is_serial(a, b); - } - - bool TaskSpace::is_superset(Task& a, Task& b) - { - return ResourceUser::is_superset(a, b); - } - - bool TaskSpace::empty() const - { - unsigned tc = task_count.load(); - return tc == 0; - } - - void TaskSpace::free_task(Task* task) - { - TRACE_EVENT("TaskSpace", "free_task()"); - unsigned count = task_count.fetch_sub(1) - 1; - - unsigned arena_id = task->arena_id; - task->~Task(); - - // FIXME: len of the Block is not correct since FunTask object is bigger than sizeof(Task) - SingletonContext::get().worker_pool->get_worker(arena_id).alloc.deallocate( - memory::Block{(uintptr_t) task, sizeof(Task)}); - - // TODO: implement this using post-event of root-task? - // - event already has in_edge count - // -> never have current_task = nullptr - // spdlog::info("kill task... {} remaining", count); - if(count == 0) - SingletonContext::get().scheduler->wake_all(); - } - - void TaskSpace::submit(Task* task) - { - TRACE_EVENT("TaskSpace", "submit()"); - task->space = shared_from_this(); - task->task = task; - - ++task_count; - - if(parent) - assert(this->is_superset(*parent, *task)); - - for(auto r = task->unique_resources.rbegin(); r != task->unique_resources.rend(); ++r) - { - r->task_entry = r->resource->users.push(task); - } - - SingletonContext::get().scheduler->emplace_task(*task); - } - -} // namespace redGrapes diff --git a/redGrapes/task/task_space.hpp b/redGrapes/task/task_space.hpp index 71e7c987..f83f347d 100644 --- a/redGrapes/task/task_space.hpp +++ b/redGrapes/task/task_space.hpp @@ -1,4 +1,4 @@ -/* Copyright 2021-2022 Michael Sippel +/* Copyright 2021-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,48 +7,85 @@ #pragma once -#include -#include -#include -#include +#include "redGrapes/TaskFreeCtx.hpp" +#include "redGrapes/memory/block.hpp" +#include "redGrapes/util/trace.hpp" #include -#include -#include +#include namespace redGrapes { /*! TaskSpace handles sub-taskspaces of child tasks */ - struct TaskSpace : std::enable_shared_from_this + template + struct TaskSpace : std::enable_shared_from_this> { std::atomic task_count; unsigned depth; - Task* parent; - - std::shared_mutex active_child_spaces_mutex; - std::vector> active_child_spaces; - - virtual ~TaskSpace(); + TTask* parent; // top space - TaskSpace(); + TaskSpace() : depth(0), parent(nullptr) + { + task_count = 0; + } // sub space - TaskSpace(Task* parent); - virtual bool is_serial(Task& a, Task& b); - virtual bool is_superset(Task& a, Task& b); + TaskSpace(TTask* parent) : depth(parent->space->depth + 1), parent(parent) + { + task_count = 0; + } // add a new task to the task-space - void submit(Task* task); + void submit(TTask* task) + { + TRACE_EVENT("TaskSpace", "submit()"); + task->space = this->shared_from_this(); + task->task = task; + + ++task_count; + + if(parent) + assert(parent->is_superset_of(*task)); + + for(auto r = task->unique_resources.rbegin(); r != task->unique_resources.rend(); ++r) + { + r->task_entry = r->resource->users.push(task); + } + } // remove task from task-space - void free_task(Task* task); + void free_task(TTask* task) + { + TRACE_EVENT("TaskSpace", "free_task()"); + unsigned count = task_count.fetch_sub(1) - 1; + + unsigned worker_id = task->worker_id; + auto task_scheduler_p = task->scheduler_p; + task->~TTask(); // TODO check if this is really required + + // FIXME: len of the Block is not correct since FunTask object is bigger than sizeof(Task) + // TODO check if arenaID is correct for the global alloc pool + TaskFreeCtx::worker_alloc_pool->get_alloc(worker_id).deallocate( + memory::Block{(uintptr_t) task, sizeof(TTask)}); + + // TODO: implement this using post-event of root-task? + // - event already has in_edge count + // -> never have current_task = nullptr + // spdlog::info("kill task... {} remaining", count); + if(count == 0) + task_scheduler_p->wake_all(); // TODO think if this should call wake_all on all schedulers + } - bool empty() const; + bool empty() const + { + unsigned tc = task_count.load(); + return tc == 0; + } }; } // namespace redGrapes diff --git a/redGrapes/util/atomic_list.hpp b/redGrapes/util/atomic_list.hpp index f67cc008..92f22687 100644 --- a/redGrapes/util/atomic_list.hpp +++ b/redGrapes/util/atomic_list.hpp @@ -1,4 +1,4 @@ -/* Copyright 2023 Michael Sippel +/* Copyright 2023-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,8 +7,8 @@ #pragma once -#include -#include +#include "redGrapes/memory/block.hpp" +#include "redGrapes/util/trace.hpp" #include #include @@ -18,7 +18,6 @@ #include #include #include -#include namespace redGrapes { @@ -298,8 +297,7 @@ namespace redGrapes { old_head = std::atomic_load(&head); std::atomic_store(&new_head->prev, old_head); - append_successful - = std::atomic_compare_exchange_strong(&head, &old_head, new_head); + append_successful = std::atomic_compare_exchange_strong(&head, &old_head, new_head); } return MutBackwardIterator{old_head}; @@ -312,7 +310,7 @@ namespace redGrapes std::shared_ptr expected(nullptr); std::shared_ptr const& desired = new_head; - return std::atomic_compare_exchange_strong(&head, &expected, desired); + return std::atomic_compare_exchange_strong(&head, &expected, desired); } }; diff --git a/redGrapes/util/bind_args.hpp b/redGrapes/util/bind_args.hpp new file mode 100644 index 00000000..0e146e57 --- /dev/null +++ b/redGrapes/util/bind_args.hpp @@ -0,0 +1,18 @@ +/* Copyright 2024 Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once +#include + +template +struct BindArgs +{ + inline auto operator()(Callable&& f, Args&&... args) + { + return std::move([f = std::move(f), args...]() mutable { return f(std::forward(args)...); }); + } +}; diff --git a/redGrapes/util/chunked_list.hpp b/redGrapes/util/chunked_list.hpp index 84629df5..e73687bc 100644 --- a/redGrapes/util/chunked_list.hpp +++ b/redGrapes/util/chunked_list.hpp @@ -1,4 +1,4 @@ -/* Copyright 2022-2023 Michael Sippel +/* Copyright 2022-2024 Michael Sippel, Tapish Narwal * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -11,20 +11,19 @@ #pragma once -#include -#include -#include +#include "redGrapes/memory/allocator.hpp" +#include "redGrapes/util/atomic_list.hpp" +#include "redGrapes/util/trace.hpp" #include -#include #include #include #include #include -#include -#include -#include +#include +#include +#include namespace redGrapes { @@ -282,7 +281,7 @@ namespace redGrapes struct ItemAccess { private: - friend class ChunkedList; + friend struct ChunkedList; typename memory::AtomicList::MutBackwardIterator chunk; /* this pointer packs the address of the current element diff --git a/redGrapes/util/trace.hpp b/redGrapes/util/trace.hpp index 0d6e6424..b78133bd 100644 --- a/redGrapes/util/trace.hpp +++ b/redGrapes/util/trace.hpp @@ -1,8 +1,12 @@ +/* Copyright 2023-2024 Michael Sippel, Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ #pragma once -// #include - #ifndef REDGRAPES_ENABLE_TRACE # define REDGRAPES_ENABLE_TRACE 0 #endif @@ -40,3 +44,5 @@ void StopTracing(std::shared_ptr tracing_session); # define TRACE_EVENT_END #endif + +#include diff --git a/redGrapes/util/trace.cpp b/redGrapes/util/trace.tpp similarity index 84% rename from redGrapes/util/trace.cpp rename to redGrapes/util/trace.tpp index 2586e75c..a0a64c0a 100644 --- a/redGrapes/util/trace.cpp +++ b/redGrapes/util/trace.tpp @@ -1,3 +1,12 @@ +/* Copyright 2023-2024 Michael Sippel, Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + #include #if REDGRAPES_ENABLE_TRACE diff --git a/redGrapes/util/tuple_map.hpp b/redGrapes/util/tuple_map.hpp new file mode 100644 index 00000000..a1d1129d --- /dev/null +++ b/redGrapes/util/tuple_map.hpp @@ -0,0 +1,99 @@ +/* Copyright 2024 Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include +#include + +#include + +namespace redGrapes +{ + namespace detail + { + template typename T_Accessor = boost::mp11::mp_identity_t> + struct InheritLinearly; + + template typename T_Accessor> + struct InheritLinearly, T_Accessor> : T_Accessor... + { + }; + + /** wrap a datum + * + * @tparam T_Pair mp_list + */ + + template + struct TaggedValue + { + using Key = boost::mp11::mp_first; + using ValueType = boost::mp11::mp_second; + + ValueType value; + }; + } // namespace detail + + /** wrap a datum + * + * @tparam T_Map mp_list, where pair is mp_list< key, type of the value > + */ + + template + struct MapTuple : protected detail::InheritLinearly + { + template + using TaggedValueFor = detail::TaggedValue>; + + /** access a value with a key + * + * @tparam T_Key key type + * + */ + template + auto& operator[](T_Key const& key) + { + return static_cast&>(*this).value; + } + + template + auto const& operator[](T_Key const& key) const + { + return static_cast&>(*this).value; + } + + template + void apply_to_all(Func func) + { + // Iterate over each element of the map + mp_for_each( + [&func](auto taggedValue) + { + // Apply the function to the value of each element + func(taggedValue.value); + }); + } + + template + void call_member_func_for_all(MemberFunc memFunc, Args&&... args) + { + // Iterate over each element of the map + mp_for_each( + [&memFunc, &args...](auto taggedValue) + { + // Call the member function for each element + (taggedValue.value.*memFunc)(std::forward(args)...); + }); + } + }; + + template + using MakeKeyValList = boost::mp11::mp_list>; + + +} // namespace redGrapes diff --git a/redGrapes/version.hpp b/redGrapes/version.hpp index bfc4a3a0..0e72c2c5 100644 --- a/redGrapes/version.hpp +++ b/redGrapes/version.hpp @@ -8,6 +8,6 @@ #pragma once #define REDGRAPES_VERSION_MAJOR 0 -#define REDGRAPES_VERSION_MINOR 1 +#define REDGRAPES_VERSION_MINOR 2 #define REDGRAPES_VERSION_PATCH 0 #define REDGRAPES_VERSION_LABEL "" diff --git a/redGrapesConfig.cmake b/redGrapesConfig.cmake index 2936ea0a..56eed262 100644 --- a/redGrapesConfig.cmake +++ b/redGrapesConfig.cmake @@ -38,50 +38,37 @@ else() message(STATUS "Found hwloc") endif() -set(redGrapes_CXX_STANDARD_DEFAULT "17") +set(redGrapes_CXX_STANDARD_DEFAULT "20") # Check whether redGrapes_CXX_STANDARD has already been defined as a non-cached variable. if(DEFINED redGrapes) set(redGrapes_CXX_STANDARD_DEFAULT ${redGrapes_CXX_STANDARD}) endif() set(redGrapes_CXX_STANDARD ${redGrapes_CXX_STANDARD_DEFAULT} CACHE STRING "C++ standard version") -set_property(CACHE redGrapes_CXX_STANDARD PROPERTY STRINGS "17;20") +set_property(CACHE redGrapes_CXX_STANDARD PROPERTY STRINGS "20;23") if( NOT TARGET redGrapes ) - add_library(redGrapes - ${CMAKE_CURRENT_LIST_DIR}/redGrapes/resource/resource.cpp - ${CMAKE_CURRENT_LIST_DIR}/redGrapes/resource/resource_user.cpp - ${CMAKE_CURRENT_LIST_DIR}/redGrapes/dispatch/thread/execute.cpp - ${CMAKE_CURRENT_LIST_DIR}/redGrapes/dispatch/thread/cpuset.cpp - ${CMAKE_CURRENT_LIST_DIR}/redGrapes/dispatch/thread/worker.cpp - ${CMAKE_CURRENT_LIST_DIR}/redGrapes/dispatch/thread/worker_pool.cpp - ${CMAKE_CURRENT_LIST_DIR}/redGrapes/scheduler/event.cpp - ${CMAKE_CURRENT_LIST_DIR}/redGrapes/scheduler/event_ptr.cpp - ${CMAKE_CURRENT_LIST_DIR}/redGrapes/scheduler/default_scheduler.cpp - ${CMAKE_CURRENT_LIST_DIR}/redGrapes/task/property/graph.cpp - ${CMAKE_CURRENT_LIST_DIR}/redGrapes/task/task_space.cpp - ${CMAKE_CURRENT_LIST_DIR}/redGrapes/task/queue.cpp - ${CMAKE_CURRENT_LIST_DIR}/redGrapes/memory/allocator.cpp - ${CMAKE_CURRENT_LIST_DIR}/redGrapes/memory/bump_allocator.cpp - ${CMAKE_CURRENT_LIST_DIR}/redGrapes/sync/cv.cpp - ${CMAKE_CURRENT_LIST_DIR}/redGrapes/util/trace.cpp - ${CMAKE_CURRENT_LIST_DIR}/redGrapes/redGrapes.cpp - ) - target_compile_features(redGrapes PUBLIC cxx_std_${redGrapes_CXX_STANDARD}) + add_library(redGrapes INTERFACE) + target_compile_features(redGrapes INTERFACE cxx_std_${redGrapes_CXX_STANDARD}) + if(MSVC) + target_compile_options(redGrapes INTERFACE /W4 /WX) + else() + target_compile_options(redGrapes INTERFACE -Wall -Wextra) endif() -target_include_directories(redGrapes PUBLIC +endif() + +target_include_directories(redGrapes INTERFACE $ $ ) -target_link_libraries(redGrapes PUBLIC ${CMAKE_THREAD_LIBS_INIT}) -target_link_libraries(redGrapes PUBLIC ${Boost_LIBRARIES}) -target_link_libraries(redGrapes PUBLIC fmt::fmt) -target_link_libraries(redGrapes PUBLIC spdlog::spdlog) -target_link_libraries(redGrapes PUBLIC ${HWLOC}) - -set(redGrapes_INCLUDE_DIRS ${redGrapes_CONFIG_INCLUDE_DIR} ${CMAKE_CURRENT_LIST_DIR}) +target_link_libraries(redGrapes INTERFACE ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(redGrapes INTERFACE ${Boost_LIBRARIES}) +target_link_libraries(redGrapes INTERFACE fmt::fmt) +target_link_libraries(redGrapes INTERFACE spdlog::spdlog) +target_link_libraries(redGrapes INTERFACE ${HWLOC}) +set(redGrapes_INCLUDE_DIRS ${CMAKE_CURRENT_LIST_DIR}) set(redGrapes_INCLUDE_DIRS ${redGrapes_INCLUDE_DIRS} "${CMAKE_CURRENT_LIST_DIR}/share/thirdParty/cameron314/concurrentqueue/include") set(redGrapes_INCLUDE_DIRS ${redGrapes_INCLUDE_DIRS} ${HWLOC_INCLUDE_DIR}) @@ -95,7 +82,7 @@ if(redGrapes_ENABLE_BACKWARDCPP) find_package(Backward) add_compile_definitions(REDGRAPES_ENABLE_BACKWARDCPP=1) - target_link_libraries(redGrapes PUBLIC Backward::Backward) + target_link_libraries(redGrapes INTERFACE Backward::Backward) endif() if(redGrapes_ENABLE_PERFETTO) @@ -111,5 +98,5 @@ if(redGrapes_ENABLE_PERFETTO) set(redGrapes_LIBRARIES ${Boost_LIBRARIES} fmt::fmt spdlog::spdlog perfetto ${CMAKE_THREAD_LIBS_INIT}) endif() -target_include_directories(redGrapes PUBLIC ${redGrapes_INCLUDE_DIRS}) +target_include_directories(redGrapes INTERFACE ${redGrapes_INCLUDE_DIRS}) diff --git a/redGrapes_config.hpp b/redGrapes_config.hpp deleted file mode 100644 index 6f70f09b..00000000 --- a/redGrapes_config.hpp +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/test/cv.cpp b/test/cv.cpp index da05bfd1..5b7ff736 100644 --- a/test/cv.cpp +++ b/test/cv.cpp @@ -11,7 +11,6 @@ TEST_CASE("CV") for(int i = 0; i < 5000; ++i) { std::atomic finished = {false}; - bool volatile start = false; redGrapes::CondVar cv; diff --git a/test/random_graph.cpp b/test/random_graph.cpp index 3a06da77..d4fa6b10 100644 --- a/test/random_graph.cpp +++ b/test/random_graph.cpp @@ -1,5 +1,13 @@ +/* Copyright 2023-2024 Michael Sippel, Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + #include "sha256.c" +#include #include #include @@ -9,14 +17,12 @@ #include #include -#include #include #include #include #include #include -namespace rg = redGrapes; using namespace std::chrono; void sleep(std::chrono::microseconds d) @@ -54,10 +60,10 @@ void generate_access_pattern() expected_hash = std::vector>(n_resources); std::vector path_length(n_resources); - for(int i = 0; i < n_tasks; ++i) + for(unsigned i = 0; i < n_tasks; ++i) { unsigned n_dependencies = distrib_n_deps(gen); - for(int j = 0; j < n_dependencies; ++j) + for(unsigned j = 0; j < n_dependencies; ++j) { unsigned max_path_length = 0; @@ -96,20 +102,20 @@ TEST_CASE("RandomGraph") generate_access_pattern(); - rg::init(n_threads); - + using TTask = redGrapes::Task<>; + auto rg = redGrapes::init(n_threads); { - std::vector>> resources(n_resources); + std::vector, TTask>> resources(n_resources); - for(int i = 0; i < n_tasks; ++i) + for(unsigned i = 0; i < n_tasks; ++i) switch(access_pattern[i].size()) { case 0: - rg::emplace_task([i]() { sleep(task_duration); }); + rg.emplace_task([]() { sleep(task_duration); }); break; case 1: - rg::emplace_task( + rg.emplace_task( [i](auto ra1) { sleep(task_duration); @@ -119,7 +125,7 @@ TEST_CASE("RandomGraph") break; case 2: - rg::emplace_task( + rg.emplace_task( [i](auto ra1, auto ra2) { sleep(task_duration); @@ -131,7 +137,7 @@ TEST_CASE("RandomGraph") break; case 3: - rg::emplace_task( + rg.emplace_task( [i](auto ra1, auto ra2, auto ra3) { sleep(task_duration); @@ -145,7 +151,7 @@ TEST_CASE("RandomGraph") break; case 4: - rg::emplace_task( + rg.emplace_task( [i](auto ra1, auto ra2, auto ra3, auto ra4) { sleep(task_duration); @@ -161,7 +167,7 @@ TEST_CASE("RandomGraph") break; case 5: - rg::emplace_task( + rg.emplace_task( [i](auto ra1, auto ra2, auto ra3, auto ra4, auto ra5) { sleep(task_duration); @@ -179,10 +185,8 @@ TEST_CASE("RandomGraph") break; } - rg::barrier(); - for(int i = 0; i < n_resources; ++i) + rg.barrier(); + for(unsigned i = 0; i < n_resources; ++i) REQUIRE(*resources[i] == expected_hash[i]); } - - rg::finalize(); } diff --git a/test/resource.cpp b/test/resource.cpp index cfe1d494..1d933d46 100644 --- a/test/resource.cpp +++ b/test/resource.cpp @@ -6,7 +6,7 @@ struct Access { - static bool is_serial(Access a, Access b) + static bool is_serial([[maybe_unused]] Access a, [[maybe_unused]] Access b) { return true; } @@ -16,12 +16,12 @@ struct Access return true; } - bool is_superset_of(Access a) const + bool is_superset_of([[maybe_unused]] Access a) const { return true; } - bool operator==(Access const& other) const + bool operator==([[maybe_unused]] Access const& other) const { return false; } @@ -36,7 +36,7 @@ struct fmt::formatter } template - auto format(Access const& acc, FormatContext& ctx) + auto format([[maybe_unused]] Access const& acc, FormatContext& ctx) { return fmt::format_to(ctx.out(), "Access"); } @@ -44,47 +44,47 @@ struct fmt::formatter TEST_CASE("Resource ID") { - redGrapes::init(1); - redGrapes::Resource a, b; + auto rg = redGrapes::init(1); + using RGTask = decltype(rg)::RGTask; + redGrapes::Resource a, b; // same resource - REQUIRE(redGrapes::ResourceAccess::is_serial(a.make_access(Access{}), a.make_access(Access{})) == true); - REQUIRE(redGrapes::ResourceAccess::is_serial(b.make_access(Access{}), b.make_access(Access{})) == true); + REQUIRE(redGrapes::ResourceAccess::is_serial(a.make_access(Access{}), a.make_access(Access{})) == true); + REQUIRE(redGrapes::ResourceAccess::is_serial(b.make_access(Access{}), b.make_access(Access{})) == true); // same resource, but copied - redGrapes::Resource a2(a); - REQUIRE(redGrapes::ResourceAccess::is_serial(a.make_access(Access{}), a2.make_access(Access{})) == true); + redGrapes::Resource a2(a); + REQUIRE(redGrapes::ResourceAccess::is_serial(a.make_access(Access{}), a2.make_access(Access{})) == true); // different resource - REQUIRE(redGrapes::ResourceAccess::is_serial(a.make_access(Access{}), b.make_access(Access{})) == false); - REQUIRE(redGrapes::ResourceAccess::is_serial(b.make_access(Access{}), a.make_access(Access{})) == false); - redGrapes::finalize(); + REQUIRE(redGrapes::ResourceAccess::is_serial(a.make_access(Access{}), b.make_access(Access{})) == false); + REQUIRE(redGrapes::ResourceAccess::is_serial(b.make_access(Access{}), a.make_access(Access{})) == false); } TEST_CASE("IOResource") { - redGrapes::init(1); - redGrapes::IOResource a, b; - - REQUIRE(redGrapes::ResourceAccess::is_serial(a.read(), a.read()) == false); - REQUIRE(redGrapes::ResourceAccess::is_serial(a.read(), a.write()) == true); - REQUIRE(redGrapes::ResourceAccess::is_serial(a.write(), a.read()) == true); - REQUIRE(redGrapes::ResourceAccess::is_serial(a.write(), a.write()) == true); - - REQUIRE(redGrapes::ResourceAccess::is_serial(b.read(), b.read()) == false); - REQUIRE(redGrapes::ResourceAccess::is_serial(b.read(), b.write()) == true); - REQUIRE(redGrapes::ResourceAccess::is_serial(b.write(), b.read()) == true); - REQUIRE(redGrapes::ResourceAccess::is_serial(b.write(), b.write()) == true); - - REQUIRE(redGrapes::ResourceAccess::is_serial(a.read(), b.read()) == false); - REQUIRE(redGrapes::ResourceAccess::is_serial(a.read(), b.write()) == false); - REQUIRE(redGrapes::ResourceAccess::is_serial(a.write(), b.read()) == false); - REQUIRE(redGrapes::ResourceAccess::is_serial(a.write(), b.write()) == false); - - REQUIRE(redGrapes::ResourceAccess::is_serial(b.read(), a.read()) == false); - REQUIRE(redGrapes::ResourceAccess::is_serial(b.read(), a.write()) == false); - REQUIRE(redGrapes::ResourceAccess::is_serial(b.write(), a.read()) == false); - REQUIRE(redGrapes::ResourceAccess::is_serial(b.write(), a.write()) == false); - - redGrapes::finalize(); + auto rg = redGrapes::init(1); + using RGTask = decltype(rg)::RGTask; + + redGrapes::IOResource a, b; + + REQUIRE(redGrapes::ResourceAccess::is_serial(a.read(), a.read()) == false); + REQUIRE(redGrapes::ResourceAccess::is_serial(a.read(), a.write()) == true); + REQUIRE(redGrapes::ResourceAccess::is_serial(a.write(), a.read()) == true); + REQUIRE(redGrapes::ResourceAccess::is_serial(a.write(), a.write()) == true); + + REQUIRE(redGrapes::ResourceAccess::is_serial(b.read(), b.read()) == false); + REQUIRE(redGrapes::ResourceAccess::is_serial(b.read(), b.write()) == true); + REQUIRE(redGrapes::ResourceAccess::is_serial(b.write(), b.read()) == true); + REQUIRE(redGrapes::ResourceAccess::is_serial(b.write(), b.write()) == true); + + REQUIRE(redGrapes::ResourceAccess::is_serial(a.read(), b.read()) == false); + REQUIRE(redGrapes::ResourceAccess::is_serial(a.read(), b.write()) == false); + REQUIRE(redGrapes::ResourceAccess::is_serial(a.write(), b.read()) == false); + REQUIRE(redGrapes::ResourceAccess::is_serial(a.write(), b.write()) == false); + + REQUIRE(redGrapes::ResourceAccess::is_serial(b.read(), a.read()) == false); + REQUIRE(redGrapes::ResourceAccess::is_serial(b.read(), a.write()) == false); + REQUIRE(redGrapes::ResourceAccess::is_serial(b.write(), a.read()) == false); + REQUIRE(redGrapes::ResourceAccess::is_serial(b.write(), a.write()) == false); } diff --git a/test/resource_user.cpp b/test/resource_user.cpp index 94a4b993..4546b907 100644 --- a/test/resource_user.cpp +++ b/test/resource_user.cpp @@ -7,45 +7,46 @@ TEST_CASE("Resource User") { - redGrapes::init(); - - redGrapes::IOResource a, b; - - redGrapes::ResourceUser f1({a.read()}); - redGrapes::ResourceUser f2({a.read(), a.write()}); - redGrapes::ResourceUser f3({b.read()}); - redGrapes::ResourceUser f4({b.read(), b.write()}); - redGrapes::ResourceUser f5({a.read(), a.write(), b.read(), b.write()}); - - REQUIRE(redGrapes::ResourceUser::is_serial(f1, f1) == false); - REQUIRE(redGrapes::ResourceUser::is_serial(f1, f2) == true); - REQUIRE(redGrapes::ResourceUser::is_serial(f1, f3) == false); - REQUIRE(redGrapes::ResourceUser::is_serial(f1, f4) == false); - REQUIRE(redGrapes::ResourceUser::is_serial(f1, f5) == true); - - REQUIRE(redGrapes::ResourceUser::is_serial(f2, f1) == true); - REQUIRE(redGrapes::ResourceUser::is_serial(f2, f2) == true); - REQUIRE(redGrapes::ResourceUser::is_serial(f2, f3) == false); - REQUIRE(redGrapes::ResourceUser::is_serial(f2, f4) == false); - REQUIRE(redGrapes::ResourceUser::is_serial(f2, f5) == true); - - REQUIRE(redGrapes::ResourceUser::is_serial(f3, f1) == false); - REQUIRE(redGrapes::ResourceUser::is_serial(f3, f2) == false); - REQUIRE(redGrapes::ResourceUser::is_serial(f3, f3) == false); - REQUIRE(redGrapes::ResourceUser::is_serial(f3, f4) == true); - REQUIRE(redGrapes::ResourceUser::is_serial(f3, f5) == true); - - REQUIRE(redGrapes::ResourceUser::is_serial(f4, f1) == false); - REQUIRE(redGrapes::ResourceUser::is_serial(f4, f2) == false); - REQUIRE(redGrapes::ResourceUser::is_serial(f4, f3) == true); - REQUIRE(redGrapes::ResourceUser::is_serial(f4, f4) == true); - REQUIRE(redGrapes::ResourceUser::is_serial(f4, f5) == true); - - REQUIRE(redGrapes::ResourceUser::is_serial(f5, f1) == true); - REQUIRE(redGrapes::ResourceUser::is_serial(f5, f2) == true); - REQUIRE(redGrapes::ResourceUser::is_serial(f5, f3) == true); - REQUIRE(redGrapes::ResourceUser::is_serial(f5, f4) == true); - REQUIRE(redGrapes::ResourceUser::is_serial(f5, f5) == true); + auto rg = redGrapes::init(); + using RGTask = decltype(rg)::RGTask; + + redGrapes::IOResource a, b; + + redGrapes::ResourceUser f1({a.read()}); + redGrapes::ResourceUser f2({a.read(), a.write()}); + redGrapes::ResourceUser f3({b.read()}); + redGrapes::ResourceUser f4({b.read(), b.write()}); + redGrapes::ResourceUser f5({a.read(), a.write(), b.read(), b.write()}); + + REQUIRE(is_serial(f1, f1) == false); + REQUIRE(is_serial(f1, f2) == true); + REQUIRE(is_serial(f1, f3) == false); + REQUIRE(is_serial(f1, f4) == false); + REQUIRE(is_serial(f1, f5) == true); + + REQUIRE(is_serial(f2, f1) == true); + REQUIRE(is_serial(f2, f2) == true); + REQUIRE(is_serial(f2, f3) == false); + REQUIRE(is_serial(f2, f4) == false); + REQUIRE(is_serial(f2, f5) == true); + + REQUIRE(is_serial(f3, f1) == false); + REQUIRE(is_serial(f3, f2) == false); + REQUIRE(is_serial(f3, f3) == false); + REQUIRE(is_serial(f3, f4) == true); + REQUIRE(is_serial(f3, f5) == true); + + REQUIRE(is_serial(f4, f1) == false); + REQUIRE(is_serial(f4, f2) == false); + REQUIRE(is_serial(f4, f3) == true); + REQUIRE(is_serial(f4, f4) == true); + REQUIRE(is_serial(f4, f5) == true); + + REQUIRE(is_serial(f5, f1) == true); + REQUIRE(is_serial(f5, f2) == true); + REQUIRE(is_serial(f5, f3) == true); + REQUIRE(is_serial(f5, f4) == true); + REQUIRE(is_serial(f5, f5) == true); REQUIRE(f1.is_superset_of(f1) == true); @@ -59,6 +60,4 @@ TEST_CASE("Resource User") REQUIRE(f2.is_superset_of(f3) == false); REQUIRE(f2.is_superset_of(f4) == false); REQUIRE(f2.is_superset_of(f5) == false); - - redGrapes::finalize(); } diff --git a/test/scheduler.cpp b/test/scheduler.cpp index 09413586..907bb805 100644 --- a/test/scheduler.cpp +++ b/test/scheduler.cpp @@ -1,31 +1,31 @@ +/* Copyright 2023-2024 Michael Sippel, Tapish Narwal + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + #include #include #include #include -#include -#include -#include #include -#include -#include -#include -#include -namespace rg = redGrapes; + using namespace std::chrono; void test_worker_utilization(unsigned n_workers) { - rg::init(n_workers); + auto rg = redGrapes::init(n_workers); spdlog::set_pattern("[thread %t] %^[%l]%$ %v"); std::atomic count(0); for(unsigned i = 0; i < n_workers; ++i) { - rg::emplace_task( + rg.emplace_task( [&count] { count++; @@ -39,8 +39,6 @@ void test_worker_utilization(unsigned n_workers) break; REQUIRE(count == n_workers); - - rg::finalize(); } /* @@ -49,7 +47,7 @@ void test_worker_utilization(unsigned n_workers) */ TEST_CASE("WorkerUtilization") { - for(int i = 1; i < std::thread::hardware_concurrency(); i += 5) + for(unsigned i = 1; i < std::thread::hardware_concurrency(); i += 5) test_worker_utilization(i); test_worker_utilization(std::thread::hardware_concurrency());