v-table vs if call
От: maks1180  
Дата: 01.12.22 04:58
Оценка:
1) Что будет быстрее работать вызов через v-table или такая конструция через if () myfunc1() else myfunc2() или вызов через switch().
Как мне кажется (я не тестировал), но при вызове через v-table процессор не может предсказать адрес перехода и поэтому кэш будет чаще промахиваться.

2) Есть ли v-table у struct ? Если есть будет ли чем отличаться по производительности от v-table class ?

3) Подскажите приемлимое решение, есть сетевое приложение:
а) на каждое входящее соединение создаём объект класс ClientCommon и далее он отвечает за работу с этим соединением.
б) после общения с ним мы понимает кто он и должны создать заместо ClientCommon либо Client1 либо Client2

для этих классов я буду вызывать: функцию OnRecv() при получении данных для них и OnClose() при разрыве соединения.
Но код в этих функциях будет отличаться для ClientCommon, Client1, Client2.
Как это лучше реализовать ?

Как я вижу это сделать: объявляю union { ClientCommon, Client1, Client2 } вызываю сначала конструктор для ClientCommon,
потом нужно как-то на этом же месте в памяти создать объект Client1 или Client2 от ClientCommon. ВАЖНО именно на этом же месте создать, так как адрес уже привязан в epoll.
Как это сделать ? Вроде через "new" можно указать адрес памяти ?

Или лучше сделать через if else заместо v-table ? В этом случаи есть шанс что gcc проинлайнит все вызовы и будет быстрее работать.
===============================================
(реклама, удалена модератором)
Отредактировано 01.12.2022 5:09 maks1180 . Предыдущая версия . Еще …
Отредактировано 01.12.2022 5:08 maks1180 . Предыдущая версия .
Re: v-table vs if call
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 01.12.22 05:02
Оценка:
Здравствуйте, maks1180, Вы писали:

M>1) Что будет быстрее работать вызов через v-table или такая конструция через if () myfunc1() else myfunc2() или вызов через switch().

M>Как мне кажется (я не тестировал), но при вызове через v-table процессор не может предсказать адрес перехода и поэтому кэш будет чаще промахиваться.

M>2) Есть ли v-table у struct ? Если есть будет ли чем отличаться по производительности от v-table class ?


Ровно то же самое.


M>Или лучше сделать через if else заместо v-table ? В этом случаи есть шанс что gcc проинлайнит все вызовы и будет быстрее работать.


Ну, вообще-то нормальный компилятор может понять, когда можно напрямую вызвать. Если напрямую вызвать нельзя, то не пойму, как тебе if else поможет
Маньяк Робокряк колесит по городу
Re[2]: v-table vs if call
От: maks1180  
Дата: 01.12.22 05:14
Оценка:
M>>Или лучше сделать через if else заместо v-table ? В этом случаи есть шанс что gcc проинлайнит все вызовы и будет быстрее работать.

M>Ну, вообще-то нормальный компилятор может понять, когда можно напрямую вызвать. Если напрямую вызвать нельзя, то не пойму, как тебе if else поможет


Т.е. он может вызов виртуальной функции ClientCommon::OnRecv, превратить в что-то типа:
if (v-table==Client1) call Client1::OnRecv
else
if (v-table==Client2) call Client2::OnRecv
else
call ClientCommon::OnRecv

У него же нет гарантий, что у меня не появиться Client3 ?
===============================================
(реклама, удалена модератором)
Отредактировано 01.12.2022 5:16 maks1180 . Предыдущая версия .
Re[3]: v-table vs if call
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 01.12.22 05:27
Оценка: +1
Здравствуйте, maks1180, Вы писали:


M>>Ну, вообще-то нормальный компилятор может понять, когда можно напрямую вызвать. Если напрямую вызвать нельзя, то не пойму, как тебе if else поможет


M>Т.е. он может вызов виртуальной функции ClientCommon::OnRecv, превратить в что-то типа:

M>if (v-table==Client1) call Client1::OnRecv
M>else
M>if (v-table==Client2) call Client2::OnRecv
M>else
M>call ClientCommon::OnRecv

M>У него же нет гарантий, что у меня не появиться Client3 ?


Нет конечно. В твоем случае если реализаций немного и расширяться это дело не будет, то наверное быстрее будет свич работать. Надо смотреть что нагенерится, но в общем случае if else будут тормознее свича. vtbl — ну тут хз, оно может в кеше лежать и будет быстро. Ну и vtbl — это константная задержка вне зависимости от количества реализаций, if — если компилятор не додумается сделать табличку — будет совсем плохим, switch — говорят, что в этом случае компилятор генерит табличку и оно довольно эффективно, но по идее, тоже зависит от числа альтернатив

