assembly - 涡轮风扇发动机为什么会剥落小环?

标签 assembly optimization x86-64 v8 micro-optimization

在编译循环时,turbofan 似乎大多数时候都会剥离第一个循环迭代。例如一个循环:

function fill256(int32Array) {
  var i = 255;
  do {
    int32Array[i] = 0;
  } while(--i >= 0);
}

针对机器代码进行优化,如下所示:

# rdx is int32Array
0x13f38bcef7a    5a  488b4a2f       REX.W movq rcx,[rdx+0x2f]
0x13f38bcef7e    5e  488b7a3f       REX.W movq rdi,[rdx+0x3f]
0x13f38bcef82    62  4c8b4237       REX.W movq r8,[rdx+0x37]
# peeled iteration
0x13f38bcef86    66  4881f9ff000000 REX.W cmpq rcx,0xff
0x13f38bcef8d    6d  0f8614010000   jna 0x13f38bcf0a7  <+0x187>
0x13f38bcef93    73  4e8d0c07       REX.W leaq r9,[rdi+r8*1]
0x13f38bcef97    77  41c781fc03000000000000 movl [r9+0x3fc],0x0  # dword store
0x13f38bcefa2    82  41b9fe000000   movl r9,0xfe
0x13f38bcefa8    88  e906000000     jmp 0x13f38bcefb3  <+0x93>
0x13f38bcefad    8d  0f1f00         nop

# loop proper
0x13f38bcefb0    90  4d8bcb         REX.W movq r9,r11
 # first iteration entry point:
0x13f38bcefb3    93  493b65e0       REX.W cmpq rsp,[r13-0x20] (external value (StackGuard::address_of_jslimit()))
0x13f38bcefb7    97  0f868b000000   jna 0x13f38bcf048  <+0x128>
0x13f38bcefbd    9d  458d59ff       leal r11,[r9-0x1]
0x13f38bcefc1    a1  4d63e1         REX.W movsxlq r12,r9
0x13f38bcefc4    a4  4c3be1         REX.W cmpq r12,rcx
0x13f38bcefc7    a7  0f83e6000000   jnc 0x13f38bcf0b3  <+0x193>
0x13f38bcefcd    ad  4e8d0c07       REX.W leaq r9,[rdi+r8*1]
0x13f38bcefd1    b1  43c704a100000000 movl [r9+r12*4],0x0       # dword store
0x13f38bcefd9    b9  4183fb00       cmpl r11,0x0
0x13f38bcefdd    bd  7dd1           jge 0x13f38bcefb0  <+0x90>

这并不是特定于特定的循环结构,但似乎是针对所有具有小体的循环进行的。 V8 source code comments just say this is an optimization但除了增加代码大小之外,它实际上还能完成什么?

我知道如果剥离引入了新的不变量,它会是有益的。

最佳答案

似乎在涡轮风扇中剥离第一次迭代是提升循环语句的要求。

This presentation by the compiler tech lead似乎表明剥离是一种更安全地提升代码的新方法。幻灯片 17:

Loop peeling: No more deoptimization loops because of aggressive hoisting

事实上,击败剥离步骤会对性能产生巨大影响。这:

// var buf = new Int32Array(10000);
for (var i = 0; i < 10; ++i) {
  if (i === 0) continue;
  for (var j = 0; j < 1000; ++j) {
    if (j === 0) continue;
    buf[i*j] = i ^ j;
  }
}

比这个慢三倍:

for (var i = 0; i < 10; ++i)
  for (var j = 0; j < 1000; ++j)
    buf[i*j] = i ^ j;

因为对 buf 的类型和范围检查仍保留在内部循环中。

关于assembly - 涡轮风扇发动机为什么会剥落小环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61753480/

相关文章:

c - 为什么gcc将8字节格式的char类型传递给函数汇编

Microsoft Windows 中的 CPU 设备驱动程序

assembly - W 汇编中的寄存器 未定义? - 微芯片 pic16f84a

没有内容的 Wordpress 获取帖子(以减少开销)

c - 拦截64位Linux内核函数: Length of a function pointer at 32/64-bit?

linux - x86-64 位汇编 Linux 输入

assembly - 将基于索引模式转换为间接寻址模式(x86 汇编)

linux - 将 Linux x86-64 程序集 hello world 程序与 ld 链接失败

algorithm - 仿真和 Ad-Hoc 优化算法

c - 如何在不指定 -O1 的情况下获得 gcc -O1 优化