C语言:任务分配、二进制运算等性能

5

我听说C语言的性能有很多方面需要注意:强制转换操作的速度较慢,函数调用也比较慢,二进制操作比普通操作要快得多,等等...

我知道这些东西可能跟硬件平台有关,而且编译器的优化也会产生很大影响,但我希望能够查看一份图表,以了解怎样才能编写高性能程序时应该注意什么,应该避免什么。是否存在这样一份图表(或者网站、书籍、其他资源)?


8
在C语言中,“casting”在运行时是一个零时间操作,它完全发生在编译时。同样,在高级语言中,函数调用的速度比在C语言中快不了多少,基本上只需要“将返回值推送到堆栈上,将0~n个参数推送到堆栈上,执行跳转”。你从哪里获取这些“真相”?因为我建议找另一个来源。:-) 注:文字中的斜体部分是对原文的强调,并非需要翻译的内容。 - T.J. Crowder
2
强制类型转换慢吗?在C语言中?它甚至在运行时都不存在。 - President James K. Polk
2
@T.J. 有些强制类型转换是不免费的。例如,将 char 转换为 double。但仍然非常便宜。 - Donal Fellows
2
@T.J.:仍然……确实如此。而且你是对的,“真相”应该被留在它们被发现的石头下面。 :-) - Donal Fellows
这种思维方式必须被消除。+1 为提供纠正错误信息的机会 :) - Norman Ramsey
1
在一般情况下,强制类型转换并不完全在编译时完成。强制类型转换是从一种类型到另一种类型的转换,这种转换可能是零成本的(例如,在二进制补码体系结构上将int转换为unsigned int),也可能是廉价的(例如,将signed char符号扩展为int),或者是昂贵的(例如,将float转换为int)。 - caf
6个回答

10

基本上,没有这样的“技巧和窍门”书籍从语法层面来说,因为没有确凿的保证你所述的任何内容是正确的(实际上,大多数都是错误的)。

一般来说,性能优化应该更注重算法,其次是内存局部性和缓存优化。最好的工具是分析器(oprofile,valgrind,cachegrind等),其次是对机器体系结构的理解(指令组合是否次优、对齐限制、内存层次结构和大小)以及针对您的CPU的汇编语言(以捕获不太理想的内部循环问题)。

如果您对英特尔架构(以及所有兼容英特尔的CPU)上的微观优化感兴趣,则必须阅读此PDF文档。在Agner的网站上还有更多有趣的指南。


1
在优化方面,算法排在第一位是最重要的。即使每个步骤都进行了微观优化,一个糟糕的算法也不会变得更快。 - Anders Abel

9

我感觉你对这些很困惑。让我们解决一些你提出的谬论。

强制类型转换比普通赋值慢。

这实际上取决于你要进行的强制类型转换。对于不同的地址类型,强制类型转换其实是免费的,因为你只是对同一个值应用了不同的解释。将不同宽度的数值类型进行强制类型转换可能会慢一些(有时隐式地在赋值时完成),但仍然非常快。

函数调用很慢。

不完全正确。虽然函数调用不是免费的,但成本并不高,所以除非你有证据表明他们确实会影响性能,否则不应该避免使用函数调用。永远不要没有充分的理由就进行优化,并且证明优化可以帮助程序的性能(如果反复尝试优化而没有得到我想要的性能平衡,我会撤回此类优化)。

二元运算比普通运算快。

什么是“普通运算”?值得一提的是,加法和乘法都是二元运算。在现代硬件上,它们都非常快,因此应该让编译器自由处理这些运算。更重要的是,正确描述你在做什么。

现在,对于真正消耗性能的问题:

  • I/O(输入输出)。
  • 内存分配。
  • 内存拷贝。
  • 深度嵌套(或非常长的)循环。

关注这些问题; 它们通常是软件变慢的地方。并且始终选择好的算法和数据结构。


非常好的回答。太棒了。虽然堆内存分配与I/O不在同一类别,甚至不接近。堆非常快。不像转换那么快,但很快——当然,直到你的内存不足为止。 :-) - T.J. Crowder
@T.J. I/O是最慢的,通常也是最难摆脱的;你通常会因为某种很好的理由而执行read()操作。堆分配速度更快,但你通常需要进行更多的分配;不要害怕使用堆分配,但它绝对不是免费的。 - Donal Fellows
1
一个经常不必要地昂贵的领域是字符串处理。糟糕的程序员通常在这个领域编写灾难性的糟糕代码,因为他们没有很好地掌握正在进行的内存复制和循环。 - Donal Fellows
你能解释一下你所说的“内存拷贝”是什么意思吗?是变量赋值吗?是memcpy函数吗?还是将一个值移动到另一个位置?还是以上所有情况都包括在内? - Aaron H.
@Aaron:所有的都可能被暗示,但大多数分配都是基本类型,所以不是问题。 - Donal Fellows

2
从前有一本名为“高效C”的书。稍后又出现了一本名为“高效C/C++编程:更小、更快、更好”的书。最近还有一本名为“高效C ++”的书。
所有这些都涵盖了许多似乎你感兴趣的内容。前两者似乎已经绝版,第三本可能也应该如此。要保持正确和有意义,这样的图表可能需要每月更新一次。几乎你想到的任何关于这方面的东西都可能是错误的,并且很少有正确的东西也很快就会变成错误。
例如,您仍然经常看到建议,如果您关心性能,则应避免使用浮点数。曾经,这甚至是合理的-但现在,一些CPU实际上通过将整数转换为浮点数进行整数运算,然后将结果转换回整数!全程使用浮点数可以提高速度。

1

基本上,你提到的所有操作都非常非常快。除非你每秒钟要执行数百万次,否则不要过于担心替代方案之间的微小差异。

如果你的程序中有一个时间关键部分运行得太慢,那就对它进行分析,找出具体花费时间的地方,并确定哪些地方需要优化。


1

你从哪里听到这些事情的??在这个领域中所有“病毒式传播”的神话中,这可能是我听过的最惊人的一个。

C语言是最接近机器语言的高级语言。

其他答案都是正确的。

我只想补充一点,在实际软件(而不是小型两页程序)中,过度的通用性、过度抽象化、用大炮打蚊子,是导致性能差的压倒性原因,尽管每个程序员都认为自己的解决方案“简单”。


1
我听说过很多关于C语言性能的事情...
有人给你一些非常奇怪的想法。我特别喜欢“二进制”和“正常”操作之间的区别。我认为对于计算机来说,二进制是正常的。有人需要向我解释这个区别。
我提供下面的图表,帮助你了解如何编写高性能程序。它假定你已经了解了Kernighan和Ritchie的C语言水平,这是关于C语言的经典教材,也是你唯一需要的C语言书籍(尽管其他书籍也有用)。
    Have you read Jon Bentley's book "Programming Pearls"?  --no--> read it
             |
             | yes
             V
    Have you read Peter van der Linden's book 
    "Expert C Programming: Deep C Secrets"?                 --no--> read it
             |
             | yes
             V
    Have you learned how to use valgrind --tool=callgrind
    and the kcachegrind visualizer?                         --no--> learn them
             |
             | yes
             V
    Congratulations!  You are now equipped to write 
    reasonably efficient C programs.

本书中大部分主题,特别是算法,值得在其他地方深入探究。但这张图表将是一个无痛且有趣的入门方式

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