А вообще — я б просто бенчмарки бы писал и тестировал, что быстрее
Маньяк Робокряк колесит по городу
Re: v-table vs if call
От: sergii.p  
Дата: 01.12.22 08:07
Оценка:
Здравствуйте, maks1180, Вы писали:

M>1) Что будет быстрее работать вызов через v-table или такая конструция через if () myfunc1() else myfunc2() или вызов через switch().

M>Как мне кажется (я не тестировал), но при вызове через v-table процессор не может предсказать адрес перехода и поэтому кэш будет чаще промахиваться.

немного описано здесь с замерами http://scrutator.me/post/2014/06/26/crtp_demystified.aspx

M>Как я вижу это сделать: объявляю union { ClientCommon, Client1, Client2 } вызываю сначала конструктор для ClientCommon,

M>потом нужно как-то на этом же месте в памяти создать объект Client1 или Client2 от ClientCommon. ВАЖНО именно на этом же месте создать, так как адрес уже привязан в epoll.
M>Как это сделать ? Вроде через "new" можно указать адрес памяти ?

звучит как std::variant
Re: v-table vs if call
От: qaz77  
Дата: 01.12.22 10:38
Оценка:
Здравствуйте, maks1180, Вы писали:

M>1) Что будет быстрее работать вызов через v-table или такая конструция через if () myfunc1() else myfunc2() или вызов через switch().

M>Как мне кажется (я не тестировал), но при вызове через v-table процессор не может предсказать адрес перехода и поэтому кэш будет чаще промахиваться.

Такое еще соображение.
vtbl — это еще и вклад в sizeof объекта.
Если объект большой и/или таких объектов не слишком много, то и пофиг.

Был у меня случай, когда делал электронную таблицу аля excel и было много мелких объектов ячеек cell.
И каждый этот cell реализовывал несколько "интерфейсов" aka наборов виртуальных функций абстрактных классов.
Итого получалось, что на каждый cell приходилось порядка десятка vtbl.
Размер всех этих vtbl составлял примерно столько же, сколько размер членов данных класса.
Тогда ради оптимальности пришлось отказаться от принципа "is a" и унаследовать часть несвязанных интерфейсов друг от друга,
потребление памяти почти в два раза снизилось.
Re: v-table vs if call
От: ArtDenis Россия  
Дата: 01.12.22 11:00
Оценка:
Здравствуйте, maks1180, Вы писали:

M>1) Что будет быстрее работать вызов через v-table или такая конструция через if () myfunc1() else myfunc2() или вызов через switch().

Только тесты, пиближенные к реальным сценариям, на разных компиляторах и разных процах покажут что быстрее

M>2) Есть ли v-table у struct ? Если есть будет ли чем отличаться по производительности от v-table class ?

Есть. Не отличается.
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re: v-table vs if call
От: σ  
Дата: 01.12.22 11:13
Оценка: +1
M>3) Подскажите приемлимое решение, есть сетевое приложение:
M>а) на каждое входящее соединение создаём объект класс ClientCommon и далее он отвечает за работу с этим соединением.
M>б) после общения с ним мы понимает кто он и должны создать заместо ClientCommon либо Client1 либо Client2

M>для этих классов я буду вызывать: функцию OnRecv() при получении данных для них и OnClose() при разрыве соединения.

M>Но код в этих функциях будет отличаться для ClientCommon, Client1, Client2.
M>Как это лучше реализовать ?

Ну делай типа PImpl, сначала Common-behavior, потом подменяй имплементацию на конкретную.

M>Как я вижу это сделать: объявляю union { ClientCommon, Client1, Client2 } вызываю сначала конструктор для ClientCommon,

M>потом нужно как-то на этом же месте в памяти создать объект Client1 или Client2 от ClientCommon.

Re[2]: v-table vs if call
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 01.12.22 11:15
Оценка:
Здравствуйте, qaz77, Вы писали:


Q>Был у меня случай, когда делал электронную таблицу аля excel и было много мелких объектов ячеек cell.

Q>И каждый этот cell реализовывал несколько "интерфейсов" aka наборов виртуальных функций абстрактных классов.

Подобная проблема была и у создателей исходного Excel. Вроде Дональд Бокс в своей книжке "Сущность технологии COM" об этом писал
Маньяк Робокряк колесит по городу
Re: v-table vs if call
От: koenjihyakkei Россия  
Дата: 01.12.22 12:48
Оценка: +1
Здравствуйте, maks1180, Вы писали:

