c++ - 有效地收集/分散任务

标签 c++ multithreading c++11 mpi task-queue

我正在使用的 MPI 实现本身并不支持完整的多线程操作(最高级别是 MPI_THREAD_SERIALIZED,原因很复杂),所以我试图将来自多个线程的请求汇集到一个单个工作线程,然后将结果分散回多个线程。

通过使用并发队列,我可以轻松地处理收集本地请求任务,并且 MPI native 支持排队异步任务。然而,问题是让双方相互交谈:

为了将响应分散回各个线程,我需要对当前进行中的请求调用类似 MPI_Waitany 的方法,但在此期间 MPI worker 被有效阻塞,因此它无法从本地工作人员那里收集和提交任何新任务。

// mpi worker thread
std::vector<MPI_Request> requests; // in-flight requests
while(keep_running)
{
    if(queue.has_tasks_available())
    {
        MPI_Request r;
        // task only performs asynchronous MPI calls, puts result in r
        queue.pop_and_run(task, &r);
        requests.push_back(r);
    }
    int idx;
    MPI_Waitany(requests.size(), requests.data(), &idx,
                MPI_STATUS_IGNORE); // problems here! can't get any new tasks
    dispatch_mpi_result(idx); // notifies other task that it's response is ready
    // ... erase the freed MPI_Request from requests
}

类似地,如果我只是让 mpi worker 等待新任务从并发队列中可用,然后使用类似 MPI_Testany 的方式轮询 MPI 响应,那么最好的响应可能要么实际到达本地 worker 需要很长时间,最坏的情况是 mpi worker 会死锁,因为它正在等待本地任务,但所有任务都在等待 mpi 响应。

// mpi worker thread
std::vector<MPI_Request> requests; // in-flight requests
while(keep_running)
{
    queue.wait_for_available_task(); // problem here! might deadlock here if no new tasks happen to be submitted
    MPI_Request r;
    queue.pop_and_run(task, &r);
    requests.push_back(r);
    int idx;
    MPI_Testany(requests.size(), requests.data(), &idx, MPI_STATUS_IGNORE);
    dispatch_mpi_result(idx); // notifies other task that its response is ready
    // ... erase the freed MPI_Request from requests
}

我能看到的解决这两个问题的唯一解决方案是让 mpi worker 只轮询双方,但这意味着我有一个永久 Hook 的线程来处理请求:

// mpi worker thread
std::vector<MPI_Request> requests; // in-flight requests
while(keep_running)
{
    if(queue.has_tasks_available())
    {
        MPI_Request r;
        // task only performs asynchronous MPI calls, puts result in r
        queue.pop_and_run(task, &r);
        requests.push_back(r);
    }
    int idx;
    MPI_Testany(requests.size(), requests.data(), &idx, MPI_STATUS_IGNORE);
    dispatch_mpi_result(idx); // notifies other task that its response is ready
    // ... erase the freed MPI_Request from requests
}

我可以引入某种休眠功能,但这似乎是一种 hack,会降低我的吞吐量。对于这种饥饿/低效率问题,是否有其他解决方案?

最佳答案

我担心你已经接近你可以用你的最终解决方案做的最好的了,循环检查本地线程和MPI_Testany(或更好的MPI_Testsome)的新任务>).

您可以做的一件事就是为此投入整个核心。优点是,这很简单,具有低延迟并提供可预测的性能。在现代 HPC 系统上,这通常是 > 20 个内核,因此开销 < 5%。如果您的应用程序受内存限制,则开销甚至可以忽略不计。不幸的是,这会浪费 CPU 周期和能源。一个小的修改是在循环中引入 usleep。您必须调整 sleep 时间以平衡利用率和延迟。

如果你想为应用程序使用所有内核,你必须小心 MPI 线程不会从计算线程中窃取 CPU 时间。我假设你的队列实现是阻塞的,即不忙等待。这导致了计算线程在等待时可以给 CPU 时间给 MPI 线程的情况。不幸的是,发送这可能不是真的,因为工作人员可以在将任务放入队列后立即继续。

您可以做的是增加 MPI 线程的 nice 级别(降低优先级),以便它主要在计算线程等待结果时运行。您还可以在循环中使用 sched_yield 向调度程序提供一些提示。虽然两者都是在 POSIX 中定义的,但它们的语义非常弱并且强烈依赖于 actual scheduler implementation .用 sched_yield 实现繁忙的等待循环通常不是一个好主意,但您别无选择。 OpenMPI 和 MPICH 在某些情况下实现了类似的循环。

额外的 MPI 线程的影响取决于您的计算线程的紧密耦合程度。例如。如果它们经常处于障碍中,则会严重降低性能,因为仅延迟单个线程就会延迟所有线程。

最后,如果您希望实现高效,则必须针对特定系统进行测量和调整。

关于c++ - 有效地收集/分散任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37915579/

相关文章:

c++ - 为什么使用 vector 会出现链接器错误?

c++ - 在字符串中添加整数值

c++ - 执行 ->[0] 时预期的不合格 ID

c++ - 我可以使用 std::vector 来调用接受数组/指针参数的函数吗?

c++ - Qt:信号主线程

c++ - 无法从 main.cpp 中的共享库捕获异常

java - newFixedThreadPool() 与 newCachedThreadPool()

winforms - BackgroundWorker.ReportProgress() 和 Control.BeginInvoke() 之间的区别

c++ - unique_ptr 的临时只读拷贝

c++ - 指针 vector 和删除特定值