| 908 | } |
| 909 | |
| 910 | void host_function::execute_host(const host_function_wrapper& func, |
| 911 | const uint32_t& cpu_count, |
| 912 | const uint3& group_dim, |
| 913 | const uint3& group_size, |
| 914 | const uint3& global_dim, |
| 915 | const uint3& local_dim, |
| 916 | const uint32_t& work_dim) const { |
| 917 | // #work-groups |
| 918 | const auto group_count = group_dim.x * group_dim.y * group_dim.z; |
| 919 | // #work-items per group |
| 920 | const uint32_t local_size = local_dim.x * local_dim.y * local_dim.z; |
| 921 | // group ticketing system, each worker thread will grab a new group id, once it's done with one group |
| 922 | std::atomic<uint32_t> group_idx { 0 }; |
| 923 | |
| 924 | // start worker threads |
| 925 | #if defined(FLOOR_HOST_FUNCTION_ENABLE_TIMING) |
| 926 | const auto time_start = floor_timer::start(); |
| 927 | #endif |
| 928 | std::vector<std::unique_ptr<std::thread>> worker_threads(cpu_count); |
| 929 | for (uint32_t cpu_idx = 0; cpu_idx < cpu_count; ++cpu_idx) { |
| 930 | worker_threads[cpu_idx] = std::make_unique<std::thread>([this, &func, cpu_idx, |
| 931 | &group_idx, group_count, group_dim, group_size, |
| 932 | global_dim, local_size, local_dim, work_dim] { |
| 933 | // set CPU affinity for this thread to a particular CPU to prevent this thread from being constantly moved/scheduled |
| 934 | // on different CPUs (starting at index 1, with 0 representing no affinity) |
| 935 | set_thread_affinity(cpu_idx + 1); |
| 936 | |
| 937 | // get and init host execution context |
| 938 | auto& exec_ctx = host_exec_context; |
| 939 | exec_ctx.ids = { |
| 940 | .instance_global_idx = { 0, 0, 0 }, |
| 941 | .instance_global_work_size = global_dim, |
| 942 | .instance_local_idx = { 0, 0, 0 }, |
| 943 | .instance_local_work_size = local_dim, |
| 944 | .instance_group_idx = { 0, 0, 0 }, |
| 945 | .instance_group_size = group_size, |
| 946 | .instance_work_dim = work_dim, |
| 947 | .instance_local_linear_idx = 0u, |
| 948 | }; |
| 949 | exec_ctx.linear_local_work_size = local_size; |
| 950 | exec_ctx.func = &func; |
| 951 | exec_ctx.thread_local_memory_offset = cpu_idx * floor_local_memory_max_size; |
| 952 | |
| 953 | // init contexts (aka fibers) |
| 954 | floor_fiber_context main_ctx; |
| 955 | main_ctx.init(nullptr, 0, nullptr, ~0u, nullptr, nullptr); |
| 956 | auto items = std::make_unique<floor_fiber_context[]>(local_size); |
| 957 | exec_ctx.item_contexts = items.get(); |
| 958 | |
| 959 | // init fibers |
| 960 | for (uint32_t i = 0; i < local_size; ++i) { |
| 961 | items[i].init(&floor_stack_memory_data.get()[(i + local_size * cpu_idx) * floor_fiber_context::min_stack_size], |
| 962 | floor_fiber_context::min_stack_size, |
| 963 | run_host_group_item, i, |
| 964 | // continue with next on return, or return to main ctx when the last item returns |
| 965 | // TODO: add option to use randomized order? |
| 966 | (i + 1 < local_size ? &items[i + 1] : &main_ctx), |
| 967 | &main_ctx); |
nothing calls this directly
no test coverage detected