Re[4]: v-table vs if call
От: Patalog Россия  
Дата: 07.12.22 09:15
Оценка: 1 (1)
Здравствуйте, qaz77, Вы писали:

хъ

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

Q>Таблица относится к каждому классу с виртуальными функциями.

Q>В примере Impl содержит vptr для классов Inter1 и Inter2:


С vptr все понятно, в исходном сообщении было про vtbl, об чем я и удивился.
Почетный кавалер ордена Совка.
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
От: σ  
Дата: 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: 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
От: 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: 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 . Предыдущая версия .
Re[4]: v-table vs if call
От: T4r4sB Россия  
Дата: 07.12.22 08:00
Оценка: :)
Здравствуйте, qaz77, Вы писали:

Q>Да, как частный случай.

Q>В общем случае не только абстрактных, а имеющих хотя бы одну виртуальную функцию.

Какая-то говняная реализация ООП, это что за говноязык? А, это же С++, в котором не платят за то, чем не пользуются.

Концепция Раста с его жирными указателями, когда в самом классе никаких указателей на таблицы нет, уже не выглядит такой странной, да?
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: 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[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[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
От: 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[3]: v-table vs if call
От: qaz77  
Дата: 07.12.22 07:56
Оценка:
Здравствуйте, T4r4sB, Вы писали:

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

Нет, конечно.

TB>Или наследование абстрактных классов требует дополнительный указатель в каждом экземпляре?

Да, как частный случай.
В общем случае не только абстрактных, а имеющих хотя бы одну виртуальную функцию.
Re[3]: v-table vs if call
От: qaz77  
Дата: 07.12.22 08:09
Оценка:
Здравствуйте, Patalog, Вы писали:

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

Таблица относится к каждому классу с виртуальными функциями.

В примере Impl содержит vptr для классов Inter1 и Inter2:
#include <iostream>

class Inter1
{
public:
  virtual void foo1() = 0;
};

class Inter2
{
public:
  virtual void foo2() = 0;
};

class Impl final: public Inter1, public Inter2
{
public:
  virtual void foo1() override {}
  virtual void foo2() override {}

private:
  double m_data = 3.14 // sizeof(m_data) == 8
};

int main()
{
  Impl instance;
  std::cout << sizeof(instance) << std::endl;
  return 0;
}


Вывод для x86: 16 (8 + 2 * 4)
Вывод для x64: 24 (8 + 2 * 8)

Я упоминал о приеме, который ценой нарушения принципа "is a" уменьшает размер объекта:
#include <iostream>

class Inter1
{
public:
  virtual void foo1() = 0;
};

class Inter2: public Inter1 // << Inter2 is not a Inter1 !!!
{
public:
  virtual void foo2() = 0;
};

class Impl final: public Inter2 // << Inter2 only!
{
public:
  virtual void foo1() override {}
  virtual void foo2() override {}

private:
  double m_data = 3.14 // sizeof(m_data) == 8
};

int main()
{
  Impl instance;
  std::cout << sizeof(instance) << std::endl;
  return 0;
}


И тут чудесным образом:
вывод для x86: 12 (8 + 1 * 4)
вывод для x64: 16 (8 + 1 * 8)
Re[5]: v-table vs if call
От: qaz77  
Дата: 07.12.22 08:21
Оценка:
Здравствуйте, T4r4sB, Вы писали:
TB>Какая-то говняная реализация ООП, это что за говноязык? А, это же С++, в котором не платят за то, чем не пользуются.

Это еще цветочки.
Настоящая веселуха начинается когда объект содержит в себе несколько подобъектов одного класса.
Или с виртуальным наследованием, ромбы всякие.

С другой стороны, не нравится — не используешь.
Вот я практически не пользуюсь множественным наследованием от не чисто абстрактных классов, а также виртуальным наследованием.
Ромб только в один раз делал, но там задача была 1 в 1 как iostream — in/out сериализация.
Re[6]: v-table vs if call
От: T4r4sB Россия  
Дата: 07.12.22 08:32
Оценка:
Здравствуйте, qaz77, Вы писали:

Q>С другой стороны, не нравится — не используешь.


А если очень надо — то пилишь велосипед?
Re[7]: v-table vs if call
От: qaz77  
Дата: 07.12.22 08:37
Оценка:
Здравствуйте, T4r4sB, Вы писали:
TB>А если очень надо — то пилишь велосипед?

Какие тут велосипеды?
Все эти vptr реализация самого C++, от нее никуда не навелосипедишь.

Или речь про какие-нибудь мультиметоды?
Этого да, иногда не хватает.
Тогда только велосипед
Re[8]: v-table vs if call
От: T4r4sB Россия  
Дата: 07.12.22 08:41
Оценка:
Здравствуйте, qaz77, Вы писали:

Q>Какие тут велосипеды?


Сэмулировать жирные указатели, а в классе никаких указателей на таблицы не хранить. Как в Расте.
Re[9]: v-table vs if call
От: qaz77  
Дата: 07.12.22 08:44
Оценка:
Здравствуйте, T4r4sB, Вы писали:
TB>Сэмулировать жирные указатели, а в классе никаких указателей на таблицы не хранить. Как в Расте.

Наверное можно и так.
Класс сделать без виртуальных функций, а полиморфизм руками наколхозить.
Re[9]: v-table vs if call
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 07.12.22 09:18
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Сэмулировать жирные указатели, а в классе никаких указателей на таблицы не хранить. Как в Расте.


А что это и как работает?
Маньяк Робокряк колесит по городу
Re[5]: v-table vs if call
От: qaz77  
Дата: 07.12.22 09:19
Оценка:
Здравствуйте, Patalog, Вы писали:

P>С vptr все понятно, в исходном сообщении было про vtbl, об чем я и удивился.


Да, я опустил слово "указатель". Т.е. подразумевалось "указатель на vtbl".
Думал, что из контекста понятно...
Re[10]: v-table vs if call
От: T4r4sB Россия  
Дата: 07.12.22 09:40
Оценка:
Здравствуйте, Marty, Вы писали:

M>А что это и как работает?


Типа если функция принимает указатель на интерфейс, то на самом деле она принимает пару указателей: указатель на объект, и указатель на vtbl, в которой этот объект реализует этот интерфейс.
Как раз чтоб в объекте не хванить все эти указатели на vtbl.
Re[11]: v-table vs if call
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 07.12.22 10:28
Оценка:
Здравствуйте, T4r4sB, Вы писали:

M>>А что это и как работает?


TB>Типа если функция принимает указатель на интерфейс, то на самом деле она принимает пару указателей: указатель на объект, и указатель на vtbl, в которой этот объект реализует этот интерфейс.

TB>Как раз чтоб в объекте не хванить все эти указатели на vtbl.

Ну, таки где-то эти указатели всё равно хранить надо. И что мешает присунуть не тот указатель на vtbl?
Маньяк Робокряк колесит по городу
Re[12]: v-table vs if call
От: T4r4sB Россия  
Дата: 07.12.22 10:37
Оценка:
Здравствуйте, Marty, Вы писали:

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


Да, один лишний указатель приходится гонять по стеку. Это лучше, чем в каждом экземпляре хранить 100500 указателей.
Ещё если ты в структуре хранишь Box<dyn MyInderface> (unique_ptr<MyInterface>), то он жрёт вдвое больше.

M>И что мешает присунуть не тот указатель на vtbl?


Если это работает на уровне языка — то у тебя при вот таком:
fn foo(arg: &dyn MyInterface) { // якобы принимает указатель на интерфейс/абстрактный класс с названием MyInterface
...
}
...

let object: MyClass;
foo(&object)

по факту автоматически передастся такая пара:
foo((&object, &vtbl_MyClass_for_MyInterface))

Короче как-то так: https://www.reddit.com/r/rust/comments/8ckfdb/were_fat_pointers_a_good_idea/


Если попытаешься сэмулировать подобное в С++, то я хз, наверное можно как-то извернуться на имплисит конструкторах и шаблонах, чтоб работало более-менее удобно.
Re[13]: v-table vs if call
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 07.12.22 10:39
Оценка:
Здравствуйте, T4r4sB, Вы писали:

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


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

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

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

Так-то в C++, если объект реализует один интерфейс, то и указатель на vtbl будет один, и никаких проблем
Маньяк Робокряк колесит по городу
Re[14]: v-table vs if call
От: T4r4sB Россия  
Дата: 07.12.22 10:47
Оценка:
Здравствуйте, Marty, Вы писали:

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


Посмотри ещё раз на мой пример — зачем что-то хранить в объекте?
Re[15]: v-table vs if call
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 07.12.22 10:51
Оценка:
Здравствуйте, T4r4sB, Вы писали:

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


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


Ну, не в объекте, а вместе с ним. Какая разница?
Маньяк Робокряк колесит по городу
Re[16]: v-table vs if call
От: T4r4sB Россия  
Дата: 07.12.22 10:54
Оценка:
Здравствуйте, Marty, Вы писали:

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


Разница в том, что объект не жиреет от переизбытка интерфейсов.
Указатель на интерфейс собирается в пару с указателем на объект только тогда, когда интерфейс реально становится нужен.
Re[17]: v-table vs if call
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 07.12.22 11:08
Оценка:
Здравствуйте, T4r4sB, Вы писали:

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


TB>Разница в том, что объект не жиреет от переизбытка интерфейсов.

TB>Указатель на интерфейс собирается в пару с указателем на объект только тогда, когда интерфейс реально становится нужен.

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

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

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

TB>>Посмотри ещё раз на мой пример — зачем что-то хранить в объекте?
M>Ну, не в объекте, а вместе с ним. Какая разница?

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

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


Point2d line[100];
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...
Пока на собственное сообщение не было ответов, его можно удалить.