違いは次のとおりです。
register
非常に局所的な最適化です (つまり、1 つの関数内)。レジスタ割り当ては、よりスマートなコンパイラとより多くのレジスタの両方によって比較的解決された問題です (ほとんどが前者ですが、x86-64 には x86 よりも多くのレジスタがあり、両方とも 8 ビット プロセッサよりも多くの数があります)。
inline
手順間の最適化であるため、より困難です。ただし、再帰の深さが比較的小さく、プロシージャの数が少ないため (インライン化されたプロシージャが大きすぎる場合は、インライン化する意味がありません)、安全にコンパイラに任せることができます。
restrict
ははるかに難しいです。2 つのポインターがエイリアスではないことを完全に知るには、プログラム全体 (ライブラリ、システム、プラグインなどを含む) を分析する必要があります。ただし、情報はプログラマーにとってより明確であり、仕様の一部です。
非常に単純なコードを考えてみましょう:
void my_memcpy(void *dst, const void *src, size_t size) {
for (size_t i = 0; i < size; i++) {
((char *)dst)[i] = ((const char *)str)[i];
}
}
このコードを効率的にする利点はありますか? はい -memcpy
非常に便利な傾向があります (GC のコピーなど)。このコードはベクトル化できますか? コンパイラはそれを推測する必要がdst
ありsrc
、エイリアスはまったくなく、それらが指す領域は独立しています。size
ユーザー入力、実行時の動作、または分析を実質的に不可能にするその他の要素に依存する可能性があります - 停止問題と同様の問題 - 一般に、実行せずにすべてを分析することはできません。または、Cライブラリ(共有ライブラリを想定)の一部であり、プログラムによって呼び出されるため、コンパイル時にすべての呼び出しサイトが認識されない場合もあります。このような分析がなければ、プログラムは最適化がオンになっていると異なる動作を示します。一方、プログラマーは、ボトムアップ分析を必要とせずに、(より高いレベルの) 設計を知るだけで、それらが異なるオブジェクトであることを確認できます。
restrict
2 つのエイリアシング ポインターを処理できない方法でプロシージャを記述したのはプログラマである可能性があるため、ドキュメントの一部にすることもできます。たとえば、エイリアスの場所からメモリをコピーする場合、上記のコードは正しくありません。
要約すると、十分にスマートなコンパイラーは、プログラム全体を知らずに (コードの意味を理解するコンパイラーに移動しない限り) を推測することはできません。それでも決定不能に近い。ただし、ローカル最適化については、コンパイラはすでに十分にスマートです。ただし、プログラム全体の分析を備えた十分にスマートなコンパイラーは、多くの興味深いケースで推測できると思います。restrict
PS。ローカルとは、単一の機能を意味します。したがって、ローカル最適化では、引数、グローバル変数などについて何も想定できません。