CXX95 Telegram 88
#compiler

Теория девиртуализации 😶

Виртуальные функции обычно работают через vtable. Если метод виртуальный, то вместо вызова точно известного метода, в рантайме вычисляется адрес метода, который зависит от динамического типа объекта.

Однако в некоторых случаях компилятор может "доказать", что он точно "знает" метод, который надо вызвать, несмотря на то, что метод виртуальный 😐

Два простых примера: класс без final (нет оптимизации), класс с final (есть оптимизация - девиртуализация). Девиртуализованный вариант меньше дергает память.
void CallDo(TDerived& obj) {
obj.Do(); // будет ли девиртуализация?
}

Компилятор считает, что можно девиртуализовать вызов в таких случаях:
1️⃣ Метод класса помечен как final.
Смысл в том, что даже в случае работы с объектами TDerived*/TDerived& (которые могут указывать на наследника TDerived) нужный метод будет одним и тем же, как его определил класс TDerived.
struct TDerived : IBase {
void Do() final override; // слово `final` тут
};

2️⃣ Класс является финальным. Смысл в том, что указатель на этот класс не будет указывать на какого-то наследника, который что-то мог бы переопределить, потому что у такого класса просто не может быть наследников.
struct TDerived final : IBase { // слово final тут
void Do() override;
};

Однако есть еще одно условие, когда класс считается финальным - если у него финальный деструктор 😁 Этот прикол я обнаружил в исходнике Clang.
struct TDerived : IBase {
~TDerived() final = default; // слово final тут
void Do() override;
};

3️⃣ Мы работаем с объектом класса, а не с указателем на класс. В этом случае точный класс объекта известен на этапе компиляции.
TDerived derived;
derived.Do(); // это же TDerived, инфа 100%

4️⃣ Объект является prvalue. В C++ есть укуренная классификация объектов, где prvalue (pure value) это грубо говоря выражение которое создает новый объект. Смысл в том, что в этом случае тоже известен точный класс объекта на этапе компиляции.
TDerived MakeDerived(); // просто функция
// ...
MakeDerived().Do(); // здесь будет девиртуализация
TDerived{}.Do(); // здесь тоже девиртуализация

На этом всё! Эта оптимизация логичная и скучная, потому что никаких чудес ожидать не приходится. Смысл в том, чтобы доказать что TDerived/TDerived&/TDerived* указывает именно на объект TDerived, а не на какой-то его потомок.

В реальном мире девиртуализация отрабатывает нечасто, так как надо, чтобы совпали два редких покемона кейса, оба противоречат ООП:
(1) работа с TDerived* вместо IBase*;
(2) Класс TDerived или нужный метод финальный (не помню когда в последний раз писал final).

Если вы хотите почитать про девиртуализацию "с нуля" с картинками, то есть крутой лонгрид.

Девиртуализация в C++ не гарантирована. В большинстве своем правила выше работают, но не всегда. В каких-то случаях оптимизировать вызовы виртуальных методов запрещено.

Например, в Apple macOS👩‍💻 есть Kext (Kernel Extension) - расширения ядра, запускающие то или иное несовместимое с оригинальным маком оборудование. Особенность этих Kext в том, что они могут в рантайме менять vtable, поэтому нельзя делать оптимизации, которые обходят обращение к vtable. В Clang есть флаг -fapple-kext для такой настройки.

А в "обычных" окружениях vtable лежат в секциях наподобии .rodata. Эта секция защищена на уровне операционной системы - программа обычно сразу падает при попытке сделать туда какую-нибудь запись в рантайме.
Please open Telegram to view this post
VIEW IN TELEGRAM



tgoop.com/cxx95/88
Create:
Last Update:

#compiler

Теория девиртуализации 😶

Виртуальные функции обычно работают через vtable. Если метод виртуальный, то вместо вызова точно известного метода, в рантайме вычисляется адрес метода, который зависит от динамического типа объекта.

