使用 std 库的 c++ 多线程:比预期慢

c++ Multithreading with std library: slower than expected

本文关键字:多线程 std 库的 c++ 使用      更新时间:2023-10-16

我尝试使用标准库,特别是线程库。这个想法是用和没有多线程填充矩阵。我有 8 个内核。

void fillMatrix(int ID, std::vector<std::vector<double>> & lMatrix,int inThread){
    for (int i = 0; i < 10000; ++i){
        if (i % inThread == ID){
            for (int j = 0; j < 10000; ++j){
                lMatrix[i][j] = 123456.0;
            }
        }
    }
}

void testMT(int inThread){
    std::vector<std::thread> lPool;
    std::vector<std::vector<double>> lMatrix(10000, std::vector<double>(10000));
    for (int i = 0; i < inThread; ++i){
        lPool.push_back(std::thread(std::bind(&fillMatrix,i, lMatrix,inThread)));
    }
    for (std::thread & t : lPool){
        t.join();
    }
}

主代码:

int main(){
    const clock_t begin_time1 = clock();
    testMT(1);
    std::cout << float(clock() - begin_time1) / CLOCKS_PER_SEC;
}

testMT(1) 需要 2.1 秒运行, 而 testMT(8) 需要 7.032 秒。

知道吗?谢谢。

如果

这会减慢速度,那么您将非常糟糕。

在 1 个线程的情况下,它总是为真,但在 8 个线程的情况下,它是真的,然后是假的假的......真。分支预测在此处失败。

最好将

第一个 for 循环拆分/划分到每个线程。就像线程 1 执行矩阵的前 1/8 一样,线程 2 执行第二个 1/8,依此类推。

像这样:

for(int i = 10000*ID/inThread; i< 10000*(ID+1)/inThread; ++i)
在 ID=0 的情况下,循环将从 0 变为 2500,ID

=1 将从 2500 变为 5000,依此类推。

您的代码无法按预期运行。线程运行后,您会注意到lMatrix的所有元素都没有设置为 123456.0 。这是因为std::bind将向量复制到自身中,然后将其发送到线程。您需要在绑定调用中使用std::ref(lMatrix)才能正常工作并使用相同的矩阵。

改变

std::bind(&fillMatrix,i, lMatrix,inThread)

std::bind(&fillMatrix, i, std::ref(lMatrix), inThread)

因此,增加的执行时间的很大一部分可能是由拷贝开销引起的;每次线程启动 10000 x 10000 个分配和拷贝(然后是解除分配)。

您的线程都在修改附近内存位置中的同一对象。这意味着内存的所有权必须不断地从一个线程到另一个线程。

这不是线程的明智用法。如果您希望在众多线程同时操作同一对象时保持性能,则需要专业知识来处理错误共享之类的事情。

在某种程度上,这是一个非常重要的问题。

    多线程代码
  • 始终比单线程代码慢!(最好的情况是,您仍然需要支付启动和停止线程的费用)

  • 并发使事情变得更快(有时)。

您的目标是让并发的收益超过使用线程的成本 - 并且好处如此显着,以至于它"支付"了代码增加的复杂性。 此示例未达到该目标。