C++编程效率

3

这里是我从微软开发者网络复制的一些代码
http://msdn.microsoft.com/zh-cn/library/dd162487(v=VS.85).aspx

LRESULT APIENTRY WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
{

**PAINTSTRUCT ps;  
HDC hdc;**

switch (message) 
{ 
    case WM_PAINT: 
        hdc = BeginPaint(hwnd, &ps); 
        TextOut(hdc, 0, 0, "Hello, Windows!", 15);
        EndPaint(hwnd, &ps); 
        return 0L; 

    // Process other messages.   
} 
} 

我可能错了,但我认为每当编译器执行像这样的语句之一时:
int var1  
double var2  
char var3[]  
PAINTSTRUCT ps  
HDC hdc  

计算机会创建一个新变量。至少这是逻辑上的想法,因为当你想要创建一个新变量时,这就是你写的内容,对吗?
我还一直认为,如果你有像这样的代码块:
for(int i = 0; i < 100; i++)
    int sum = i;

计算机将创建100个不同的变量,它们都具有相同的名称“sum”,并且包含在“i”中的某些值。
在上面的示例代码中,函数WndProc将在程序运行期间被调用多次,然而函数创建的两个变量“ps”和“hdc”只会在函数执行的一部分时间内使用。
那么计算机是否会创建许多单独的、多余的PAINTSTRUCT和HDC变量,而它永远不会使用它们呢? 在case WM_PAINT之后声明“ps”和“hdc”是否会更加高效?
case WM_PAINT:
{ 
     **PAINTSTRUCT ps;  
     HDC hdc;**

     hdc = BeginPaint(hwnd, &ps); 
     TextOut(hdc, 0, 0, "Hello, Windows!", 15);
     EndPaint(hwnd, &ps); 
 }
 return 0L; 

(1) 你的理解...充其量也只是过于简单化了。我希望有人能写一篇更接近现实的好文章。(2) 除非确信存在性能问题或将来会出现这样的问题,否则不要担心它——代码可维护性和实际完成工作要比性能重要得多。(有些应用程序需要如此关注性能——最好留给那些确切知道自己在做什么的人。)如果确实存在性能问题,请优化实际上有影响的地方(而这种事情是最不重要的之一)。 - user395760
5个回答

2

我可能错了,但我认为每次编译器运行像下面任何一个语句时:

int var1  
double var2  
char var3[]  
PAINTSTRUCT ps  
HDC hdc  

计算机将创建一个新变量。这些变量对应于运行时的存储空间。由于它们是本地变量,所以它们可能存在于堆栈上,并且为这些本地变量分配空间就像移动堆栈指针一样简单。这非常快速。
我也一直认为,如果您有这样的代码块: for(int i = 0; i < 100; i++) int sum = i; 计算机将创建100个不同的变量,所有变量都具有相同的名称“sum”和包含在“i”中的某个值。
在这里可能会发生的是,在循环的每次迭代中重复使用堆栈上的同一位置。
在case WM_PAINT之后声明“ps”和“hdc”是否至少略微更有效?
不,它们是本地变量,它们很可能在堆栈上分配,并且它们的空间在方法进入时独立于您在代码中声明它们的位置而被保留。
最后,请不要过于担心此处的性能,这只是微观优化。专注于代码的含义。让编译器尽可能有效地将其转换为可执行代码。只有当您遇到不能满足客户预期性能要求的性能问题时,才应开始担心。

1

你说得对,计算机将会声明很多局部变量,但幸运的是这非常高效。编译器通常在函数第一次调用时输出产生局部变量的代码作为单个汇编指令,因此创建这些变量的开销可以忽略不计。在你的循环示例中,编译器通常不会多次创建和销毁变量。相反,它只创建一次,然后在每次循环迭代中重复使用该空间。换句话说,变量在逻辑上被创建和销毁在每次迭代中,但在生成的代码中,它实际上被共享在多个迭代之间。

作为一个更普遍的规则,除非你有特定的理由认为性能低于应有的水平,否则不要担心程序的效率。你的优先级应该是编写清晰、优雅的代码,而不是一开始就优化代码,因为在实际运行程序之前,很难知道你的优化是否对程序的运行时间有用。通常你会使用分析器来确定代码中哪些地方比较慢,然后集中你的优化工作在那里。


1
对于原始本地变量 - 指针、int、double、float 等 - 以及原始数组,声明变量确实会为其“分配”空间,但实际发生的是编译器说:“我将在堆栈上调用此位置,堆栈指针下面三个字。”没有实际计算发生 - 根本不执行任何指令!因此,在循环内部或外部声明“sum”完全没有任何区别!
然而,对于本地声明的类实例,情况就不同了。声明对象意味着构造函数将在进入块时被调用,并且析构函数将在退出块时被调用,这会导致指令被执行 - 因此您需要小心处理。

1
计算机将会创建一个新的变量,这只是表面上的说法。从技术上讲,处理器实际上是将堆栈指针移动到一定长度。如果您声明的变量没有任何构造函数,那么此操作的开销几乎为零。
计算机不会创建大量单独的、多余的PAINTSTRUCT和HDC变量,因为它永远不会使用它们。一旦函数返回,为这些变量分配的所有堆栈空间都将被回收,没有任何浪费。
在case WM_PAINT之后声明"ps"和"hdc"是否更有效呢?不太可能。无论您在哪里放置它们的声明,编译器都很可能看到这些声明并在堆栈上为这些变量分配空间。同样,只要变量没有构造函数,这个说法就是正确的。如果有构造函数,那么将它们放在case中可以节省调用构造函数的时间。

0

编译器会对您的代码进行重大更改,只要它具有相同的结果。特别是在发布(非调试)版本中。

这意味着如果您有未使用的代码、过早声明变量或者有变量在循环外部没有被使用,编译器可能会将其删除或移动。

/* If you never use these, the compiler will probably throw them out */
int var1;
double var2;
char var3[];
PAINTSTRUCT ps;
HDC hdc;

/* Nothing here effects other code, so the code will probably be removed */
for(int i = 0; i < 100000; ++i)
{
    int j = i * 7;
}

除非您有非常强的性能要求,否则最好的做法是使您的代码更易读。如果变量在较小的范围内使用,请将其放置在那里,因为这样更容易阅读,而不是因为它可能会使程序更快。

计算机非常擅长运行您所讨论的类型的代码,其中您只是使用CPU和内存。您可以真正获得良好性能提升的地方是在处理相对较慢的设备(如网络或从磁盘读取)时。在这些情况下,单个字节的内存很重要。

CPU/内存优化最有意义的地方是在更复杂的算法中,或者当您处理大量数据时,例如图形或搜索算法。

这并不是说您不应该考虑优化 - 只是您应该选择自己的战斗。在大多数程序中优化代码的可读性,并在您拥有更复杂的算法或正在处理低级别图形或复杂数据结构/算法时优化性能,然后仅在测试了程序一段时间并发现它运行缓慢时再进行优化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接