他の人が言うように、最初にプログラムのパフォーマンスを測定する必要がありますが、実際には違いはおそらくないでしょう。
それでも、概念的なレベルから、私はあなたの質問に混同されるいくつかのことを明確にすると思った。まず、あなたが尋ねる:
最新のコンパイラーでは、関数呼び出しのコストは依然として重要ですか?
キーワード「関数」と「コンパイラ」に注意してください。あなたの引用は微妙に異なります:
言語によっては、メソッド呼び出しのコストがかなり高くなる可能性があることに注意してください。
これは、オブジェクト指向の意味でのメソッドのことです。
「関数」と「メソッド」は頻繁に交換可能に使用されますが、コスト(あなたが求めている)とコンパイル(あなたが与えたコンテキスト)に関しては違いがあります。
特に、静的ディスパッチと動的ディスパッチについて知る必要があります。現時点では最適化を無視します。
Cのような言語では、通常static staticで関数を呼び出します。例えば:
int foo(int x) {
return x + 1;
}
int bar(int y) {
return foo(y);
}
int main() {
return bar(42);
}
コンパイラは、呼び出しを認識するとfoo(y)
、そのfoo
名前が参照している関数を知っているため、出力プログラムはそのfoo
関数に直接ジャンプできます。これは非常に安価です。それが静的ディスパッチの意味です。
代替手段は動的ディスパッチで、コンパイラはどの関数が呼び出されているかを知りません。例として、Haskellのコードをいくつか示します(Cに相当するコードは面倒だからです!):
foo x = x + 1
bar f x = f x
main = print (bar foo 42)
ここで、bar
関数は引数を呼び出していますがf
、引数は何でもかまいません。したがって、コンパイラはbar
高速ジャンプ命令にコンパイルすることはできません。どこにジャンプするかわからないからです。代わりに、生成するコードは、bar
参照f
している関数を見つけるために逆参照し、ジャンプします。それが動的ディスパッチの意味です。
これらの例は両方とも関数用です。メソッドについては、動的にディスパッチされる特定のスタイルのスタイルと考えることができます。たとえば、次のPythonがあります。
class A:
def __init__(self, x):
self.x = x
def foo(self):
return self.x + 1
def bar(y):
return y.foo()
z = A(42)
bar(z)
y.foo()
コールは、それがの値まで見ていることから、動的ディスパッチを使用foo
してプロパティをy
オブジェクト、そしてそれが見つけたものは何でも呼び出します。それy
がclassを持っていることA
や、A
クラスにfoo
メソッドが含まれていることを知らないので、そのままジャンプすることはできません。
OK、それが基本的な考え方です。コンパイルまたは解釈するかどうかに関係なく、静的ディスパッチは動的ディスパッチよりも高速であることに注意してください。他のすべてが等しい。どちらの場合も、逆参照には追加コストが発生します。
では、これは現代の最適化コンパイラにどのように影響しますか?
最初に注意することは、静的ディスパッチをより高度に最適化できることです。ジャンプ先の関数がわかると、インライン化などを実行できます。動的ディスパッチでは、実行時までジャンプしていることがわからないため、実行できる最適化はあまりありません。
第二に、いくつかの言語では、いくつかの動的ディスパッチがジャンプを終了する場所を推測し、それによって静的ディスパッチに最適化することが可能です。これにより、インライン化などの他の最適化を実行できます。
上記のPythonの例では、Pythonが他のコードでクラスとプロパティをオーバーライドできるため、このような推論は非常に絶望的です。
たとえば、注釈を使用y
してクラスに制限するなど、言語により多くの制限を課すA
ことができる場合、その情報を使用してターゲット関数を推測できます。サブクラス化された言語(クラスを持つほとんどすべての言語です!)では、y
実際には異なる(サブ)クラスを持っている可能性があるため、実際には十分ではありませんfinal
。
Haskellはオブジェクト指向言語ではありませんが、我々はの値を推測することができますf
インライン化bar
(された静的にディスパッチ)main
代わりに、foo
ためy
。foo
in のターゲットmain
は静的に知られているため、呼び出しは静的にディスパッチされ、おそらく完全にインライン化および最適化されます(これらの関数は小さいため、コンパイラーはインライン化する可能性が高くなりますが、一般的には期待できませんが) )。
したがって、コストは次のようになります。
- 言語は静的または動的に呼び出しをディスパッチしますか?
- 後者の場合、言語は実装が他の情報(たとえば、型、クラス、注釈、インライン化など)を使用してターゲットを推測できるようにしますか?
- 静的ディスパッチ(推論またはその他)をどの程度積極的に最適化できますか?
「非常に動的な」言語を使用していて、多くの動的ディスパッチがあり、コンパイラーが利用できる保証がほとんどない場合、すべての呼び出しにコストがかかります。「非常に静的な」言語を使用している場合、成熟したコンパイラは非常に高速なコードを生成します。中間にいる場合は、コーディングスタイルと実装のスマートさによって異なります。