C++ 编译器何时优化C/C++;来源?

C++ 编译器何时优化C/C++;来源?,c++,c,assembly,optimization,inline-assembly,x86,C++,C,Assembly,Optimization,Inline Assembly,X86,大多数编译器不优化内联汇编代码(,),它允许我们编写它不支持的新指令 但是C/C++编译器什么时候应该实现内联程序集优化?通常,编译器不会优化内联程序集的内容。也就是说,它们不会删除或更改组装块中的指令。特别是,gcc只需将内联程序集的主体原封不动地传递给底层汇编程序(gas) 但是,好的编译器可能会围绕内联程序集进行优化,在某些情况下甚至可能完全忽略内联程序集代码的执行!例如,如果Gcc确定程序集的已声明输出无效,则可以执行此操作。它还可以将部件块从循环中提升出来,或者将多个调用组合成一个调用

大多数编译器不优化内联汇编代码(,),它允许我们编写它不支持的新指令


但是C/C++编译器什么时候应该实现内联程序集优化?

通常,编译器不会优化内联程序集的内容。也就是说,它们不会删除或更改组装块中的指令。特别是,
gcc
只需将内联程序集的主体原封不动地传递给底层汇编程序(
gas

但是,好的编译器可能会围绕内联程序集进行优化,在某些情况下甚至可能完全忽略内联程序集代码的执行!例如,如果Gcc确定程序集的已声明输出无效,则可以执行此操作。它还可以将部件块从循环中提升出来,或者将多个调用组合成一个调用。因此,它永远不会干扰块内的指令,但更改块的执行次数是完全合理的。当然,如果块具有其他一些重要的副作用,也可以禁用此行为


扩展asm语法上的gcc文档包含了所有这些内容。

通常,编译器不会优化内联程序集的内容。也就是说,它们不会删除或更改组装块中的指令。特别是,
gcc
只需将内联程序集的主体原封不动地传递给底层汇编程序(
gas

但是,好的编译器可能会围绕内联程序集进行优化,在某些情况下甚至可能完全忽略内联程序集代码的执行!例如,如果Gcc确定程序集的已声明输出无效,则可以执行此操作。它还可以将部件块从循环中提升出来,或者将多个调用组合成一个调用。因此,它永远不会干扰块内的指令,但更改块的执行次数是完全合理的。当然,如果块具有其他一些重要的副作用,也可以禁用此行为


扩展asm语法上的gcc文档包含了所有这些内容。

从不。这将挫败内联汇编的目的,内联汇编的目的是完全满足您的要求

如果希望以编译器能够理解和优化的方式使用目标CPU指令集的全部功能,则应使用内部函数,而不是内联asm

e、 g.使用
int count=\uuu内置\uu popcount(x),而不是用于的内联asm(在用
-mpopnt
编译的GNU C中)。内联asm也是特定于编译器的,因此如果有什么内部函数更具可移植性,特别是如果您使用Intel的x86内部函数,则所有主要的针对x86的编译器都支持该内部函数。使用
#include
,您可以使用
int\u popcnt32(int a)
可靠地获取
popcnt
x86指令。请参见标记wiki中的、和其他链接


:

这是内联asm击败常数传播的一个经典示例,如果可以避免,为什么不应该使用它来提高性能:


这是我用于此测试的内联asm定义:

int popc_asm(int x) {
  // force use of the same register because popcnt has a false dependency on its output, on Intel hardware
  // this is just a toy example, though, and also demonstrates how non-optimal constraints can lead to worse code
  asm("popcnt %0,%0" : "+r"(x));
  return x;
}
如果您不知道这一点,这也是您应该尽可能将其留给编译器处理的另一个原因



使用编译器不知道的特殊指令是内联asm的一个用例,但是如果编译器不知道,它肯定无法优化它。在编译器擅长优化内部函数(例如SIMD指令)之前,这种类型的内联asm更为常见。但我们现在已经有很多年了,而且编译器通常都很擅长使用内部函数,即使对于ARM这样的非x86体系结构也是如此。

永远不会。这将挫败内联汇编的目的,内联汇编的目的是完全满足您的要求

如果希望以编译器能够理解和优化的方式使用目标CPU指令集的全部功能,则应使用内部函数,而不是内联asm

e、 g.使用
int count=\uuu内置\uu popcount(x),而不是用于的内联asm(在用
-mpopnt
编译的GNU C中)。内联asm也是特定于编译器的,因此如果有什么内部函数更具可移植性,特别是如果您使用Intel的x86内部函数,则所有主要的针对x86的编译器都支持该内部函数。使用
#include
,您可以使用
int\u popcnt32(int a)
可靠地获取
popcnt
x86指令。请参见标记wiki中的、和其他链接


:

这是内联asm击败常数传播的一个经典示例,如果可以避免,为什么不应该使用它来提高性能:


这是我用于此测试的内联asm定义:

int popc_asm(int x) {
  // force use of the same register because popcnt has a false dependency on its output, on Intel hardware
  // this is just a toy example, though, and also demonstrates how non-optimal constraints can lead to worse code
  asm("popcnt %0,%0" : "+r"(x));
  return x;
}
如果您不知道这一点,这也是您应该尽可能将其留给编译器处理的另一个原因



使用编译器不知道的特殊指令是内联asm的一个用例,但是如果编译器不知道,它肯定无法优化它。在编译器擅长优化内部函数(例如SIMD指令)之前,这种类型的内联asm更为常见。但我们现在已经有很多年了,编译器通常都很擅长使用内部函数,即使对于ARM等非x86体系结构也是如此。

当您请求全局优化时,它可能会这样做。g++和MSVC都支持全局优化。我希望永远不会!如果您非常关心内联组装,那么您可能不希望它被弄乱。编译器如何知道对0xbeefface看似无用的写入对某些嵌入式设备并不重要?当您使用内联汇编时,您基本上是在告诉编译器您知道自己在做什么,这将比它能做的更好。为什么编译器要尝试优化它?如果你想要
    xor     eax, eax
    popcnt  eax, eax
    mov     ecx, 1
    popcnt  ecx, ecx
    add     ecx, eax
    mov     edx, 2
    popcnt  edx, edx
    add     edx, ecx
    mov     eax, 3
    popcnt  eax, eax
    add     eax, edx
    ret
int popc_asm(int x) {
  // force use of the same register because popcnt has a false dependency on its output, on Intel hardware
  // this is just a toy example, though, and also demonstrates how non-optimal constraints can lead to worse code
  asm("popcnt %0,%0" : "+r"(x));
  return x;
}