Когда-то задавался таким вопросом для какого-то конкретного случая. С того раза остался такой бенч: https://quick-bench.com/q/RFPkn-G2Q_jXIzpSushRSXo0beU

Из него видно, что свитч быстрее. Но за качество бенча конечно не ручаюсь Там вроде инлайнится все в свитче.

Но вообще switch table и vtable по своей сути одно и то же. В том и другом случае будет индиректный кол, который гораздо хуже предсказывается бранч предиктором.
Re[2]: v-table vs if call
От: maks1180  
Дата: 02.12.22 04:47
Оценка:
K>Но вообще switch table и vtable по своей сути одно и то же. В том и другом случае будет индиректный кол, который гораздо хуже предсказывается бранч предиктором.

Спасибо, получается в 3-10 раз switch быстрее vtable.

Если сделать через if будет direct call это будет быстрее чем switch ?
===============================================
(реклама, удалена модератором)
Re[3]: v-table vs if call
От: koenjihyakkei Россия  
Дата: 02.12.22 15:44
Оценка:
Здравствуйте, maks1180, Вы писали:

M>Спасибо, получается в 3-10 раз switch быстрее vtable.


M>Если сделать через if будет direct call это будет быстрее чем switch ?


Ну так switch это в принципе обычная if else цепочка. Он оптимизируется в switch table только при определенных условиях, например, количество кейсов большое, значения кейсов плотные и т.д.
Re[4]: v-table vs if call
От: maks1180  
Дата: 03.12.22 02:00
Оценка:
M>>Если сделать через if будет direct call это будет быстрее чем switch ?

K>Ну так switch это в принципе обычная if else цепочка. Он оптимизируется в switch table только при определенных условиях, например, количество кейсов большое, значения кейсов плотные и т.д.


if else может быть компилятором оптимизирована в switch table ?
===============================================
(реклама, удалена модератором)
Re[2]: v-table vs if call
От: VVV Россия  
Дата: 03.12.22 11:59
Оценка: +1
Здравствуйте, koenjihyakkei, Вы писали:

K>Когда-то задавался таким вопросом для какого-то конкретного случая. С того раза остался такой бенч: https://quick-bench.com/q/RFPkn-G2Q_jXIzpSushRSXo0beU


K>Из него видно, что свитч быстрее. Но за качество бенча конечно не ручаюсь


К сожалению, тест некорректный. И на его основе нельзя делать какие-то выводы.

Проблемы теста:
1)
Virtual: std::uniform_int_distribution<std::mt19937::result_type> dist6(0,2);
Switch: std::uniform_int_distribution<std::mt19937::result_type> dist6(0,3); 25% не попадают в case.
2)
Virtual содержит и switch и new Foo1/2/3, т.е. время считается и для выделения памяти по new и для switch, что неправильно: switch должен быть только в конфигурации Switch (т.к. наша задача измерить 'виртуальная функция' против 'switch', а этот switch в Virtual съедает такое же время как и switch в Switch).
3)
std::array<Base*, 1000> arr; и std::array<int, 1000> arr; здесь не нужны, т.к. такой тест сейчас больше тестирует доступ к элементу массива и попадание массива в кэш (к тому же sizeof(int) < sizeof(Base*)), а не вызов функций.
4)
При вызове функций объекта передаётся ещё и this, если это учесть в Switch, что функции будут сложнее, чем в тесте и, скорее всего, им придётся передавать указатель на структуру, с которой они работают, то тест показывает равенство по времени (иногда switch для средних значений чуть отстаёт. Для вызова без параметров switch работает быстрее, что и понятно)

Base *self=new Foo1;

