コンパイラが何もせず、ループを排除しないループに対して礼儀正しく見えるのはなぜですか?
C規格では、ループに時間がかかる必要がありますか?
例、次のコード:
void foo(void) {
while(1) {
for(int k = 0; k < 1000000000; ++k);
printf("Foo\n");
}
}
これよりも実行が遅くなります:
void foo(void) {
while(1) {
for(int k = 0; k < 1000; ++k);
printf("Foo\n");
}
}
でもで -O3
最適化レベル。許可されている空のループを削除すると、両方のコードで同じ速度が得られると思います。
「費やした時間」は、コンパイラーが保持する必要のある副作用ですか?
いいえ、費やした時間は、as-ifルールによって保護される観察可能な動作としてカウントされません。
[C++14: 1.8/5]:
整形式プログラムを実行する適合実装は、同じプログラムと同じ入力を持つ抽象マシンの対応するインスタンスの可能な実行の1つと同じ観察可能な動作を生成するものとします。ただし、このような実行には未定義の操作が含まれているため、この国際標準では、その入力を使用してそのプログラムを実行する実装に要件はありません(最初の未定義の操作に先行する操作に関しても)。
[C++14: 1.5/8]:
準拠する実装の最小要件は次のとおりです。
- 揮発性オブジェクトへのアクセスは、抽象マシンのルールに従って厳密に評価されます。
- プログラムの終了時に、ファイルに書き込まれるすべてのデータは、抽象セマンティクスに従ってプログラムを実行すると生成される可能性のある結果の1つと同一である必要があります。
- 対話型デバイスの入力と出力のダイナミクスは、プログラムが入力を待つ前に、プロンプト出力が実際に配信されるように行われる必要があります。インタラクティブデバイスを構成するものは、実装によって定義されます。
これらをまとめて、プログラムの観察可能な動作と呼びます。[注: 抽象セマンティクスと実際のセマンティクスの間のより厳密な対応は、各実装によって定義される場合があります。 -文末脚注]
これらのループは法的に最適化することができ、実際、標準が意図的にそれをさらに簡単にしようとするシナリオがあります。
[C++14: 1.10/24]:
実装は、任意のスレッドが最終的に次のいずれかを実行すると想定する場合があります。
- 終了、
- ライブラリI/O関数を呼び出します。
- 揮発性オブジェクトにアクセスまたは変更する、または
- 同期操作またはアトミック操作を実行します。
[注:これは、終了が証明できない場合でも、空のループの削除などのコンパイラ変換を可能にすることを目的としています。 -文末脚注]
実際、コンパイラは、これらのプログラムのループの意図が、繰り返されるテキストの放出を遅くしているように見えることに気付いて「礼儀正しく」なっている可能性があります。出力。 :)
コンパイラーを指定しませんでしたが、gcc
であると仮定しましょう。
gccはnot、少なくとも documentation に従って、空のループを削除しません。次のテキストが含まれています。
歴史的に、GCCは「空の」ループを削除していません。これは、プログラムにループを入れる理由として最も可能性が高いのは遅延があるためです。したがって、ループを削除しても実際のプログラムの実行速度は上がりません。
ただし、オプティマイザーによって「空にされた」場合、つまり、オプティマイザーがループの外に移動できるコードがループに含まれていて、結果のループが空の場合は、空のループを削除できます。
これが最新バージョンでも当てはまるかどうかは、ドキュメントからは明らかではありません。マニュアルには、理由を明記せずに「歴史的に」言及されています。正確なプラットフォームとコンパイラに関する情報で質問を更新すると、より良い答えが得られるかもしれません。
実行時間は次のような多くのプラットフォーム固有の問題に依存するため、CまたはC++実行可能ファイルには最小実行時間はありません。
乗算をサポートするプロセッサもあれば、サポートしないプロセッサもあります。乗算をサポートしないプロセッサは、乗算命令を持つプロセスよりもプログラムの実行に時間がかかります。浮動小数点と同じです。
プロセッサの内部動作速度は異なります。 「クロックサイクル」と呼ばれる一般的な時間測定単位があります。ほとんどのプロセッサベンダーは、命令の期間をクロックサイクルで指定しています。この測定は、キャッシュ管理などの内部サポートのために難しい場合があります。
一部のプロセッサには、命令または命令パターンの実行を最適化できるロジックがあります。 1つの最適化は分岐予測です。
多くのプラットフォームには割り込みがあります。たとえば、オペレーティングシステムが実行を別のプログラムに切り替えるタイミングを知ることができる「システムティック」割り込みが存在する場合があります。 I/Oが発生するときなど、それほど周期的ではないものもあります。プログラムが中断された場合、最小実行時間は保証されません。
最小実行時間を指定すると、CおよびC++言語の移植性に大きな打撃を与えます。一部のプラットフォームでは、最小時間よりも速くコードを実行する必要があります。他のプラットフォームでは、最小実行時間を達成できない場合があります(ただし、Cのような高級言語の恩恵を受ける可能性があります)。
また、時間はどのように測定されますか?
最小実行時間は遅延ループまたはポーリングに適用されますか?
いいえ、保証はありません:(からの引用 N1570、5.1.2.3プログラム実行 )
1この国際規格のセマンティック記述は、最適化の問題が無関係である抽象マシンの動作を記述しています。
とにかく、C標準は、プログラムが無限のメモリやCPUを持つことができる抽象マシンで実行されたときのプログラムの動作のみを指定します。