【问题标题】:How to write more efficient code如何编写更高效的代码
【发布时间】:2011-04-04 22:30:36
【问题描述】:

世纪问题?我基本上想知道如果我将这段代码写成几个不同的变量或者我使用小数组,哪个会更有效。

int x = 34;
int y = 28;
int z = 293;

double coordinate[3] = {34, 28, 293};

我有一个坐标结构,我将按以下方式使用它:

typedef struct coordinates_t {
    double x = 0.0;
    double y = 0.0;
    double z = 0.0;

} coordinates;


typedef struct car_t {
    coordinates start; // car starting point
    coordinates location; // car current Location
    coordinates targCarVector; // Vector to car from target
    coordinates altitude; // Altitude of car
    coordinates distance; // Distance from car start to current position
} car;

我需要做以下事情:

distance = car1.location - car1.start;

如果我不使用数组,我将不得不有很多代码行,但如果我使用数组,我将不得不使用循环。数组和循环是否更占用内存/cpu?我基本上是想看看哪种方法是编写此代码的最有效方式。

谢谢, 半羊

【问题讨论】:

  • Benchmarks benchmarks 基准测试!
  • @Daenyth - 准点!或者,在你拥有你的应用程序之前,甚至不要打扰基准测试。写和“它很慢” - 然后优化发生缓慢的几个地方并完成。通常不必过多担心前期的效率——您很少能预料到程序中真正缓慢的部分会在哪里——也不值得在很多时候额外考虑。
  • 我同意 Benchmarks 的评论,但同时,我对此事的看法是变数。最好有一些东西来说明它被抓住了什么。例如,您的坐标[] 数组可能是 x、y、z,但其他人可能认为它们是 z、y、x。有变量 x、y 和 z,就不会有混淆。
  • 我的怀疑是,由于结构在内部(或多或少)只是数组,因此在进入中间表示后不会产生最微弱的区别。
  • 上个世纪的问题。

标签: c performance premature-optimization


【解决方案1】:

效率比可维护性和可读性更重要吗?答案是不。即使您有一个时间紧迫的应用程序,您也会将 90% 的时间花在不到 10% 的代码上,因此只有这 10% 的代码需要尽可能高效地进行编码。

如果您没有测量并发现哪 10% 是罪魁祸首,那么您几乎可以肯定会优化一开始不会占用太多运行时间的代码。这是浪费时间。

【讨论】:

  • 我们应该忘记小的效率,比如说大约 97% 的时间:过早的优化是万恶之源。然而,我们不应该放弃关键的 3% 的机会。一个好的程序员不会因为这样的推理而自满,他会明智地仔细查看关键代码;但只有在识别出该代码之后 -- Donald Knuth
  • @Paul Nathan:大师总是说得比我好。
  • @Paul:你或多或少是对的,但这些想法让我抓狂,欢快的格言大约占 10% 和 3%。似乎这就是人们所说的,因为他们已经听到其他人这么说。也许这在调用堆栈永远不会超过 2 的小 1 页学术算法中是有意义的。在现实世界的巨型班轮中,超过 90% 的时间(根据我的经验)问题是 1 行函数调用站点由于过于笼统的数据结构设计,不必要地调用大量的调用树干。
  • @Mike:这是数据结构和算法设计的问题,而不是像这个问题所说的代码优化问题。 (我敢肯定 Knuth 是凭空挑选了 97% 的数字——实际数字并不重要。而且我敢肯定 Knuth 知道大型班轮。)
  • 我把我的一些经验性能调整放在了这个答案中。 (stackoverflow.com/questions/926266/…) 这和大多数人对优化的理解很不一样。
【解决方案2】:

与往常一样,您需要对代码进行概要分析才能确定。

话虽如此,我建议使用数组和循环 - 你的代码应该更简洁/可维护,编译器应该能够很好地优化/展开所有小的常量大小循环如果您对每个向量都使用 x,y,z 坐标,这实际上就是您手动执行的操作。

在一个完全不相关的说明中,我看到你的车有高度。是飞行汽车吗?如果是这样,那么酷应用绝对是 +1。

