C++ 在线程之间拆分任务总是值得的吗?

C++ 在线程之间拆分任务总是值得的吗?,c++,multithreading,c++14,C++,Multithreading,C++14,我曾经认为,当我们执行阻塞操作时,多线程是最有效的,在此期间,我们可以在另一个线程上继续执行其他指令 最近我执行了一个简单的测试。我创建了一个数据向量,在线程之间平均分割行,并将执行时间与一个线程工作者进行比较。多线程是赢家 这是我的密码: #include <iostream> #include <thread> #include <vector> #include <mutex> #include <numeric> #includ

我曾经认为,当我们执行阻塞操作时,多线程是最有效的,在此期间,我们可以在另一个线程上继续执行其他指令

最近我执行了一个简单的测试。我创建了一个数据向量,在线程之间平均分割行,并将执行时间与一个线程工作者进行比较。多线程是赢家

这是我的密码:

#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <numeric>
#include <chrono>

double g_sum = 0;
std::mutex g_mutex;

void worker(const std::vector<double>& vec)
{
    const auto vectorSum = std::accumulate(vec.begin(), vec.end(), 0.0);
    std::lock_guard<std::mutex> lg(g_mutex);
    std::cout << "Thread-Worker adding " << vectorSum << " to final sum ("<< g_sum <<")\n";
    g_sum += vectorSum;
}

int main()
{
    const int ROW_SIZE = 10000000;
    const int threadsSize = std::thread::hardware_concurrency();
    std::cout << "Task will be seprated on " << threadsSize << " threads\n";

    // data vector with row for every thread
    std::vector<std::vector<double>> dataVector;
    double fillVal = 1.1;
    for (auto i = 0; i < threadsSize; ++i, fillVal += 1.1)
    {
        dataVector.push_back(std::vector<double>(ROW_SIZE, fillVal));
    }

    std::vector<std::thread> threadContainer;
    auto start = std::chrono::system_clock::now();
    for (const auto& row : dataVector)
    {
        std::thread thread(&worker, std::ref(row));
        threadContainer.push_back(std::move(thread));
    }
    for (auto& thread : threadContainer)
    {
        thread.join();
    }
    auto end = std::chrono::system_clock::now();
    std::chrono::duration<double> elapsed_seconds = end-start;
    std::cout << "threads time: " << elapsed_seconds.count() << "s\n";

    // main thread only
    g_sum = 0;
    start = std::chrono::system_clock::now();
    for (const auto& row : dataVector)
    {
        const auto vectorSum = std::accumulate(row.begin(), row.end(), 0.0);
        std::cout << "Main Thread adding " << vectorSum << " to final sum ("<< g_sum <<")\n";
        g_sum += vectorSum;
    }
    end = std::chrono::system_clock::now();
    elapsed_seconds = end-start;
    std::cout << "one-thread time: " << elapsed_seconds.count() << "s\n";
}
#包括
#包括
#包括
#包括
#包括
#包括
双g_和=0;
std::mutex g_mutex;
无效工作程序(const std::vector和vec)
{
const auto vectorSum=std::accumulate(vec.begin(),vec.end(),0.0);
标准:锁紧保护lg(g_互斥);

没有简单的答案。 多线程计时结果取决于实现。它可能更快,也可能更快。 有很多微妙的地方:

  • 硬件线程数。如果在硬件线程数为1时创建100个线程,则多线程解决方案将比单线程慢。因为线程上下文切换所需的时间太长
  • 同步实现。例如,日志记录是一个缓慢的操作。关键部分上下文中的日志记录是不好的,因为它很慢。如果您将日志记录移动到另一个线程中,您可以加快它:
    std::cout具有固定线程数的基于任务的并行性(无超额订阅)通常是性能最好的方法。但是,任务必须具有合理的大小,以避免过多的调度开销。IIRC作为tbb的经验法则,任务的执行至少需要10k个周期。您必须注意的一个重要细节是不同任务之间的同步。因为您通常不知道在执行任务的线程中,必须小心不要引入死锁(例如,在持有锁的同时生成任务)

    然而,一个问题是否能通过多个任务有效地解决在很大程度上取决于具体的问题以及它如何映射到任务。对于您的示例来说,这当然很有效,但不能概括为始终是更好的选择


    只是附带说明:我建议使用现有的任务调度框架(例如,tbb)不要滚动你自己的线程池。

    不,拆分并不总是正确的选择。线程创建成本很高。没有银弹-只需给你的应用程序在N个线程之间拆分的选项。有时你可以使用线程池。线程创建有成本。线程之间的同步有成本。创建和管理线程的代码有成本cost.Threads not==免费性能。它们并不总是值得的,有时甚至可能会使事情变得更慢。你需要一个问题,在这个问题上它们是有意义的,并且收益大于成本。请尝试将行大小设置为10。将执行输出的时间包含在计时内可能会扭曲结果。@MichaelChourdakis线程的成本是多少意思是?这仅仅是编译时成本吗?请进一步解释。@JesperJuhl肯定线程不是免费性能成本,这就是为什么存在这个问题的原因……你知道线程管理的成本如何与使用它的利润相比较吗????我问的是已经平均分割的可以同时工作的单个任务。它有已在合理数量的线程上拆分(std::hardware\u并发)。同步已最小化。问题仍然没有回答。这似乎是我想讨论的最佳答案。无论如何,我将在此主题下执行更多测试。Thanks@Piodo如果你对基于任务的并行性感兴趣,我建议你去看看。