首页 > 解决方案 > 为 Python 程序员优化编译器心态

问题描述

我主要来自 Python 背景,现在正在学习 C 和 x86-64 汇编。我之前通过 Cython 间接使用了 C,但现在除了汇编之外,我正在学习 C。

我的基本问题是在优化编译器时我应该把自己放在什么样的心态上。我应该让编译器完成它的工作,但是一旦我足够精通汇编,就开始检查并确认汇编输出吗?这就是想要编写高性能代码的负责任的 C 程序员所做的吗?

这个问题被触发是因为我想检查什么gcc 7.5.0可以优化下面的代码。特别是,我跑去objdump了解如何在不同级别上优化在同一索引处访问两次数组。

我的问题不是什么时候应该使用这些级别。我只是问更有经验的程序员,如果这就是你所做的,运行高度优化的代码并检查汇编输出以确保一切都符合预期?对于想要真正了解编译器生成什么机器代码的人来说,这是自然的工作流程吗?

我知道下面的示例是一种微不足道的优化机会,但您是否刚刚了解到某些优化肯定会发生并且您不再考虑它们?关于可以进行什么样的转换和优化的信息并不多,更不用说编译器没有留下任何注释或消息让程序员了解优化的内容和原因,所以我无法想象除了简单的其他方式在实践中学习这一切。谢谢。

#include <stddef.h>
#include <stdio.h>

int main(int argc, char ** argv)
{
    size_t len_messages = 9;
    int messages[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

    for(size_t idx=0; idx < len_messages; idx++) {
        printf("Accessing here %d and there %d\n", messages[idx], messages[idx]);
    }

    return 0;
}

标签: ccompiler-optimization

解决方案


我的基本问题是在优化编译器时我应该把自己放在什么样的心态上。我应该让编译器完成它的工作,但是一旦我足够精通汇编,就开始检查并确认汇编输出吗?

大多数情况下没有。

不同的代码段对性能的影响程度不同——在初始化期间只使用一次的代码段不会对性能产生太大影响,而在循环中间频繁执行的一段代码可能会对性能产生极大的影响。通过组装进行优化会花费开发人员的时间和便携性;并且这些额外成本通常不能通过不经常执行的代码的可忽略的性能改进来证明是合理的。

出于这个原因,主要策略是使用分析器来确定最重要(对于性能)的代码片段在哪里;并仅调查这些部分的性能改进。

然而,“调查性能改进”并不一定意味着直接进行组装。您考虑改进算法、改进数据结构和缓存局部性、改进并行性(“更多线程!”)等。

毕竟,您可能会查看编译器生成的程序集,看看您是否可以找到一种方法来手动改进/优化它。你也可能不会。

您仍然可能不使用汇编语言的原因是不同的 CPU 是不同的。您可以针对一个 CPU(无论您的计算机有什么)进行优化,并使软件在其他 CPU 上显着变慢(无论运行您的软件的最终用户有什么);或者您可以依赖可能不存在的功能(例如 AVX512)。当然,这也意味着您从分析中获得的结果并不像您想象的那么有用(对于粗略估计来说已经足够了,而且永远不能用作适用于所有 CPU 的准确表示)。

为了解决这个问题,您可能需要针对不同 CPU 使用多种不同版本的汇编语言 - 一种用于“64 位 Intel with AVX-512”,一种用于“64-bit Intel with AVX2”,一种用于“64-bit Intel with AVX2”,一种用于“64-bit Intel without any AVX”,AMD 的另外 2 个版本,因为您发现一些指令在 AMD 上花费的时间更长,而其他一些指令在 AMD 上更快;然后是 64 位 ARM 的另一个不同版本的集合,然后是 PowerPC,然后......

基本上; 在装配中进行优化是很少见的。对于一个“重磅炸弹”的库(例如 MPEG 解码器、大数字库……),它可能很有意义,而对于大型程序的一些性能关键部分,它可能是合理的;但除此之外,你的时间可能还有更重要的事情要做。


推荐阅读