私はしばらくの間、プログラミング言語のアイデアをいじっていました。それは基本的に、システムプログラミング(または実際には高性能を必要とするすべてのプログラミング)を対象とした構文でC ++およびJavaに似ていますが、私の意見では、 、C++ より楽しい構文です。階層クラス構造で仮想メソッドを処理する方法 (私の言語には多重継承は含まれない) と、vtable ルックアップを回避する方法について考えていました。私の質問は 2 つあります。
- 私の理解では、vtable ルックアップが (少なくともゲーム開発のようなタイム クリティカルなシナリオでは) パフォーマンスに影響を与える理由は、オブジェクトの vtable ポインターを参照する必要があり、この vtable は一般的にキャッシュ ミスであるためです。これは正しいですか、それとも問題の一部が欠けていますか?
- 部分的な解決策についての私の考えは次のとおりです。コンパイラがオブジェクトの型を完全に決定できる場合(つまり、それが考えている型から派生した型であることができない場合)、このオブジェクトは、型が次の引数として関数に渡されますオブジェクトの型のスーパークラスの場合、関数で呼び出される仮想メソッドの場所は、コンパイル時に追加される一種の「隠し」引数として渡すことができます。おそらく例が役立つでしょう:
クラス階層の次の疑似コードを検討してください。
class Animal {
public void talk() { /* Generic animal noise... */ }
// ...
}
class Dog extends Animal {
public void talk() { /* Override of Animal::talk(). */ }
// ...
}
void main() {
Dog d = new Dog();
doSomethingWithAnimal(d);
}
void doSomethingWithAnimal(Animal a) {
// ...
a.talk();
// ....
}
これは疑似コードであり、C++ や Java などではありません。また、Animal 引数は、値ではなく参照によって暗黙的に渡されると仮定します。コンパイラはそれd
が間違いなく typeであることを確認できるため、定義を次のようにDog
変換できます。doSomethingWithAnimal
void doSomethingWithAnimal(Animal a, methodptr talk = NULL) {
// ...
if ( talk != NULL ) {
talk(a);
} else {
a.talk();
}
// ...
}
次にmain
、コンパイラによって次のように変換されます。
void main() {
Dog d = new Dog();
doSomethingWithAnimal(d, Dog::talk);
}
明らかに、これで vtable の必要性が完全になくなるわけではありません。また、オブジェクトの正確なタイプを特定できない場合に備えて vtable を提供する必要があるかもしれませんが、パフォーマンスの最適化としてこれについてどう思いますか? 可能な限りレジスタを使用して引数を渡す予定です。引数がスタックにあふれたとしても、スタック上の methodptr 引数は vtable 値よりもキャッシュ ヒットになる可能性が高くなりますよね? どんな考えも大歓迎です。