Re[18]: v-table vs if call
От: T4r4sB Россия  
Дата: 07.12.22 11:12
Оценка:
Здравствуйте, Marty, Вы писали:

M>А, ну попонятнее стало. Типа есть какая-то отдельная таблица указателей на VTBL тех интерфейсов, которые поддерживает объект? Да, по идее, должно места сэкономить. А как dynamic_cast работает?


dynamic_cast работает хреново
https://doc.rust-lang.org/std/any/index.html
И многоуровневые иерархии классов тоже не делаются.
А ещё эти же интерфейсы могут работать как концепты в шаблонах и раскрываться на этапе компиляции.

M>В принципе, в плюсах подобное тоже можно сделать, будет не слишком удобно, но оно точно вот так постоянно надо? Не так часто объекты реализуют множество различных интерфейсов, и не так часто эти объекты настолько малы и их так много, чтобы это играло какую-то заметную роль


Ну в задаче с ячейками Екселя это бы сыграло.
Re[17]: v-table vs if call
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 07.12.22 11:15
Оценка:
Здравствуйте, night beast, Вы писали:

TB>>>Посмотри ещё раз на мой пример — зачем что-то хранить в объекте?

M>>Ну, не в объекте, а вместе с ним. Какая разница?

NB>
NB>struct IPoint {
NB>   virtual ~IPoint() {}
NB>};

NB>struct Point2d : IPoint {
NB>   int16_t x;
NB>   int16_t y;
NB>};


NB>Point2d line[100];
NB>



Не понял, и что это иллюстрирует?
Маньяк Робокряк колесит по городу
Re[19]: v-table vs if call
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 07.12.22 11:16
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Ну в задаче с ячейками Екселя это бы сыграло.


Ну вот в такой задаче можно и костылями подпереться
Маньяк Робокряк колесит по городу
Re[18]: v-table vs if call
От: night beast СССР  
Дата: 07.12.22 11:18
Оценка:
Здравствуйте, Marty, Вы писали:

M>>>Ну, не в объекте, а вместе с ним. Какая разница?


NB>>
NB>>Point2d line[100];
NB>>



M>Не понял, и что это иллюстрирует?


размеры line c vtable и без.
Re[19]: v-table vs if call
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 07.12.22 11:22
Оценка:
Здравствуйте, night beast, Вы писали:

M>>Не понял, и что это иллюстрирует?


NB>размеры line c vtable и без.


А, ну это опять же про Excel с кучей ячеек
Маньяк Робокряк колесит по городу
Re: v-table vs if call
От: fk0 Россия https://fk0.name
Дата: 07.12.22 12:44
Оценка:
Здравствуйте, maks1180, Вы писали:

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

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

Спекулятивно исполнить может. Предсказать -- смотря как сделано, потенциальная возможность есть.
Но на самом деле это всё не столь критично и не столь принципиально. Это -- десятки тактов.

А вот то, что компилятор не может "предсказать", что там за переход через vtable будет -- куда серьёзней.
В случае switch-case многообразие вариантов по крайней мере резко снижается, оно становится вообще перечислимым,
и код в целом начинает поддаваться оптимизации. Это принципиальный момент, который позволит работу регистрового
аллокатора распространить вниз по всей цепочке вызовов, а не тупо следовать ABI, включить inline-фунцкии,
провести глобальную оптимизацию на всю глубину вызова. И это уже могут быть не десятки тактов, а тысячи.

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


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

M>3) Подскажите приемлимое решение, есть сетевое приложение:

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

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

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

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

Динамическая диспетчеризация является краеугольным камнем, от неё так просто не откажешься
и чем-то, непонятно чем, так просто не заменишь. Не зависимо от языка, фреймворка и т.п.
Варианты со свитч-кейсами же архитектурно плохое решение, т.к. при дальнейшей модификации
кода может выясниться, что где-то их забыли, где-то их нет, где-то оно разрослось до неприличных
размеров (и switch-case по огромной таблице выполняется сильно медленее, чем вызов по указателю).

Можно пытаться снижать количество точек где производится динамическая диспетчеризация,
и в пределе достигнуть одной такой точки, где сразу делается ветвление по одной из множества
веток, для каждой из которых существуют свои отдельные реализации классов/функций уже не требующих
динамической диспетчеризации (но раздувающие объём кода). Что обычно достигается с помощью шаблонов.

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

