C++ 为什么一个线程比只调用一个函数快,mingw
当我调用函数时,执行时间是6.8秒。 从线程调用它的时间是3.4秒 当使用2线程时为1.8秒。无论我使用什么优化,口粮都保持不变 在VisualStudio中,时间与预期的3.1、3和1.7秒相同C++ 为什么一个线程比只调用一个函数快,mingw,c++,multithreading,performance,function,mingw,C++,Multithreading,Performance,Function,Mingw,当我调用函数时,执行时间是6.8秒。 从线程调用它的时间是3.4秒 当使用2线程时为1.8秒。无论我使用什么优化,口粮都保持不变 在VisualStudio中,时间与预期的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];
#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;
}
CPU-核心2 Duo 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-14461-14392-721。正如其他人所建议的,更改三次测试的顺序以获得更多的洞察力。此外,您拥有一台多核机器这一事实很好地解释了为什么使用两个线程完成一半的工作,每个线程只需要一半的时间。查看您的CPU使用率监视器(控制移位转义),以了解在运行期间有多少内核被最大化。这里不是一个
缓存问题
用户创建的线程和主线程可能有不同的运行库。
您可以比较计算结果a[i][j]+=k*sin(j)-j*cos(k)差异是数学库中实现sin()
和cos()的结果
-如果您将这些函数的调用替换为其他需要花费时间的调用,步骤与0和步骤1之间的显著差异就会消失
请注意,我看到了与gcc(tdm-1)4.6.1
的区别,它是一个针对32位二进制文件的32位工具链。优化没有任何区别(这并不奇怪,因为它似乎是数学库中的东西)
但是,如果我使用64位工具链gcc(tdm64-1)4.6.1
进行构建,则不会出现差异-无论构建是创建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
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
- 使用64位TDM 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
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)通过提供的导入库链接到msvcrt.dll
系统dll中的sin()
/cos()
实现:
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)
更新/结论:
在调试程序中进行了一些探索之后,我发现主线程与显式创建的线程的计时差异是由于FPU的精度设置为非默认设置(可能问题中的MinGW运行时在启动时执行此操作。)在thread()
函数耗时两倍的情况下,FPU被设置为64位精度(REAL10
或在MSVC speak中\u PC_64
)。当FPU控制字不是0x27f(默认状态?),则msvcrt.dll
运行时将在sin()
和cos()
函数(可能还有其他浮点函数)中执行以下步骤:
- 保存当前FPU控制字
- 将FPU控制字设置为0x27f(我相信可以修改此值)
- 执行
fsin
/fcos
操作
- 恢复保存的FPU控制字
如果FPU控制字已设置为预期/期望的0x27f值,则将跳过保存/还原FPU控制字。显然,保存/还原FPU控制字的成本很高,因为它似乎使函数所需的时间增加了一倍
您可以通过在调用thread()
之前将以下行添加到main()
来解决此问题:
\u control87(_PC_53,_MCW_PC);//需要
原因是主线程正在进行64位浮点运算,而线程正在进行53位浮点运算
您可以通过将代码更改为
...
extern "C" unsigned int _control87( unsigned int newv, unsigned int mask );
DWORD WINAPI thread(LPVOID p)
{
printf( "_control87(): 0x%.4x\n", _control87( 0, 0 ) );
_control87(0x00010000,0x00010000);
...
输出将是:
c:\temp>test
_control87(): 0x8001f
0 - 2667
_control87(): 0x9001f
1 - 2683
_control87(): 0x9001f
_control87(): 0x9001f
2 - 1373
c:\temp>mingw32-c++ --version
mingw32-c++ (GCC) 4.6.2
您可以看到0将在没有0x10000标志的情况下运行,但一旦设置,运行速度将与1和2相同。如果查找函数,您将看到此值是_PC_53标志,如果保留为零,则将精度设置为53而不是64
出于某种原因,Mingw在进程初始化时没有将其设置为与CreateThread()在线程创建时相同的值
另一项工作是打开SSE2,它将运行得更快,但可能会产生不同的结果
c:\temp>test
0 - 1341
1 - 1326
2 - 702
我相信默认情况下64位处理器都支持SSE2,因为所有64位处理器都支持SSE2。我能感觉到神秘的键入…@SethCarnegie-Nah…我一眼就看不出问题,而且我没有安装mingw32。是的,尝试不同的顺序,看看这是否有区别。我打赌“第一次”需要更长的时间,而不是线程与非线程。MinGW 4.6.2。正是您的代码。编译选项:-O2。结果:0-1446 1-1439 2-721
。与std::thread
的结果相同。我尝试在函数调用之前运行线程,我移动了浮点a[N][N];
内部结构b
-结果不正确change@user1978768这是一个exe文件,由my mingw根据您的代码编译-每次调用后所有元素的总和相同:3189909.237955Pls。请检查RTL选择并验证您正在使用哪个。mingw定义。#定义MSVCRT_版本0x0600看到了吗