私には、これにはコンパイラに代わって通常よりもスマートさが必要になるように思われます。最初に反復子がランダムアクセス反復子であると推測し、次にループが実行される回数を把握します。
完全にテンプレートで構成される STL には、すべてのコードが含まれていますinline
。そのため、ランダム アクセス反復子は、コンパイラが最適化の適用を開始した時点で既にポインターに縮小されています。STL が作成された理由の 1 つは、プログラマーがコンパイラーの裏をかく必要が少なくなるようにするためでした。そうでないことが証明されるまでは、正しいことを行うために STL に依存する必要があります。
もちろん、使用する STL から適切なツールを選択するのはあなた次第です...
編集:g++
ループ展開を行うかどうかについての議論がありました。私が使用しているバージョンでは、ループ展開は-O
、-O2
、またはの一部ではありません-O3
。次のコードを使用して、後者の 2 つのレベルで同一のアセンブリを取得します。
void foo (std::vector<int> &v) {
volatile int c = 0;
const std::vector<int>::const_iterator end = v.end();
for (std::vector<int>::iterator i = v.begin(); i != end; ++i) {
*i = c++;
}
}
対応するアセンブリ-O2
アセンブリ:
_Z3fooRSt6vectorIiSaIiEE:
.LFB435:
movq 8(%rdi), %rcx
movq (%rdi), %rax
movl $0, -4(%rsp)
cmpq %rax, %rcx
je .L4
.p2align 4,,10
.p2align 3
.L3:
movl -4(%rsp), %edx
movl %edx, (%rax)
addq $4, %rax
addl $1, %edx
cmpq %rax, %rcx
movl %edx, -4(%rsp)
jne .L3
.L4:
rep
ret
オプションを追加する-funroll-loops
と、関数ははるかに大きなものに拡張されます。ただし、ドキュメントではこのオプションについて警告しています。
コンパイル時またはループの開始時に反復回数を決定できるループを展開します。-funroll-loops は、-frerun-cse-after-loop を意味します。また、完全なループ ピーリングを有効にします (つまり、一定回数の繰り返しでループを完全に削除します)。このオプションはコードを大きくしますが、実行が速くなる場合とそうでない場合があります。
ループを自分で展開することを思いとどまらせるためのさらなる議論として、上記の関数にDuff's Deviceを適用する図でこの回答を締めくくります。foo
void foo_duff (std::vector<int> &v) {
volatile int c = 0;
const std::vector<int>::const_iterator end = v.end();
std::vector<int>::iterator i = v.begin();
switch ((end - i) % 4) do {
case 0: *i++ = c++;
case 3: *i++ = c++;
case 2: *i++ = c++;
case 1: *i++ = c++;
} while (i != end);
}
GCC には別のループ最適化フラグがあります。
-ftree-loop-optimize
ツリーでループの最適化を実行します。このフラグは、以降でデフォルトで有効になっています-O
。
そのため、この-O
オプションは、反復回数が固定されたループの完全なループ展開 (ピーリング) を含む、最も内側のループの単純なループ最適化を有効にします。(これを指摘してくれたdocに感謝します。)