optimasi yang tidak dapat diubah


Kompiler Microsoft memungkinkan Anda untuk menambahkan ekstensi "novtable" untuk atribut "__declspec" ketika mendeklarasikan sebuah kelas.

Tujuan yang dinyatakan adalah untuk secara signifikan mengurangi ukuran kode yang dihasilkan. Dalam percobaan dengan komponen kami, penurunannya dari 0,6 menjadi 1,2 persen dari ukuran DLL.

Penerapan: kelas yang tidak dimaksudkan untuk instantiate langsung dari mereka.

Misalnya: kelas antarmuka murni.

Dalam kode, tampilannya seperti ini:

struct __declspec(novtable) IDrawable { virtual void Draw() const = 0; }; 

Catatan: kata kunci struct digunakan untuk mendeklarasikan kelas antarmuka untuk menghilangkan contoh detail artikel yang tidak relevan; sedangkan dalam hal menggunakan kelas, seseorang harus menggunakan publik untuk menunjukkan "publisitas" metode. Untuk alasan yang sama, saya tidak akan menambahkan destruktor virtual ke kelas antarmuka dalam artikel ini.

Nama "novtable" menjanjikan bahwa tidak akan ada tabel virtual ... Tapi bagaimana mekanisme untuk menjalankan fungsi virtual bekerja dalam kode berikut:

 //   ,   IDrawable: class Rectangle : public IDrawable { virtual void Draw() const override { } int width; int height; }; … IDrawable* drawable = new Rectangle; drawable->Draw(); //   Rectangle::Draw … 


Ingat apa yang ditambahkan saat mendeklarasikan fungsi virtual di kelas:

  1. Menentukan tabel fungsi virtual. Satu instance dari tabel ini digunakan untuk semua instance kelas.
  2. Pointer ke tabel fungsi virtual ditambahkan ke anggota data kelas.
  3. Kode untuk menginisialisasi penunjuk ini dalam konstruktor kelas.

Dengan demikian, dalam contoh kita, akan ada deklarasi dua tabel fungsi virtual: untuk IDrawable dan untuk Rectangle. Saat membuat objek Rectangle, konstruktor IDrawable adalah yang pertama kali dieksekusi, yang menginisialisasi pointer ke tabel fungsi virtualnya. Secara skematis, tampilannya seperti ini:


Karena fungsi draw di IDrawable dideklarasikan virtual murni ("= 0" ditunjukkan sebagai ganti badan fungsi), alamat fungsi purecall yang dihasilkan oleh kompiler dituliskan dalam tabel fungsi virtual.

Kemudian konstruktor Rectangle dieksekusi, yang menginisialisasi pointer yang sama, tetapi ke tabel fungsi virtualnya:



Apa yang dilakukan β€œnovtable”, dan mengapa Microsoft berjanji untuk mengurangi ukuran kode?


Ini adalah definisi yang tidak perlu dari tabel fungsi virtual IDrawable dan inisialisasi pointer ke dalam konstruktor IDrawable yang dikecualikan dari kode yang dihasilkan ketika menambahkan "novtable".

Dalam hal ini, ketika membangun IDrawable, pointer ke tabel fungsi virtual akan berisi nilai yang tidak dapat diprediksi. Tapi ini seharusnya tidak mengganggu kita, karena membuat implementasi dengan akses ke fungsi virtual sebelum pembangunan objek yang lengkap biasanya merupakan kesalahan. Jika, misalnya, fungsi non-virtual dari kelas ini dipanggil dalam konstruktor dari kelas dasar, yang pada gilirannya memanggil fungsi virtual, maka fungsi purecall akan dipanggil tanpa novtable, dan perilaku yang tidak dapat diprediksi dengan novtable; tidak ada opsi yang dapat diterima.

Perhatikan bahwa tidak hanya penurunan ukuran, tetapi juga beberapa percepatan program.

RTTI


Seperti yang Anda ketahui, std :: dynamic_cast memungkinkan Anda untuk melemparkan pointer dan tautan dari satu instance kelas ke pointer dan tautan ke yang lain, jika kelas-kelas ini terhubung secara hierarkis dan polimorfik (berisi tabel fungsi virtual). Pada gilirannya, operator typeid memungkinkan Anda untuk mendapatkan informasi tentang suatu objek dalam runtime menggunakan pointer (tautan) ke objek yang diteruskan ke sana. Kemampuan ini disediakan oleh mekanisme RTTI, yang menggunakan tipe informasi yang terletak dengan merujuk pada tabel kelas. Rincian struktur dan lokasi tergantung pada kompiler. Dalam kasus kompiler Microsoft, tampilannya seperti ini:



Oleh karena itu, jika selama kompilasi, kompiler diperintahkan untuk mengaktifkan RTTI, maka novtable juga mengecualikan pembuatan definisi type_info untuk IDrawable dan data layanan yang diperlukan untuk itu.
Perhatikan bahwa jika Anda entah bagaimana memberikan pengetahuan bahwa pointer yang direferensikan (tautan) ke kelas dasar menunjukkan implementasi turunan, maka std :: static_cast lebih efisien dan tidak memerlukan RTTI.

Spesifik Microsoft


Selain MSVC, fitur ini dengan sintaksis yang sama ada di Dentang ketika mengkompilasi di Windows.

Kesimpulan


  1. __declspec (novtable) - tidak memengaruhi jumlah memori yang digunakan oleh instance kelas.
  2. Mengurangi ukuran dan mempercepat program dipastikan dengan menghilangkan definisi tabel fungsi virtual yang tidak digunakan, RTTI overhead, dan menghilangkan kode inisialisasi untuk pointer ke tabel fungsi virtual di konstruktor kelas antarmuka.

Source: https://habr.com/ru/post/id442340/


All Articles