「ループは -1 まで回せ」

元の話題は int vs size_t problem のあたり。符号なし型の減算ループをどう書くかという話。

実は、一定数までカウントアップするよりも 0 を通り過ぎるまでカウントダウンする方が速度とコードサイズの両面で良い、ってのは最適化の定石だと思ってました。特にアセンブリレベルでは。

自分が使ってた 68000 だと、ずばり、「レジスタの値をデクリメントして -1 じゃなければジャンプ」という DBRA 命令がある (しかも速い) し、x86 でも、

loop:
  ...
  subl $1, %esi
  jns  loop

みたいな形で、カウンタが符号なし型であっても高速なループが書けるんじゃないかと。

でもそういえば Metrowerks のコンパイラはこの最適化をしてくれなかったような気がするけど GCC (4.0.1 (Apple Inc. build 5465)) だとどうなんだろというわけで試してみた。

結論: 最適化してくれない orz

size_t i;
while (i--)
while (--i != (size_t)-1)

の両者がいずれも、

      movl $-1, %edi
loop: ...
      cmpl %edi, %esi
      jne  loop

みたいなコードになる。しかも、勢いあまって、

if (i != 0) do {
  --i;
  ...
} while (i != 0);

と書いたら、0 から一定数までカウントアップするループに変換される。

      movl %eax, %esi
      xorl %edi, %edi
      movl %eax, -32(%ebp)
loop: decl %esi
      incl %edi
      ...
      cmpl %edi, -28(%ebp)
      jne  loop

うーむ。

実行速度的な考察は、別途必要かなと思いますが、コード密度的にはもうひとがんばりしてほしい感じ。

10:21 追記: decl だとキャリーフラグ立たないから subl 使うべきと光成さんに教えてもらって直した。