【问题标题】:C: performance of assignments, binary operations, et ceteraC:赋值、二元运算等的性能
【发布时间】:2010-05-23 07:48:46
【问题描述】:

我听说过很多关于 C 语言性能的事情;与正常分配相比,强制转换很慢,函数调用很慢,二进制操作比正常操作快得多,等等......

我确信其中一些内容是特定于架构的,编译器优化可能会产生巨大的影响,但我希望看到一个图表来大致了解我应该做什么以及应该避免写什么高性能程序。有没有这样的图表(或网站、书籍、任何东西)?

【问题讨论】:

  • "...转换很慢..." 在 C 中,转换是运行时的零时间操作。它完全在编译时发生。同样,函数调用在高级语言中的速度并不比在 C 中快得多。 字面意思只是“将返回值压入堆栈,将 0..n 参数压入堆栈,执行跳转。”你从哪里得到这些“真相”?因为我会找到另一个来源。 :-)
  • 投射很慢?在 C?它甚至在运行时都不存在。
  • @T.J.有些演员表不是免费的。例如,将 char 转换为 double。还是很便宜的。
  • @T.J.:仍然……确实。你是对的,“真相”应该被留在发现它们的岩石之下。 :-)
  • 一般情况下的转换完全在编译时完成。强制转换是从一种类型到另一种类型的转换,这种转换可以是零成本(例如,在 2s 补码架构上将 int 转换为 unsigned int)、廉价(例如,符号扩展 signed charint ) 或昂贵的(例如,将float 转换为int)。

标签: c performance


【解决方案1】:

基本上没有。从语法级别来看,没有这样的“技巧和窍门”书,因为无法保证您所说的任何内容都是真实的(事实上,大部分都是错误的)。

一般来说,性能调优应该更多地关注算法,其次是内存局部性和缓存优化。您将拥有的最佳工具是分析器(oprofile、valgrind、cachegrind 等),然后了解机器架构(次优的指令组合、对齐限制、内存层次结构和大小)和 CPU 的汇编语言(捕获少于最优内循环问题)。

如果您对英特尔架构(以及所有与英特尔兼容的 CPU)的微优化感兴趣,请查看is a must read (PDF)。 Agner's website.有更多有趣的指南

【讨论】:

  • +1 用于优化顺序 - 将算法放在首位。一个糟糕的算法即使在每一步都进行了微优化,也不会很快。
【解决方案2】:

在我看来,您对这一切感到非常困惑。让我们来解决您提出的一些神话。

与正常分配相比,铸造速度较慢。

这实际上取决于您要投射的内容。不同地址类型之间,没有;转换实际上是 free 那里的,因为您只是对相同的值应用不同的解释。不同宽度的数字类型之间的转换可能会慢一些(有时在赋值时隐式完成)但仍然非常快。

函数调用很慢。

不是真的。它们不是免费的,但成本还不够高,您应该避免使用它们除非您的分析数据另有说明。永远不要在没有充分理由的情况下进行优化并且证明它会有所帮助。 (据记录,我曾尝试过恢复未达到我想要的性能提升平衡的优化。)

二元运算比普通运算快。

什么是“正常操作”? FWIW,加法是二元运算。乘法也是如此。在现代硬件上,它们都非常快。让编译器担心这一点。更重要的是你要专注于描述你在做什么正确。

现在,对于那些真正花费的东西:

  • I/O。
  • 内存分配。
  • 内存副本。
  • 深度嵌套(或非常长)的循环。

留意那些;它们是软件通常变慢的地方。并且总是选择好的算法和数据结构。

【讨论】:

  • 优秀的答案。出色的。虽然,堆内存分配与 I/O 不在同一类,甚至不接近。堆非常快。不像演员那样快,而是快——当然,直到你跑得低。 :-)
  • @T.J. I/O 是最慢的,通常也是最难真正摆脱的;你通常这样做read() 是有充分理由的。堆分配要快得多,但您通常会做更多的事情;不用害怕,但绝对不是免费的。
  • 还有一个经常不必要地昂贵的领域是字符串处理。糟糕的编码人员经常在该领域编写灾难性的糟糕代码,因为他们没有很好地处理正在发生的内存副本和循环。
  • 你能解释一下你所说的“内存拷贝”是什么意思吗?变量赋值?内存?将一个值转移到另一个值?以上都是?
  • @Aaron:所有都可能隐含,但大多数分配都是元素类型,因此不是问题。
【解决方案3】:

从前有一本书叫Efficient C。不久之后,有一本书名为Efficient C/C++ Programming: Smaller, Faster, Better。最近,还有一个名为 Efficient C++

所有这些都涵盖了许多您似乎感兴趣的事物。前两个似乎已绝版,第三个可能应该是。为了保持正确和有意义,这样的图表可能需要大约每月更新一次。几乎所有你认为你在这种情况下的东西一开始都可能是错误的,而正确的一点可能很快就会变成错误的。

例如,您仍然经常看到一些建议,即如果您关心性能,则应避免使用浮点数。曾经,这甚至是合理的——但现在,一些 CPU 实际上通过将整数转换为浮点数、进行数学运算,然后将结果转换回整数来进行整数数学运算!在整个过程中使用浮点可以提高速度。

【讨论】:

    【解决方案4】:

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

    如果您的程序中有一个时间关键部分运行得太慢,请对其进行分析以找出确切的时间花费在哪里以及哪些地方需要优化。

    【讨论】:

      【解决方案5】:

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

      C 尽可能接近机器语言,并且仍然是一种独立于机器的“高级”语言。

      其他答案都是对的。

      我只想补充一点,在真正的软件(不是只有两页的小程序)中,过度的通用性、过度抽象、用火箭筒杀死苍蝇是导致性能低下的主要原因,即使每个最后的程序员都考虑他/她的解决方案“简单”。

      【讨论】:

        【解决方案6】:

        我听说过很多关于 C 语言性能的事情...

        有人给了你一些非常奇怪的想法。我特别喜欢“二进制”和“正常”操作之间的区别。我认为对于计算机而言,二进制 是正常的。必须有人向我解释这种区别。

        我想看一个图表来大致了解我应该做什么以及应该避免什么来编写高性能程序。

        我为您提供下面的图表。它假定您已经了解了 Kernighan 和 Ritchie 级别的 C 语言,这是 C 的经典教科书,也是您唯一需要的 C 书籍(尽管其他书籍很有用)。 p>

            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.
        

        Bentley 书中的大多数主题,尤其是算法,都值得在其他地方进行更深入的研究。但此图表将是一种轻松且有趣的入门方式

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-07-08
          • 2013-10-26
          • 1970-01-01
          • 2011-07-29
          • 1970-01-01
          • 2012-11-02
          • 2019-08-09
          相关资源
          最近更新 更多