【问题标题】:Making a for loop faster by splitting it in threads通过将其拆分为线程来加快 for 循环
【发布时间】:2021-02-13 17:15:26
【问题描述】:

在我开始之前,让我说我只使用过一次线程,当时我们在大学里被教导过线程。因此,我使用它们的经验几乎为零,我不知道我想做的是否是个好主意。

我正在做我自己的一个项目,并且我正在尝试使 for 循环快速运行,因为我需要循环中的计算以用于实时应用程序。在“优化”循环中的计算之后,我已经接近了所需的速度。但是,它仍然需要改进。

然后,我想起了线程。我想如果我将它分成 4 部分,我可以让循环运行得更快,每个部分用于我机器的每个核心。所以这就是我试图做的:

void doYourThing(int size,int threadNumber,int numOfThreads) {
    int start = (threadNumber - 1) * size / numOfThreads;
    int end = threadNumber * size / numOfThreads;
    for (int i = start; i < end; i++) {
        //Calculations...
    }
}
int main(void) {
    int size = 100000;
    int numOfThreads = 4;

    int start = 0;
    int end = size / numOfThreads;
    std::thread coreB(doYourThing, size, 2, numOfThreads);
    std::thread coreC(doYourThing, size, 3, numOfThreads);
    std::thread coreD(doYourThing, size, 4, numOfThreads);

    for (int i = start; i < end; i++) {
        //Calculations...
    }
    coreB.join();
    coreC.join();
    coreD.join();
}

这样,计算时间从 60 毫秒变为 40 毫秒。

问题:

1)我的线程真的在不同的内核上运行吗?如果这是真的,我预计速度会更快。更具体地说,我假设它需要接近初始时间的 1/4。

2)如果他们不这样做,我应该使用更多线程来拆分工作吗?它会让我的循环更快还是更慢?

【问题讨论】:

  • 你是如何编译这个的,用什么编译器和什么标志?计算是否有意义地长,足以克服线程开销?结果如何合并?
  • @FrançoisAndrieux 如果这回答了您的第一个问题,我正在使用 Visual Studio。我不知道哪些计算被认为很长,但循环最初需要大约 60 毫秒才能完成。至于结果,我只是在整个过程之前和之后使用了一个断点。
  • 您了解调试和发布构建以及编译器可以优化您的代码的程度吗?如果您在调试构建中测量了时间,那么测量几乎没有意义。切换到 Release 并重试。
  • 您无法在调试器中可靠地测量执行时间。您需要在优化的构建中自己测量时间。

标签: c++ multithreading optimization parallel-processing


【解决方案1】:

(1)。 @François Andrieux 提出的问题很好。因为在原始代码中有一个结构良好的for循环,如果你使用-O3优化,编译器可能会vectorize the computation。这种矢量化可以加快速度。

此外,这取决于计算中的关键路径是什么。根据阿姆达尔定律,可能的加速受到不可并行路径的限制。您可能会检查计算是否到达了您有锁的某个变量,那么时间也可能花在锁上。

(2)。要找出您计算机上的内核和线程总数,您可能有lscpu 命令,它将显示您计算机/服务器上的内核和线程信息

(3)。不一定是线程越多性能越好

【讨论】:

    【解决方案2】:

    有一个纯标题库in Github,它可能正是您所需要的。大概您的 doYourThing 处理输入向量(在您的代码中大小为 100000)并将结果存储到另一个向量中。在这种情况下,你需要做的就是说

    auto vectorOut = Lazy::runForAll(vectorIn, myFancyFunction);
    

    该库将根据您拥有的内核数量决定使用多少线程。

    另一方面,如果编译器能够对您的算法进行矢量化,并且看起来仍然像示例代码中那样将工作分成 4 个块是一个好主意,您可以像这样执行以下操作:

    #include "Lazy.h"
    
    void doYourThing(const MyVector& vecIn, int from, int to, MyVector& vecOut)
    {
      for (int i = from; i < to; ++i) {
        // Calculate vecOut[i]
      }
    }
    
    int main(void) {
      int size = 100000;
      MyVector vecIn(size), vecOut(size)
      // Load vecIn vector with input data...
      
      Lazy::runForAll({{std::pair{0, size/4}, {size/4, size/2}, {size/2, 3*size/4}, {3*size/4, size}},
        [&](auto indexPair) { 
          doYourThing(vecIn, indexPair.first, indexPair.second, vecOut);
        });
      // Now the results are in vecOut                                    
    }
    

    README.md 提供了有关并行执行的更多示例,您可能会觉得这些示例很有用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-04
      • 1970-01-01
      • 2021-06-29
      • 2013-02-05
      • 1970-01-01
      • 2023-01-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多