为什么turbofan会剥离小循环?

问题描述 投票:1回答:1

当编译一个循环时,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源码的评论只是说这是一个优化而已 但除了膨胀代码大小之外,它实际上能达到什么目的呢?

我知道剥离如果引入新的不变量会有好处。

assembly optimization x86-64 v8 micro-optimization
1个回答
2
投票

好像剥皮第一次迭代是turbofan中提升循环语句的要求。

编译器技术负责人的这个介绍 似乎表明,剥皮是一种更安全地吊装代码的新方式。幻灯片17:

环路剥离 没有更多的非优化循环,因为积极的举起

而事实上,打败剥皮步骤对性能的影响很大。这个。

// 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 留在内环中。

© www.soinside.com 2019 - 2024. All rights reserved.