Shilei Tian 9d64275ae0 [OpenMP] Added the support for hidden helper task in RTL
The basic design is to create an outer-most parallel team. It is not a regular team because it is only created when the first hidden helper task is encountered, and is only responsible for the execution of hidden helper tasks.  We first use `pthread_create` to create a new thread, let's call it the initial and also the main thread of the hidden helper team. This initial thread then initializes a new root, just like what RTL does in initialization. After that, it directly calls `__kmpc_fork_call`. It is like the initial thread encounters a parallel region. The wrapped function for this team is, for main thread, which is the initial thread that we create via `pthread_create` on Linux, waits on a condition variable. The condition variable can only be signaled when RTL is being destroyed. For other work threads, they just do nothing. The reason that main thread needs to wait there is, in current implementation, once the main thread finishes the wrapped function of this team, it starts to free the team which is not what we want.

Two environment variables, `LIBOMP_NUM_HIDDEN_HELPER_THREADS` and `LIBOMP_USE_HIDDEN_HELPER_TASK`, are also set to configure the number of threads and enable/disable this feature. By default, the number of hidden helper threads is 8.

Here are some open issues to be discussed:
1. The main thread goes to sleeping when the initialization is finished. As Andrey mentioned, we might need it to be awaken from time to time to do some stuffs. What kind of update/check should be put here?

Reviewed By: jdoerfert

Differential Revision: https://reviews.llvm.org/D77609
2021-01-25 22:16:17 -05:00

133 lines
3.8 KiB
C++

// RUN: %libomp-cxx-compile-and-run
/*
* This test aims to check whether hidden helper thread has right gtid. We also
* test if there is mixed dependences between regular tasks and hidden helper
* tasks, the tasks are executed by right set of threads. It is equivalent to
* the following code:
*
* #pragma omp parallel for
* for (int i = 0; i < N; ++i) {
* int data1 = -1, data2 = -1, data3 = -1;
* int depvar;
* #pragma omp task shared(data1) depend(inout: depvar)
* {
* data1 = omp_get_global_thread_id();
* }
* #pragma omp task hidden helper shared(data2) depend(inout: depvar)
* {
* data2 = omp_get_global_thread_id();
* }
* #pragma omp task shared(data3) depend(inout: depvar)
* {
* data3 = omp_get_global_thread_id();
* }
* #pragma omp taskwait
* assert(data1 == 0 || data1 > __kmp_num_hidden_helper_threads);
* assert(data2 > 0 && data2 <= __kmp_num_hidden_helper_threads);
* assert(data3 == 0 || data3 > __kmp_num_hidden_helper_threads);
* }
*/
#include "common.h"
extern "C" {
struct kmp_task_t_with_privates {
kmp_task_t task;
};
struct anon {
int32_t *data;
};
}
kmp_int32 __kmp_hidden_helper_threads_num;
kmp_int32 omp_task_entry(kmp_int32 gtid, kmp_task_t_with_privates *task) {
auto shareds = reinterpret_cast<anon *>(task->task.shareds);
auto p = shareds->data;
*p = __kmpc_global_thread_num(nullptr);
return 0;
}
template <bool hidden_helper_task> void assert_gtid(int v) {
if (__kmp_hidden_helper_threads_num) {
if (hidden_helper_task) {
assert(v > 0 && v <= __kmp_hidden_helper_threads_num);
} else {
assert(v == 0 || v > __kmp_hidden_helper_threads_num);
}
} else {
assert(v >= 0);
}
}
int main(int argc, char *argv[]) {
__kmp_hidden_helper_threads_num = get_num_hidden_helper_threads();
constexpr const int N = 1024;
#pragma omp parallel for
for (int i = 0; i < N; ++i) {
int32_t data1 = -1, data2 = -1, data3 = -1;
int depvar;
int32_t gtid = __kmpc_global_thread_num(nullptr);
// Task 1, regular task
auto task1 = __kmpc_omp_task_alloc(
nullptr, gtid, 1, sizeof(kmp_task_t_with_privates), sizeof(anon),
reinterpret_cast<kmp_routine_entry_t>(omp_task_entry));
auto shareds = reinterpret_cast<anon *>(task1->shareds);
shareds->data = &data1;
kmp_depend_info_t depinfo1;
depinfo1.base_addr = reinterpret_cast<intptr_t>(&depvar);
depinfo1.flags.in = 1;
depinfo1.flags.out = 1;
depinfo1.len = 4;
__kmpc_omp_task_with_deps(nullptr, gtid, task1, 1, &depinfo1, 0, nullptr);
// Task 2, hidden helper task
auto task2 = __kmpc_omp_target_task_alloc(
nullptr, gtid, 1, sizeof(kmp_task_t_with_privates), sizeof(anon),
reinterpret_cast<kmp_routine_entry_t>(omp_task_entry), -1);
shareds = reinterpret_cast<anon *>(task2->shareds);
shareds->data = &data2;
kmp_depend_info_t depinfo2;
depinfo2.base_addr = reinterpret_cast<intptr_t>(&depvar);
depinfo2.flags.in = 1;
depinfo2.flags.out = 1;
depinfo2.len = 4;
__kmpc_omp_task_with_deps(nullptr, gtid, task2, 1, &depinfo2, 0, nullptr);
// Task 3, regular task
auto task3 = __kmpc_omp_task_alloc(
nullptr, gtid, 1, sizeof(kmp_task_t_with_privates), sizeof(anon),
reinterpret_cast<kmp_routine_entry_t>(omp_task_entry));
shareds = reinterpret_cast<anon *>(task3->shareds);
shareds->data = &data3;
kmp_depend_info_t depinfo3;
depinfo3.base_addr = reinterpret_cast<intptr_t>(&depvar);
depinfo3.flags.in = 1;
depinfo3.flags.out = 1;
depinfo3.len = 4;
__kmpc_omp_task_with_deps(nullptr, gtid, task3, 1, &depinfo3, 0, nullptr);
__kmpc_omp_taskwait(nullptr, gtid);
// FIXME: 8 here is not accurate
assert_gtid<false>(data1);
assert_gtid<true>(data2);
assert_gtid<false>(data3);
}
std::cout << "PASS\n";
return 0;
}
// CHECK: PASS