【问题标题】:Pipeline optimzation, is there any point to do this?流水线优化,这样做有什么意义吗?
【发布时间】:2013-02-02 09:07:47
【问题描述】:

另一家公司的一些非常有经验的程序员告诉我一些针对特定 CPU 的低级代码优化技巧,包括管道优化,这意味着以特殊顺序排列代码(显然是内联汇编)以使其适合管道更适合目标硬件。

由于存在乱序和推测执行,我只是想知道做这种低级的事情有什么意义吗?我们主要从事高性能计算,因此我们可以真正专注于一种非常特定的 CPU 类型来进行优化,但我只是不知道是否有任何意义进行这种特定的优化,任何人都在这里有任何经验,从哪里开始?有这种优化的代码示例吗?非常感谢!

【问题讨论】:

  • 只有在您完全确定已用尽所有其他优化(锁争用、原子指令、算法复杂性等)时,这样的优化才会有用。
  • 如果目标 CPU 支持乱序指令执行,那么简短的回答几乎肯定是“否”。我认为流水线选择对移动平台很有用,因为那些通常缺乏乱序执行。 (虽然很快就会用下一代芯片改变)
  • 答案很复杂,但一般情况下是“比较找出来”。

标签: c++ optimization assembly cpu


【解决方案1】:

首先我会说编译器通常会充分优化代码(即足够好),只要您的高级代码和算法得到优化,您就不必担心这一点。一般来说,只有当您有确凿的证据表明存在可以量化和追踪的实际性能问题时,才应该进行手动优化。

现在,话虽如此,但总是可以改进一些事情——有时是一点点,有时是很多。

如果您从事高性能计算游戏,那么这种优化可能是有意义的。有各种各样的“技巧”可以做,但最好留给真正的专家,而不是胆小的人。

如果您真的想了解更多有关此主题的信息,最好从阅读Agner Fog's website 开始。

【讨论】:

  • 这个。而且通常情况下,编译器会做得更好,而不仅仅是足够。
  • 非常感谢,这正是我要找的
  • 其实通常是对的。这取决于您在哪个平台上工作。在某些平台上,编译器往往落后...
  • @Damon 我认为整个“编译器会做得更好”的说法都是垃圾。比编译器进行更好的优化并不总是微不足道的,但肯定也不是非常困难——当那是你的目标时。编译器的优势在于,它甚至可以优化那些你并不真正关心的快速运行的部分——如果你是手写的话,你可能会让它们变得有点慢且易于理解。
  • @Damon 免费交错是我们所做的标准优化。并不奇怪。 应该对有经验的汇编程序员来说是显而易见的。听起来您可能在没有考虑代码的真正目标的情况下陷入了微优化。我同意编译器生成的代码比大多数人都好。当您用完更好的算法时,或者您只是想玩得开心时,最好留给手动优化。 :-)
【解决方案2】:

管道优化将提高您的程序性能:

分支和跳转可能会迫使您的处理器重新加载指令流水线,这需要一些时间。这段时间可以专门用于数据处理指令。

一些平台无关的流水线优化方法:

  1. 减少分支数量。
  2. 使用布尔算术
  3. 设置代码以允许有条件地执行指令。
  4. 展开循环。
  5. 使循环具有短内容(可以放入处理器的缓存中 不加载)。

编辑 1:其他优化

  1. 通过消除功能和要求来减少代码。
  2. 审查和优化设计。
  3. 查看实施以提高实施效率。
  4. 仅当所有其他优化都已完成时才恢复为汇编语言 提供很少的性能改进;只优化代码 80% 的时间执行;通过分析找出。

编辑 2:数据优化

您还可以通过组织数据来提高性能。在网上搜索“数据驱动设计”或“优化性能数据”。

一个想法是,最常用的数据应该靠近在一起,并最终适合处理器的数据缓存。这将降低处理器必须重新加载其数据缓存的频率。

另一个优化是:加载数据(到寄存器中),对数据进行操作,然后将所有数据写回内存。这里的想法是在处理器处理数据(或寄存器)之前触发处理器的数据缓存加载电路。

如果可以,组织数据以适应处理器缓存的一个“行”。顺序位置比随机访问位置需要更少的时间。

【讨论】:

    【解决方案3】:

    总有一些事情“帮助”与“阻碍”管道中的执行,但对于大多数非高度专业化的通用代码,我希望编译代码的性能与最佳代码一样好对于每种型号的处理器,您无需高度专业化的代码即可获得。如果您有一个受控系统,您的所有机器都使用相同(或少数类似)处理器型号,并且您知道 99% 的时间都花在了这个特定功能上,那么优化该特定功能以提高效率。

    在您的情况下,它是 HPC,手写一些低级代码(例如矩阵乘法)以针对您正在运行的处理器进行优化可能是有益的。但是,这确实需要对处理器有一些合理的了解,因此您需要研究该处理器型号的优化指南,如果可以的话,请与以前使用过该处理器的人交谈。

    您要查看的一些内容是“注册以注册依赖项” - 您需要 c = a + b 的结果来计算 x = c + d - 因此您尝试将这些与其他一些有用的工作分开,这样 x 的计算就不会被 c = a + b 计算所阻碍。

    缓存预取和通常关心缓存的使用方式也是一件有用的事情 - 当您存储结果 1MB 数组时,不要将有用的缓存数据踢出,因为您需要 100 条指令再次使用几秒钟可能值得大量的处理器时间。

    当编译器决定在它自己的优化中对其进行洗牌时,很难(呃)控制这些东西,所以手写汇编器几乎是唯一的方法。

    【讨论】:

      猜你喜欢
      • 2020-05-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-11
      • 1970-01-01
      • 2011-02-12
      • 2016-05-26
      • 1970-01-01
      • 2014-01-05
      相关资源
      最近更新 更多