gpt4 book ai didi

c++ - 为什么一个线程比调用一个函数更快,mingw

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:19:22 25 4
gpt4 key购买 nike

当我调用函数时,执行时间是 6.8 秒。从线程调用它的时间是 3.4 秒当使用 2 线程时 1.8 秒。无论我使用什么优化,口粮都保持不变。

在 Visual Studio 中,时间与预期的一样,分别为 3.1、3 和 1.7 秒。

#include<math.h>
#include<stdio.h>
#include<windows.h>
#include <time.h>

using namespace std;

#define N 400

float a[N][N];

struct b{
int begin;
int end;
};

DWORD WINAPI thread(LPVOID p)
{
b b_t = *(b*)p;

for(int i=0;i<N;i++)
for(int j=b_t.begin;j<b_t.end;j++)
{
a[i][j] = 0;
for(int k=0;k<i;k++)
a[i][j]+=k*sin(j)-j*cos(k);
}

return (0);
}

int main()
{
clock_t t;
HANDLE hn[2];

b b_t[3];

b_t[0].begin = 0;
b_t[0].end = N;

b_t[1].begin = 0;
b_t[1].end = N/2;

b_t[2].begin = N/2;
b_t[2].end = N;

t = clock();
thread(&b_t[0]);
printf("0 - %d\n",clock()-t);

t = clock();
hn[0] = CreateThread ( NULL, 0, thread, &b_t[0], 0, NULL);
WaitForSingleObject(hn[0], INFINITE );
printf("1 - %d\n",clock()-t);

t = clock();
hn[0] = CreateThread ( NULL, 0, thread, &b_t[1], 0, NULL);
hn[1] = CreateThread ( NULL, 0, thread, &b_t[2], 0, NULL);
WaitForMultipleObjects(2, hn, TRUE, INFINITE );
printf("2 - %d\n",clock()-t);

return 0;
}

时间:

0 - 6868
1 - 3362
2 - 1827

CPU - 酷睿 2 双核 T9300

操作系统 - Windows 8、64 位

编译器:mingw32-g++.exe,gcc版本4.6.2

编辑:

尝试了不同的顺序,结果相同,甚至尝试了不同的应用程序。任务管理器显示函数和 1 个线程的 CPU 使用率约为 50%,2 个线程的 CPU 使用率约为 100%

每次调用后所有元素之和都一样:3189909.237955

Cygwin 结果:2.5、2.5 和 2.5 秒Linux 结果(pthread):3.7、3.7 和 2.1 秒

@borisbn 结果:0 - 1446 1 - 1439 2 - 721。

最佳答案

差异是数学库中某些实现 sin()cos() 的结果 - 如果您将对这些函数的调用替换为其他需要时间步骤与 0 和步骤 1 之间的显着差异消失。

请注意,我看到了与 gcc (tdm-1) 4.6.1 的区别,后者是针对 32 位二进制文​​件的 32 位工具链。优化没有区别(这并不奇怪,因为它似乎是数学库中的东西)。

但是,如果我使用 gcc (tdm64-1) 4.6.1 构建,这是一个 64 位工具链,则差异不会出现 - 无论build 正在创建 32 位程序(使用 -m32 选项)或 64 位程序(-m64)。

下面是一些示例测试运行(我对源代码做了一些小修改以使其与 C99 兼容):

  • 使用 32 位 TDM MinGW 4.6.1 编译器:

    C:\temp>gcc --version
    gcc (tdm-1) 4.6.1

    C:\temp>gcc -m32 -std=gnu99 -o test.exe test.c

    C:\temp>test
    0 - 4082
    1 - 2439
    2 - 1238
  • 使用 64 位 TDM 4.6.1 编译器:

    C:\temp>gcc --version
    gcc (tdm64-1) 4.6.1

    C:\temp>gcc -m32 -std=gnu99 -o test.exe test.c

    C:\temp>test
    0 - 2506
    1 - 2476
    2 - 1254

    C:\temp>gcc -m64 -std=gnu99 -o test.exe test.c

    C:\temp>test
    0 - 3031
    1 - 3031
    2 - 1539

更多信息:

32 位 TDM 发行版 (gcc (tdm-1) 4.6.1) 链接到 中的 sin()/cos() 实现msvcrt.dll 通过提供的导入库的系统 DLL:

c:/mingw32/bin/../lib/gcc/mingw32/4.6.1/../../../libmsvcrt.a(dcfls00599.o)
0x004a113c _imp__cos

虽然 64 位分发版 (gcc (tdm64-1) 4.6.1) 似乎没有这样做,而是链接到分发版提供的一些静态库实现:

c:/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/4.6.1/../../../../x86_64-w64-mingw32/lib/../lib32/libmingwex.a(lib32_libmingwex_a-cos.o)
C:\Users\mikeb\AppData\Local\Temp\cc3pk20i.o (cos)

更新/结论:

在调试器中逐步探索 msvcrt.dllcos() 实现程序集后,我发现时间上的差异主线程与显式创建线程的区别是由于 FPU 的精度被设置为非默认设置(可能是有问题的 MinGW 运行时在启动时这样做)。在 thread() 函数花费两倍时间的情况下,FPU 设置为 64 位精度(REAL10 或在 MSVC 中为 _PC_64)。当 FPU 控制字不是 0x27f(默认状态?)时,msvcrt.dll 运行时将在 sin() 中执行以下步骤cos() 函数(可能还有其他浮点函数):

  • 保存当前FPU控制字
  • 将FPU控制字设置为0x27f(我相信这个值是可以修改的)
  • 执行fsin/fcos操作
  • 恢复保存的FPU控制字

如果 FPU 控制字已设置为预期/所需的 0x27f 值,则跳过 FPU 控制字的保存/恢复。显然,保存/恢复 FPU 控制字的成本很高,因为它似乎使函数花费的时间加倍。

您可以通过在调用thread() 之前将以下行添加到main() 来解决问题:

_control87( _PC_53, _MCW_PC);   // requires <float.h>

关于c++ - 为什么一个线程比调用一个函数更快,mingw,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14329214/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com