为什么这些代码在Release版和Debug版中输出的结果不一样?

时间:2021-12-10 19:01:09
#include "stdafx.h"
#include <windows.h>
#include <process.h>
#define SIZEI 100000
unsigned int __stdcall ThreadFunc(LPVOID lpv)
{
int a[SIZEI], b[SIZEI];
int i=0, j=0;
for(i=0; i<10000; i++)
memcpy(a, b, sizeof(int)*SIZEI);
FILETIME start, end,user, kernel;
GetThreadTimes(GetCurrentThread(), &start, &end, &user, &kernel);
printf("Thread1:%d\n", kernel.dwLowDateTime);
return 0;
}
unsigned int __stdcall ThreadFunc2(LPVOID lpv)
{
int a[SIZEI], b[SIZEI];
int i=0, j=0;
for(i=0; i<10000; i++)
{
for(j=0; j<SIZEI; j++)
{
a[j] = b[j];
}
}
FILETIME start, end,user, kernel;
GetThreadTimes(GetCurrentThread(), &start, &end, &user, &kernel);
printf("Thread2:%d\n", kernel.dwLowDateTime);
return 0;
}
int main(int argc, char* argv[])
{
HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, (void*)NULL, 0, NULL);
WaitForSingleObject(hThread, INFINITE);

HANDLE hThread2 = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc2, (void*)NULL, 0, NULL);
WaitForSingleObject(hThread2, INFINITE);
return 0;
}

Debug版输出:
Thread1:77500000
Thread2:80468750

Release版输出:
Thread1:0
Thread2:0

另外,关于使用memcpy复制数组与自己做个循环复制数组在性能上有何差别?有定论吗?

7 个解决方案

#1


for(i=0; i<10000; i++)
memcpy(a, b, sizeof(int)*SIZEI);
这个用法错误,去掉for循环。

#2


GetThreadTimes(
  HANDLE hThread,
  LPFILETIME lpCreationTime,
  LPFILETIME lpExitTime,
  LPFILETIME lpKernelTime,
  LPFILETIME lpUserTime
);
Debug 和 Release 编译方式的区别,Debug不做任何优化;Release进行了各种优化,运行速度和代码大小都是较优的

#3


erdgzw(编译通过那一刻感觉真好) :
不能在for循环中使用memcpy吗?

#4


Estfania(Estfania):
两种版本的区别不用您再给我讲解一遍,我只想知道它们的输出结果不一样?

#5


up

#6


erdgzw(编译通过那一刻感觉真好) :
不能在for循环中使用memcpy吗?

----------------------------------
可以的, 好象你那不需要循环就已经达到你想要的效果了吧.

#7


查看两个版本程序的汇编代码终于会发现如下问题:
版本1:
; 27   :  int a[SIZEI] = {0}, b[SIZEI] = {0};
; 28   :  int i=0, j=0, k=0;
; 29   :  for(i=0; i<FORC; i++)
; 30   :  {
; 31   :  for(j=0; j<SIZEI; j++)
; 32   :  {
; 33   :  a[j] = b[j];
; 34   :  }
; 35   :  }
也就是说根本没有产生这段程序的目标代码!但是Debug版中:
; 15   :  for(i=0; i<FORC; i++)

mov DWORD PTR _i$[ebp], 0
jmp SHORT $L17588
$L17589:
mov eax, DWORD PTR _i$[ebp]
add eax, 1
mov DWORD PTR _i$[ebp], eax
$L17588:
cmp DWORD PTR _i$[ebp], 10000 ; 00002710H
jge SHORT $L17590

; 17   :  memcpy(a, b, sizeof(int)*SIZEI);

push 400000 ; 00061a80H
lea ecx, DWORD PTR _b$[ebp]
push ecx
lea edx, DWORD PTR _a$[ebp]
push edx
call _memcpy
add esp, 12 ; 0000000cH

; 18   :  }

