可以以有效的方式在模拟循环内进行多线程吗?

Posted

技术标签:

【中文标题】可以以有效的方式在模拟循环内进行多线程吗?【英文标题】:Possible to multi-thread inside the loop of simulation in an efficient manner? 【发布时间】:2017-04-18 11:45:18 【问题描述】:

我的问题是关于如何正确地设计以下多线程,而不是真正降低我的模拟性能,而不是提高它。

假设您有一个名为 MyClass 的类,它包含几个大数组(每个大约 500MB)和一个使用这些数组处理信息的函数:

class MyClass

private:
    int *data1 = new int[ARRAY_SIZE]();
    int *data2 = new int[ARRAY_SIZE]();

public:
    void fillData(); //any function that fills the inner data
    void processData(const int iteration);

每次模拟迭代,都会处理 4 个 MyClass 实例。在我的理想世界中,我想做的是将每个这样的实例传递给一个线程,然后在每个线程内调用instance.processData()。使用#include <thread> 如下所示:

int main()

    MyClass inst1,inst2, inst3, inst4;

    //<----- here you would have code that fills the arrays inside each instance of MyClass

    for(int iteration=0; iteration<MAX_ITERATIONS; iteration++)
    
        std::thread t1(&MyClass::processData, &inst1, iteration);
        std::thread t2(&MyClass::processData, &inst2, iteration);
        std::thread t3(&MyClass::processData, &inst3, iteration);
        std::thread t4(&MyClass::processData, &inst4, iteration);

        t1.join();
        t2.join();
        t3.join();
        t4.join();
    

    return 0;

我每次迭代都将MyClass 实例分派给线程的原因是,在每个实例的processData 结束后,我在每次迭代的每个实例中的数据结果之间进行比较.

问题在于它所描述的代码实际上比非多线程版本慢得多(比如慢几个数量级)。那么问题就变成了:我做错了什么?考虑到我必须在每次迭代结束时比较每个实例的处理结果,有没有办法改进?

PS1:我绝对不能并行化processData 中包含的进程。这是 100% 不可能的。

PS2:虽然我不能透露与真实代码本身相关的任何内容,但如果有帮助,我可以将上面写的 sn-ps 变成一个真正的可编译示例。虽然我认为没有它,这一点可能已经很清楚了。

【问题讨论】:

在循环内做线程开销是非常低效的。 关于PS2,是的,请提供minimal reproducible example。特别是,如果没有任何此类代码,很难解决您关于“每次迭代中每个实例的数据结果之间的比较”的评论。由于这是关于性能的,因此示例也应该具有类似的性能特征,这样您的 MCVE 的改进可能会转化为您的真实代码。 【参考方案1】:

有几件事可能会导致您发现使用四个线程而不是一个线程会降低性能。以下是要检查的基本事项:

并行化开销:这是创建和同步线程的成本。如果在processData 中完成的工作量很低并且您有大量迭代,那么线程创建和销毁成本可能是一个问题。如果您在 processData() 中有任何同步构造,例如屏障、锁或原子操作,这些可能是导致速度变慢的原因。

Thrashing:当单个线程执行时,活动内存集(正在写入和读取的内存)通常比运行多个线程时小得多。这可能会导致大量的缓存未命中(即,由于多个线程必须共享 L2 和 L3 缓存)。如果您的程序超出系统上的物理内存量(即交换),多线程可能会导致页面抖动,而单线程则不会。

资源争用:如果您的进程正在对磁盘或网络进行读/写操作,您可能会遇到类似于该资源抖动的情况,其中不同的线程会阻止彼此有效地访问它。

False Sharing:这是线程写入和读取同一缓存行上不同位置的地方,反复导致计算被丢弃,因为缓存行无效和刷新。

有助于更好诊断的问题:

    串行运行时每次迭代的总执行时间是多少?

    使用四个线程时,每次迭代的总挂钟执行时间是多长时间?

    processData() 是什么类型的运算/算法(例如,排序、稀疏线性代数、密集线性

    这个系统的物理规格是什么(即它有多少物理和逻辑内核,缓存有多大,有多少物理内存)?

    执行了多少次迭代?

【讨论】:

以上是关于可以以有效的方式在模拟循环内进行多线程吗?的主要内容,如果未能解决你的问题,请参考以下文章

#导入MD文档图片#JMeter-多线程组间通信

Delphi多线程访问公共变量,可以吗

for循环改为多线程方式进行执行

JAVA多线程用实现Runnable接口的方式创建线程

MFC多线程程序可以并行方式运行吗?

HTML5之Javascript多线程