Ещё может быть такой подход, что реализуется какой-то обобщённый класс/функция, которая
может работать с любым произвольным данным ей другим классом, для чего свойства последнего
класса выражаются отдельным классом и отдельно передаются в виде аргумента. Если свойства
выражаются функциями -- опять получается динамическая диспетчеризация. Но если свойства
могут быть выражены константами, то вместо неё остаются максимум только ветвления. И запросто,
что часть свойств может быть выражена именно что константами, а не виртуальными функциями,
что позволит снизить количество точек где нужна динамическая диспетчеризация.

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

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

Это какое-то велосипедостроительство. Напоминает виртуальное наследование (когда поряок вызова конструкторов ручной).
Может его тогда и стоит использовать. Тем более что виртуальный деструктор уже весьма полезен.

Но непонятно зачем, почему сразу ClientN не сконструировать? Или почему нужнo именно
наследование (когда ClientCommon является базовым классом), а не агрегация (когда ClientCommon
создаётся в сторонке и по ссылке передаётся в новый создаваемый ClientN, владение тоже передается).

М> ВАЖНО именно на этом же месте создать, так как адрес уже привязан в epoll.

M>Как это сделать ? Вроде через "new" можно указать адрес памяти ?

В случае юниона и виртуального наследования использование placement new уже не имеет смысла
(потому, что у юниона был какой-то конструктор и он что-то там уже наконструировал).
Оно имеет смысл когда вместо юниона под низом лежит тупо char[100500], например. Что тоже
имеет смысл, чтоб избавиться от юниона, тем более если не известно, что в этом массиве
будет потом положено (юнион заранее не напишешь). Но оно вообще нужно, ручное управление
памятью? Аллокатор из библиотеки, в общем и целом, не так уж и плох. Если 100500 запросов
в секунду в разных тредах -- может вручную и лучше, или через отдельный специальный аллокатор.

И этот массив, его нужно выравнить, через alignof, на std::max_align_t или что-то вроде того.
Юнион сам выровняет всё как надо. Но с юнионом нужно помнить, что если обратился к одному полю --
то всё, к другим обращаться не имеешь права. Так только в голом C можно, но не в C++.

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


  if (object->type == XXX) do_something();
  else if (object->type == YYY) do_something_another();


Вот так ^^^ точно делать не стоит, кроме очень узких мест и для классов где всего три типа может быть
и четвёртый никогда не появится. Потому, что дальнейшая эволюция кода все такие места превратит
в бесконечный источник проблем. Я выше расписал возможный вариант: снижение точек диспетчеризации,
выделение классов несущих (статические, не зависимые от конкретного инстанса) свойства другого класса (traits),
использование этих классов в обобщённых классах или функциях.
Re[14]: v-table vs if call
От: Sm0ke Россия ksi
Дата: 07.12.22 19:53
Оценка:
Здравствуйте, Marty, Вы писали:

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


M>>>Ну, таки где-то эти указатели всё равно хранить надо.


TB>>Да, один лишний указатель приходится гонять по стеку. Это лучше, чем в каждом экземпляре хранить 100500 указателей.

TB>>Ещё если ты в структуре хранишь Box<dyn MyInderface> (unique_ptr<MyInterface>), то он жрёт вдвое больше.

M>А если объект реализует много интерфейсов? Где эти указатели хранятся


Интерфейсы к классу относятся, а не к объекту. Один класс — одна таблица. Соотв один указатель на неё.

M>Так-то в C++, если объект реализует один интерфейс, то и указатель на vtbl будет один, и никаких проблем
Re[15]: v-table vs if call
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 07.12.22 20:14
Оценка:
Здравствуйте, Sm0ke, Вы писали:

M>>А если объект реализует много интерфейсов? Где эти указатели хранятся


S>Интерфейсы к классу относятся, а не к объекту. Один класс — одна таблица. Соотв один указатель на неё.


Никаких проблем, и дополнительный уровень косвенности
Маньяк Робокряк колесит по городу
Re[2]: v-table vs if call
От: Aleх  
Дата: 15.12.22 15:16
Оценка:
Здравствуйте, koenjihyakkei, Вы писали:

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


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


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


K>Но вообще switch table и vtable по своей сути одно и то же. В том и другом случае будет индиректный кол, который гораздо хуже предсказывается бранч предиктором.


Если посмотреть asm, то там инлайнятся вызовы GetN1.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.