Virtual
    for (size_t i = 0; i < 1000; i++) {
      int res = self->GetN();

...
Switch
    for (size_t i = 0; i < 1000; i++) {
      int res = 0;
      switch(1) {
        case 0:
          res = GetN1(self); break;
        case 1:
          res = GetN2(self); break;
...
Re[2]: v-table vs if call
От: Patalog Россия  
Дата: 06.12.22 18:23
Оценка:
Здравствуйте, qaz77, Вы писали:

хъ

Q>И каждый этот cell реализовывал несколько "интерфейсов" aka наборов виртуальных функций абстрактных классов.

Q>Итого получалось, что на каждый cell приходилось порядка десятка vtbl.

Не очень понял как такое получилось В объекте же афаир только vptr, а сама таблица относится в классу, а не к инстансу?
Почетный кавалер ордена Совка.
Re[3]: v-table vs if call
От: watchmaker  
Дата: 06.12.22 20:15
Оценка:
Здравствуйте, Patalog, Вы писали:

P>Здравствуйте, qaz77, Вы писали:


P>хъ


Q>>И каждый этот cell реализовывал несколько "интерфейсов" aka наборов виртуальных функций абстрактных классов.

Q>>Итого получалось, что на каждый cell приходилось порядка десятка vtbl.



P> сама таблица относится в классу, а не к инстансу?

Да, верно. Для каждого типа класса будет одна таблица вне зависимости от числа экземпляров. Впрочем на них тоже может много места потратится и в компиляторах даже есть нестандатные средства борьбы с этим, например как __declspec(novtable).


P>Не очень понял как такое получилось В объекте же афаир только vptr


Тоже верно. В объекте хранится указатель vptr на таблицу.
Только нюанс в том сколько таких указателей содержит объект: внезапно vptr в нём не всегда один.


struct Base {
  virtual ~Base();
}

struct A : Base {
  int a;
}

struct B : Base {
  int b;
}

struct C : A, B {
  int c;
};

Как в памяти поля расположены должны быть у экземпляра C? Например, так:
|  vptr  |   a   |   vptr  |    b     |     c    |

А если считаешь, что один vptr лишний, то попробуй предложить свой вариант расположения. Но только чтобы в нём можно было по прежнему dynamic_cast реализовать от базовых классов к неследнику и обратно.
Re[2]: v-table vs if call
От: T4r4sB Россия  
Дата: 06.12.22 20:57
Оценка:
Здравствуйте, qaz77, Вы писали:

Q>Итого получалось, что на каждый cell приходилось порядка десятка vtbl.


Не понял, каждая ячейка отдельный класс что ли? Или наследование абстрактных классов требует дополнительный указатель в каждом экземпляре?
Re[3]: v-table vs if call
От: σ  
Дата: 06.12.22 21:39
Оценка:
Q>>Итого получалось, что на каждый cell приходилось порядка десятка vtbl.

TB>Не понял, каждая ячейка отдельный класс что ли?


Адепты ООП обезумели, сумеешь ли ты совладать с ними?
Re[3]: v-table vs if call
От: Sm0ke Россия ksi
Дата: 06.12.22 22:42
Оценка:
Здравствуйте, Patalog, Вы писали:

P>Здравствуйте, qaz77, Вы писали:


P>хъ


Q>>И каждый этот cell реализовывал несколько "интерфейсов" aka наборов виртуальных функций абстрактных классов.

Q>>Итого получалось, что на каждый cell приходилось порядка десятка vtbl.

P>Не очень понял как такое получилось В объекте же афаир только vptr, а сама таблица относится в классу, а не к инстансу?


Если интерфейсов много, то на каждый по vtable с несколькими вирт функциями. Может это имелось ввиду?
Re: v-table vs if call
От: vsb Казахстан  
Дата: 06.12.22 23:03
Оценка: +1
vtable это запрос в память. Если не в кеше, то очень дорого. Если в кеше, то быстро. Но если перед этим есть ещё один запрос в память, то может предсказать и заранее запросить. Тогда будет быстро.

if это практически гарантированно очень быстро. Код же у тебя более-менее компактно расположен. В крайнем случае потратит сколько-то циклов, выполняя неправильно предсказанную ветку, но это мелочи в сравнение с запросом в память.

В общем однозначно сказать нельзя. Если у тебя будет миллион if-ов непредсказуемых, то процессор запутается. Если ты по одному объекту вызываешь кучу разных методов и vtable не вытесняется из кеша, то будет быстро. Если ты ни с того ни с сего взял и вызвал виртуальный метод и процессору пришлось ждать полного цикла запроса в память, это грустно.

struct и class это одно и то же.

По твоему вопросу — без разницы. Расходы на переключение между ядром и юзерспейсом настолько огромны, что на их фоне меркнет всё.

По коду — я бы сделал что-то вроде struct { void (*handler)(uint8_t *data, uint32_t length, void *ctx); void *ctx; } то бишь структуру из указателя на функцию и void* контекста. На указатель присваивал бы нужную реализацию, на ctx нужные данные (если они вообще нужны).

Ну на C++ наверное надо два класса объявить. Один внутренний с виртуальным методом и один наружний, который будет держать указатель на внутренний, а в epoll уже отдавай указатель на наружний класс.

Ну или тупо указатель на указатель.
Отредактировано 06.12.2022 23:08 vsb . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.