C++ 标准::日志函数无法缩放

c++ std::log function doesn't scale

本文关键字:缩放 日志 标准 C++ 函数      更新时间:2023-10-16

我正在运行一段代码来测试log()函数是否在缩放中。我在 4 核机器上运行它,结果显示它没有缩放。我的代码如下:

#include<iostream>
#include<cmath>
#include<omp.h>
#include<chrono>
using namespace std;
typedef std::chrono::milliseconds ms;
int main(){
        #pragma omp parallel for schedule(static)
        for(int i=0;i<4;i++){
                auto start = std::chrono::high_resolution_clock::now();
                double tmp=1.0;
                for(double j=0.0;j<10000000;j++){
                        tmp=log(j);
                }
                auto end = std::chrono::high_resolution_clock::now();
                #pragma omp critical
                {
                        cout<<"tmp="<<tmp<<endl;
                        cout<<"Thread "<<omp_get_thread_num()<<" calculated tmp, time used: "<<std::chrono::duration_cast<ms>(end - start).count() << "ms" << endl;
                }
        }
        return 0;
}

如果我使用 4 个线程,结果是:

Thread 1 calculated tmp, time used: 21ms
Thread 0 calculated tmp, time used: 21ms
Thread 2 calculated tmp, time used: 21ms
Thread 3 calculated tmp, time used: 21ms

如果仅使用 1 个线程,则结果为:

Thread 0 calculated tmp, time used: 20ms
Thread 0 calculated tmp, time used: 16ms
Thread 0 calculated tmp, time used: 16ms
Thread 0 calculated tmp, time used: 15ms

因此,并行运行时,每个线程比顺序运行花费更长的时间。有谁知道为什么它不扩展?std::log 实际上是如何工作的(线程可能必须共享一些东西)?有没有办法让 log() 函数可以扩展?谢谢!

编辑1:我将迭代次数增加到 10e10 次,但结果显示并行版本甚至更慢,所以这里可能不是线程创建时间占主导地位。4 线程:

Thread 0 calculated tmp, time used: 17890ms
Thread 2 calculated tmp, time used: 17890ms
Thread 1 calculated tmp, time used: 17892ms
Thread 3 calculated tmp, time used: 17892ms

1 线程:

Thread 0 calculated tmp, time used: 15664ms
Thread 0 calculated tmp, time used: 15659ms
Thread 0 calculated tmp, time used: 15660ms
Thread 0 calculated tmp, time used: 15647ms

EDIT2:我让tmp变量最后被打印出来,这样log()就不能被优化出来了。但结果还是和以前一样。还有其他想法吗?

EDIT3:所以总执行时间的加速是3.5,即使迭代次数增加,它也不会再变高。我不确定这是否是一个合理的加速,因为我期待像这样的简单程序的加速,比如 3.7 或 3.8。

简而言之

你是完全正确的。 但是要完全衡量多核性能的改进,你不应该仅仅依靠对单个线程进行计时:你还应该计时整体执行。

多核架构似乎实现了更高的吞吐量,但代价是当多个内核处于活动状态时,每个内核略有减少。 这是另一个具有类似观察结果的基准(使用 std::thread 而不是 OMP)。

因此,每个单独的日志计算都无法扩展,但 oveall 系统做得很好。

其他详细信息

如果您要运行一些整体端到端测量:

int main()
{
    auto common_start = std::chrono::high_resolution_clock::now();
    ... 
    auto common_end = std::chrono::high_resolution_clock::now();
    cout << "Overall calculated time : " << std::chrono::duration_cast<ms>(common_end - common_start).count() << "ms" << endl;
    return 0; 
}

您肯定会同时观察到更好的整体性能。

这是我自己的 4 个线程的时间:

Thread 2 calculated tmp, time used: 269ms
Thread 3 calculated tmp, time used: 274ms
Thread 0 calculated tmp, time used: 281ms
Thread 1 calculated tmp, time used: 289ms
Overall calculated time : 296ms

还有一个:

Thread 0 calculated tmp, time used: 218ms
Thread 0 calculated tmp, time used: 218ms
Thread 0 calculated tmp, time used: 229ms
Thread 0 calculated tmp, time used: 224ms
Overall calculated time : 903ms

正如您已经观察到的,使用一个线程执行计算的速度提高了 22%。 但总的来说,执行相同数量的计算所需的时间是多线程所需时间的 3 倍。

因此,这一切都与吞吐量有关:单线程仅以 44 K 迭代/毫秒的速度运行,而多线程的单线程为 135 K 迭代/毫秒。