Однако в некоторых случаях компилятор может "доказать", что он точно "знает" метод, который надо вызвать, несмотря на то, что метод виртуальный 😐

Два простых примера: класс без final (нет оптимизации), класс с final (есть оптимизация - девиртуализация). Девиртуализованный вариант меньше дергает память.
void CallDo(TDerived& obj) {
obj.Do(); // будет ли девиртуализация?
}

Компилятор считает, что можно девиртуализовать вызов в таких случаях:
1️⃣ Метод класса помечен как final.
Смысл в том, что даже в случае работы с объектами TDerived*/TDerived& (которые могут указывать на наследника TDerived) нужный метод будет одним и тем же, как его определил класс TDerived.
struct TDerived : IBase {
void Do() final override; // слово `final` тут
};

2️⃣ Класс является финальным. Смысл в том, что указатель на этот класс не будет указывать на какого-то наследника, который что-то мог бы переопределить, потому что у такого класса просто не может быть наследников.
struct TDerived final : IBase { // слово final тут
void Do() override;
};

Однако есть еще одно условие, когда класс считается финальным - если у него финальный деструктор 😁 Этот прикол я обнаружил в исходнике Clang.
struct TDerived : IBase {
~TDerived() final = default; // слово final тут
void Do() override;
};

3️⃣ Мы работаем с объектом класса, а не с указателем на класс. В этом случае точный класс объекта известен на этапе компиляции.
TDerived derived;
derived.Do(); // это же TDerived, инфа 100%

4️⃣ Объект является prvalue. В C++ есть укуренная классификация объектов, где prvalue (pure value) это грубо говоря выражение которое создает новый объект. Смысл в том, что в этом случае тоже известен точный класс объекта на этапе компиляции.
TDerived MakeDerived(); // просто функция
// ...
MakeDerived().Do(); // здесь будет девиртуализация
TDerived{}.Do(); // здесь тоже девиртуализация

На этом всё! Эта оптимизация логичная и скучная, потому что никаких чудес ожидать не приходится. Смысл в том, чтобы доказать что TDerived/TDerived&/TDerived* указывает именно на объект TDerived, а не на какой-то его потомок.

В реальном мире девиртуализация отрабатывает нечасто, так как надо, чтобы совпали два редких покемона кейса, оба противоречат ООП:
(1) работа с TDerived* вместо IBase*;
(2) Класс TDerived или нужный метод финальный (не помню когда в последний раз писал final).

Если вы хотите почитать про девиртуализацию "с нуля" с картинками, то есть крутой лонгрид.

Девиртуализация в C++ не гарантирована. В большинстве своем правила выше работают, но не всегда. В каких-то случаях оптимизировать вызовы виртуальных методов запрещено.

Например, в Apple macOS👩‍💻 есть Kext (Kernel Extension) - расширения ядра, запускающие то или иное несовместимое с оригинальным маком оборудование. Особенность этих Kext в том, что они могут в рантайме менять vtable, поэтому нельзя делать оптимизации, которые обходят обращение к vtable. В Clang есть флаг -fapple-kext для такой настройки.

А в "обычных" окружениях vtable лежат в секциях наподобии .rodata. Эта секция защищена на уровне операционной системы - программа обычно сразу падает при попытке сделать туда какую-нибудь запись в рантайме.

BY C++95


Share with your friend now:
tgoop.com/cxx95/88

View MORE
Open in Telegram


Telegram News

Date: |

According to media reports, the privacy watchdog was considering “blacklisting” some online platforms that have repeatedly posted doxxing information, with sources saying most messages were shared on Telegram. Choose quality over quantity. Remember that one high-quality post is better than five short publications of questionable value. As five out of seven counts were serious, Hui sentenced Ng to six years and six months in jail. bank east asia october 20 kowloon How to create a business channel on Telegram? (Tutorial)
from us


Telegram C++95
FROM American