【讨论】:

    【解决方案3】:

    使用数组,使用 -funroll-loops 编译。您可以同时获得两者的好处。

    【讨论】:

      【解决方案4】:

      您必须针对要执行此操作的每个平台进行测量。

      但是我认为这根本不会产生任何明显的差异。 (也许除了一些嵌入式平台。这是我不太了解的领域。)所以首先以最容易阅读和理解的方式编写代码。然后测量您的代码是否变慢,并使用分析器找到程序花费大量时间的确切位置。然后尝试改进这些,在每次更改后进行测量,看看它有什么效果。

      提高易于理解的代码库的速度比理解充斥着过早且不需要的“优化”的代码库要容易得多。

      可衡量的运行时改进通常来自算法更改,而不是像这样的微优化。花时间尝试寻找更好的算法。

      【讨论】:

      • "使用分析器找到程序花费过多时间的确切位置" 太多人认为这意味着“程序计数器花费过多时间的地方,因此缺少合理的函数调用和 I/O,根据我的经验,在大型软件中,这些是迄今为止占主导地位的。
      • @Mike:因此我的“可衡量的运行时改进通常来自算法更改”。
      • 问题是,当人们想到算法变化时,他们会想到搜索、排序、线性、二进制、哈希等。他们不会想到你不会想到的东西,例如:不必要的国际化在启动时从资源中加载字符常量会导致很多隐藏的 I/O,或者只是将内容加载到树控件中会导致很多涟漪效应,创建和破坏窗口和其他数据结构,或者修改网格控件中的列名称/类型会导致贯穿始终很多数据结构。这就是为什么谈论“算法”让我感到不安。
      • @Mike:是的,请参阅我对另一个答案的评论。预加载数据索引而不是加载实际数据……对我来说这是一种算法变化。
      【解决方案5】:

      如果编译器认为有帮助,他们可以“展开”循环。所以编译器可能会悄悄地替换下面的代码:

      for (i = 0; i < 3; ++i) {
          c[i] = a[i] - b[i];
      }
      

      与:

      c[0] = a[0] - b[0];
      c[1] = a[1] - b[1];
      c[2] = a[2] - b[2];
      

      考虑到所涉及的操作成本和您提供的优化标志,编译器会做出是否值得这样做的最佳猜测。

      对于“哪一个会更快?”没有简单的答案,但如果有的话,您可以确定通过最大限度的优化,您的编译器会使用它。

      【讨论】:

        【解决方案6】:

        选择更正确的方法——使用循环和数组——这两种方法都不会导致更多的内存使用(更少的使用,因为所有那些 car1、car2、car3... 指令所需的内存会更多) - 在 CPU 使用方面,您会看到最细微的差异。

        【讨论】:

          【解决方案7】:

          如有疑问,请为每个原型编写代码并对其进行分析。对于这个级别的东西,我预测性能上的任何差异都会降低噪音。使用有意义并且最清楚地传达设计意图的东西。

          按照重要性降序排列,代码必须是

          1. 正确 - 如果代码给出错误答案或执行错误操作,代码的速度无关紧要;
          2. 可维护 - 如果您无法修复或修改代码以适应新的需求,那么代码的速度就无关紧要;
          3. 健壮 - 如果代码在第一次出现不可靠的输入时发生核心转储,那么无论您的代码有多快;

          在此之后,您可能会开始担心性能问题。

          【讨论】:

            【解决方案8】:

            第一个问题是:你想优化它吗?很可能,你不想。至少如果你“总是编码,就好像最终维护你的代码的人将是一个知道你住在哪里的暴力精神病患者”。 可读性、意图清晰和可维护性始终是第一位的。

            第二个问题是:值得优化?根据 Donald Knuth 的说法,在 97% 的情况下都不是——而且你不会质疑 Knuth,是吗?另一个常见的经验法则是 80/20 规则,即 80% 的执行时间花在 20% 的代码上。 如果您要优化,首先要了解要优化的位置。如果你猜,你就错了。期间。

            第三个问题是:你能优化它吗?不,你不能,至少不是这么容易。 您认为自己比数十年来编写您的编译器的数百名程序员更聪明吗?你不是。如果你的算法和数据结构的实际实现可以被优化,你可以假设你的编译器可以自己做。编译器可以进行循环展开、指令重新排序、组合具有非重叠生命周期的变量、结构布局优化等等 - 在这个时代,它在大多数情况下甚至比大多数汇编程序员更好。即使有一点潜力,你最好专注于实现更好的算法。没有编译器可以将 O(n^2) 转换为 O(n log n),但也许是一位聪明的计算机科学家做到了,您可以实现他的算法以获得比任何微优化所能产生的更好的性能。

            【讨论】:

            • 第三个问题在许多不是 C 语言(例如 BASIC)中变得非常有争议。 :-) /迂腐
            • 是的,但你总是可以坐下来等待一个足够聪明的编译器 :) 如果一个人如此关心速度,甚至没有优化算法(众所周知,它的影响力比,好吧,micro-优化)就足够了,你应该用 C 或类似的方式重写它。
            • @delnan:从技术上讲,是的,但可能有,嗯,尖头类型的外部因素会阻止您选择其他语言。
            • “你不会质疑 Knuth 吧?”。我会说,总是质疑“权威”。就像我们其他人一样,Knuth 一次只穿一只鞋。
            • @Mike Dunlavey:嘿,我是最后一个坚持权威的人。但是某些人(例如 Knuth)赢得了他们的权威,这并不意味着您必须服从,但您应该在无视他的建议之前三思而后行。此外,这主要是个玩笑;)
            【解决方案9】:

            如果您真的想对此进行微优化,请使用 CPU 的 SIMD 指令功能。如果您使用的是 x86 平台,则可以使用 MMXSSE 指令进行矢量运算,而不是单独添加坐标的每个部分(如果没有特殊的命令行开关或内联汇编,您的编译器可能无法生成这些)。与在单个变量和数组之间切换相比,这可能意味着更大的加速。我说“可能”是因为如果不尝试两种方法并测量执行时间,就无法确定。

            【讨论】:

              【解决方案10】:

              请分析您的代码并找出导致效率低下的主要问题。效率可以通过代码的运行时执行来衡量。

              一些工具可以作为开源工具使用,例如gprof

              【讨论】:

                【解决方案11】:

                世纪的答案是

                不要本末倒置。

                换句话说,首先是配置文件。

                每个人都“知道”这一点,但是关于 SO 的一大类问题的形式是“哪个更快,X 还是 Y?”

                这会引发猜测,而当您关心性能时,猜测并不值得,因为如果您遇到性能问题,它可能完全在其他地方。

                【讨论】:

                【解决方案12】:

                我通常不担心效率...

                加快速度的一个地方是,如果我搜索一个数值 假设我想找到一个帐号“188335344”,它会比搜索字母字符快得多。在搜索非数值时,搜索必须将每行文本切换为大写。数字并非如此。

                实际上快了一点。

                任何需要用户输入的东西都可能效率极低,一点也不重要。

                我会在每次搜索结束时显示经过的时间。因此,可以将较旧的代码与较新的更改进行比较。

                【讨论】:

                  猜你喜欢
                  • 2021-05-07
                  • 2020-01-18
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-01-24
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-11-25
                  相关资源
                  最近更新 更多