jmp SHORT $L17589
明显是在Release版中这段代码被优化掉了!
两个线程函数中如果加入如下语句的时候,就到了非0值:
printf("a[0]:%d,b[0]:%d\n", a[0], b[0]);
版本2:
; 15   :  for(i=0; i<FORC; i++)
; 16   :  {
; 17   :  memcpy(a, b, sizeof(int)*SIZEI);

mov ecx, 100000 ; 000186a0H
lea esi, DWORD PTR _b$[esp+800040]
lea edi, DWORD PTR _a$[esp+800040]
dec eax
rep movsd
jne SHORT $L17595
它输出了代码!
我猜想原因是这样的,在版本1中,这段代码的所有操作只是涉及了本地变量,而且后来的输出以及函数返回都没有使用这些变量,所以编译器认为这些代码是垃圾,把它优化掉了!的确,如果在现实的编程中这些代码的确是无用的!但是,如果想在某个函数中使用一个空循环制造时间延迟的时候就要注意这个问题了!
避免这个问题的方法还有,把a和b声明成全局变量(编译器不敢保证不在别的地方不会用它)、返回a或b的一个元素、在堆上声明a和b等;
对比memcpy和自己循环赋值相比较的结果它们的性能是在优化的Release版本是一样的,因为它们的代码是一样的:
mov ecx, 100000 ; 000186a0H
mov esi, eax
mov edi, ebx
dec edx
rep movsd
在Debug版下,用memcpy性能要稍好一些。

#1


for(i=0; i<10000; i++)
memcpy(a, b, sizeof(int)*SIZEI);
这个用法错误,去掉for循环。

#2


GetThreadTimes(
  HANDLE hThread,
  LPFILETIME lpCreationTime,
  LPFILETIME lpExitTime,
  LPFILETIME lpKernelTime,
  LPFILETIME lpUserTime
);
Debug 和 Release 编译方式的区别,Debug不做任何优化;Release进行了各种优化,运行速度和代码大小都是较优的

#3


erdgzw(编译通过那一刻感觉真好) :
不能在for循环中使用memcpy吗?

#4


Estfania(Estfania):
两种版本的区别不用您再给我讲解一遍,我只想知道它们的输出结果不一样?

#5


up

#6


erdgzw(编译通过那一刻感觉真好) :
不能在for循环中使用memcpy吗?

----------------------------------
可以的, 好象你那不需要循环就已经达到你想要的效果了吧.

#7


查看两个版本程序的汇编代码终于会发现如下问题:
版本1:
; 27   :  int a[SIZEI] = {0}, b[SIZEI] = {0};
; 28   :  int i=0, j=0, k=0;
; 29   :  for(i=0; i<FORC; i++)
; 30   :  {
; 31   :  for(j=0; j<SIZEI; j++)
; 32   :  {
; 33   :  a[j] = b[j];
; 34   :  }
; 35   :  }
也就是说根本没有产生这段程序的目标代码!但是Debug版中:
; 15   :  for(i=0; i<FORC; i++)

mov DWORD PTR _i$[ebp], 0
jmp SHORT $L17588
$L17589:
mov eax, DWORD PTR _i$[ebp]
add eax, 1
mov DWORD PTR _i$[ebp], eax
$L17588:
cmp DWORD PTR _i$[ebp], 10000 ; 00002710H
jge SHORT $L17590

; 17   :  memcpy(a, b, sizeof(int)*SIZEI);

push 400000 ; 00061a80H
lea ecx, DWORD PTR _b$[ebp]
push ecx
lea edx, DWORD PTR _a$[ebp]
push edx
call _memcpy
add esp, 12 ; 0000000cH

; 18   :  }

jmp SHORT $L17589
明显是在Release版中这段代码被优化掉了!
两个线程函数中如果加入如下语句的时候,就到了非0值:
printf("a[0]:%d,b[0]:%d\n", a[0], b[0]);
版本2:
; 15   :  for(i=0; i<FORC; i++)
; 16   :  {
; 17   :  memcpy(a, b, sizeof(int)*SIZEI);

mov ecx, 100000 ; 000186a0H
lea esi, DWORD PTR _b$[esp+800040]
lea edi, DWORD PTR _a$[esp+800040]
dec eax
rep movsd
jne SHORT $L17595
它输出了代码!
我猜想原因是这样的,在版本1中,这段代码的所有操作只是涉及了本地变量,而且后来的输出以及函数返回都没有使用这些变量,所以编译器认为这些代码是垃圾,把它优化掉了!的确,如果在现实的编程中这些代码的确是无用的!但是,如果想在某个函数中使用一个空循环制造时间延迟的时候就要注意这个问题了!
避免这个问题的方法还有,把a和b声明成全局变量(编译器不敢保证不在别的地方不会用它)、返回a或b的一个元素、在堆上声明a和b等;
对比memcpy和自己循环赋值相比较的结果它们的性能是在优化的Release版本是一样的,因为它们的代码是一样的:
mov ecx, 100000 ; 000186a0H
mov esi, eax
mov edi, ebx
dec edx
rep movsd
在Debug版下,用memcpy性能要稍好一些。