Всю жизнь был уверен, что оценки вроде "виртуальные функции сильно снижают быстродействие" идут от неграмотных гуманитариев, которые где-то слышали звон, а потом рожают на его основе заумные тексты, которые потом цитируют технари, совершенно незнакомые с языком. Но почитал описании истории языка от Страуструпа (я читал несколько его книг, но истории раньше почему-то не попадалось), где он пишет, что многим программистам, в том числе системным, "было трудно поверить в то, что виртуальные функции могут быть достаточно быстрыми".
Откуда вообще могло взяться такое опасение в профессиональной-то среде? Ведь каждый системщик должен знать, что в любой ОС тьма косвенных вызовов, начиная от обработчиков прерываний, точек входа в драйверы ядра, внутренних служб ядра, и заканчивая всякими обработчиками событий и системными услугами "высокого уровня". Все это вызывается до тысяч раз в секунду, и я не помню, чтоб кто-то переживал по поводу самого факта косвенности вызова, ибо затраты на него ничтожны на фоне полезной работы любого кода.
Что эти люди, которые переживали (а некоторые и сейчас переживают, судя по дискуссиям) о "стоимости вызова", собирались делать с помощью виртуальных функций? Если вызывать их сотни тысяч раз в секунду и чаще, то и при обычном прямом вызове такие функции будут сильно тормозить. А если вызывать с разумной частотой, то лишь в очень редких случаях можно обнаружить заметную разницу.
Все эти люди старательно избегали любой косвенности, указатели/ссылки использовали лишь в самых крайних случаях?
Кому-нибудь удавалось заметно снизить быстродействие заменой обычных функций на виртуальные? Ну, кроме случаев совсем уж плохого проектирования.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Кому-нибудь удавалось заметно снизить быстродействие заменой обычных функций на виртуальные? Ну, кроме случаев совсем уж плохого проектирования.
Может на какой-то машине вроде спектрума или realtime микроконтроллеров это возможно, где метод может вызываться много раз за цикл. На современных машинах об этом даже задумываться нет смысла, лучше устранять другие ботлнеки. Более того, большинство ЯП идут по умолчанию с виртуальными ф-циями ON.
Кстати, почему они называются виртуальными? Дурацкое название.
Страуструп придумал?
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Всю жизнь был уверен, что оценки вроде "виртуальные функции сильно снижают быстродействие" идут от неграмотных гуманитариев, которые где-то слышали звон, а потом рожают на его основе заумные тексты, которые потом цитируют технари, совершенно незнакомые с языком...
ЕМ>Кому-нибудь удавалось заметно снизить быстродействие заменой обычных функций на виртуальные? Ну, кроме случаев совсем уж плохого проектирования.
На борландовском компиляторе (ещё в ДОСе) обращение к статическим переменным было на порядок быстрее, чем к мемберам класса.
В Ваткомовском компиляторе такой проблемы уже не было.
Предполагаю, что разница между прямым и косвенным вызовом функции происходит с тех времён.
Ну и да, в каких-то задачах косвенный вызов может стоить дополнительных тактов и может дать некоторое замедление — лишние микросекунды. Для сурового риалтайма может быть важным.
Мы в своих задачах таким не заморачивались — все ускорения достигались за счёт алгоритмов.
_____________________
С уважением,
Stanislav V. Zudin
Здравствуйте, opfor, Вы писали:
O>Может на какой-то машине вроде спектрума или realtime микроконтроллеров это возможно, где метод может вызываться много раз за цикл.
Так то же самое получится при активном использовании любой косвенности. Теми же ссылками народ обычно пользуется, не глядя, но на косвенные вызовы почему-то сразу настораживается.
O>Кстати, почему они называются виртуальными? Дурацкое название.
Вы в курсе, что virtual — это "действительный", "фактический"?
Здравствуйте, Stanislav V. Zudin, Вы писали:
SVZ>На борландовском компиляторе (ещё в ДОСе) обращение к статическим переменным было на порядок быстрее, чем к мемберам класса.
Не помню такого. Может, там где-то одна лишняя команда и затесывалась, но чтоб на порядок — не было.
Ну и если тормозила вообще вся адресация членов класса, то вой должен был стоять до небес, но вообще ни разу не помню такого.
SVZ>В Ваткомовском компиляторе такой проблемы уже не было.
SVZ>в каких-то задачах косвенный вызов может стоить дополнительных тактов и может дать некоторое замедление — лишние микросекунды. Для сурового риалтайма может быть важным.
В те времена, когда одно-два обращения к памяти занимали микросекунды, "суровый реалтайм" даже не помышляли писать даже на C, не говоря уже о C++.
SVZ>Мы в своих задачах таким не заморачивались — все ускорения достигались за счёт алгоритмов.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Вы в курсе, что virtual — это "действительный", "фактический"?
Нет, не в курсе:
Virtual:
Existing in the mind, especially as a product of the imagination
Existing or resulting in essence or effect though not in actual fact, form, or name
Created, simulated, or carried on by means of a computer or computer network
Здравствуйте, Евгений Музыченко, Вы писали:
SVZ>>На борландовском компиляторе (ещё в ДОСе) обращение к статическим переменным было на порядок быстрее, чем к мемберам класса.
ЕМ>Не помню такого. Может, там где-то одна лишняя команда и затесывалась, но чтоб на порядок — не было.
Подробностей уже не помню, кажется, компилятор делал вычисление адреса мембера класса в рантайме в лоб — с несколькими уровнями косвенности, а со статическими переменными обращался напрямую по адресу.
ЕМ>Ну и если тормозила вообще вся адресация членов класса, то вой должен был стоять до небес, но вообще ни разу не помню такого.
Версию компилятора уже не помню. Не исключено, что в последующих версиях этот косяк был поправлен, но, код уже написан, я пришел в тот проект в 97г, в 98 мы портировали его на винду. И, кажется, он до сих пор используется в неизменном виде. 💪
SVZ>>в каких-то задачах косвенный вызов может стоить дополнительных тактов и может дать некоторое замедление — лишние микросекунды. Для сурового риалтайма может быть важным.
ЕМ>В те времена, когда одно-два обращения к памяти занимали микросекунды, "суровый реалтайм" даже не помышляли писать даже на C, не говоря уже о C++.
Я уже говорю про сейчас — какой-нибудь HFT, передача по сети и т.п.
_____________________
С уважением,
Stanislav V. Zudin
Здравствуйте, opfor, Вы писали:
o> ЕМ>Вы в курсе, что virtual — это "действительный", "фактический"? o> Нет, не в курсе:
Синоним "почти", например в рекламе часто попадается virtually indestructible.
virtual: almost a particular thing or quality
She suffered a virtual breakdown when her marriage broke up.
Сотни тысяч раз можно и функцию на питоне вызывать. Наверное те, кто переживает за виртуальные функции, собираются их вызывать хотя бы миллиард раз в секунду.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Все эти люди старательно избегали любой косвенности, указатели/ссылки использовали лишь в самых крайних случаях?
Беседовал когда-то с челом из HFT, так его беспокоили не сами вызовы виртуальных функций, а наличие таблицы виртуальных функций, которая увеличивала размер класса. Ну и массовое создание/удаление множества мелких объектов без виртуальности происходило заметно быстрее. Сериализация — вот это всё.
Я в детали не вдавался, это было на какой-то конференции по С++, поэтому на дополнительные вопросы ответить не смогу.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Откуда вообще могло взяться такое опасение в профессиональной-то среде?
Можно у разработчиков STL спросить почему они не сделали абстракции в виде Collection/ICollection как в библиотеках той же Java или C#.
Удобно ведь было бы в метод принимать просто коллекцию, получать какой-то абстрактный итератор для перебора или через виртуальный метод add добавить пару элементов.
Вместо этого приходится писать методы конкретно под std::vector и никакой std::set не передашь.
Здравствуйте, karbofos42, Вы писали:
K>Можно у разработчиков STL спросить почему они не сделали абстракции в виде Collection/ICollection как в библиотеках той же Java или C#.
Вроде бы об этом Страуструп в своей книге "Дизайн и эволюция" писал. Как и об отсутствии единого базового класса Object, как в во многих ООЯП.
K>Вместо этого приходится писать методы конкретно под std::vector и никакой std::set не передашь.
Это какой-то странный код, если ему фиолетово, используется ли std::vector или std::set.
Здравствуйте, Stanislav V. Zudin, Вы писали:
SVZ>кажется, компилятор делал вычисление адреса мембера класса в рантайме в лоб — с несколькими уровнями косвенности
class If {
public:
virtual void f1 () = 0;
virtual void f2 () = 0;
};
void f (If & i) {
i.f1 ();
i.f2 ();
}
TC++ 1.0:
@f$qr2If proc near
push bp
mov bp,sp
push si
mov si,word ptr [bp+4]
;
;
; i.f1 ();
;
push si
mov bx,word ptr [si]
call word ptr [bx]
pop cx
;
; i.f2 ();
;
push si
mov bx,word ptr [si]
call word ptr [bx+2]
pop cx
;
;
; }
;
pop si
pop bp
ret
@f$qr2If endp
Я тут вижу только лишние push/pop, но получить из-за них сколько-нибудь ощутимое снижение быстродействия нужно очень-очень постараться.
ЕМ>>В те времена, когда одно-два обращения к памяти занимали микросекунды, "суровый реалтайм" даже не помышляли писать даже на C, не говоря уже о C++.
SVZ>Я уже говорю про сейчас — какой-нибудь HFT, передача по сети и т.п.
А сейчас — тем более, десяток-другой лишних обращений к памяти — ничто по сравнению с затратами на основную обработку данных и временем ожидания ответа устройств.
Здравствуйте, Nuzhny, Вы писали:
N>Беседовал когда-то с челом из HFT, так его беспокоили не сами вызовы виртуальных функций, а наличие таблицы виртуальных функций, которая увеличивала размер класса.
Они использовали хотя бы миллионы разных классов? Таблица-то общая на весь класс, а в каждом объекте добавляется только ее адрес. Чтобы получить заметное (в нынешних масштабах) увеличение расходов, они должны создавать хотя бы миллионы объектов очень маленького размера (десяток-другой байт), иначе этот прирост теряется на фоне размера самого объекта.
N>Ну и массовое создание/удаление множества мелких объектов без виртуальности происходило заметно быстрее. Сериализация — вот это всё.
Тоже странно. Такое ощущение, что было как-то неправильно спроектировано.
Здравствуйте, karbofos42, Вы писали:
K>Вместо этого приходится писать методы конкретно под std::vector и никакой std::set не передашь.
Насколько я понимаю, они делали простые контейнеры вроде vector/list так, чтобы после оптимизации получался предельно эффективный код, не уступающий коду со встроенными массивами и ручными списками. В принципе, это правильно, иначе нареканий на STL было бы гораздо больше.
Здравствуйте, Евгений Музыченко, Вы писали:
N>>Ну и массовое создание/удаление множества мелких объектов без виртуальности происходило заметно быстрее. Сериализация — вот это всё.
ЕМ>Тоже странно. Такое ощущение, что было как-то неправильно спроектировано.
Ну так в HFT же криворукие идиоты работают, половину которых набрали по объявлению, а вторую половину -- по блату.
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ> N>Беседовал когда-то с челом из HFT, так его беспокоили не сами вызовы виртуальных функций, а наличие таблицы виртуальных функций, которая увеличивала размер класса. ЕМ> Они использовали хотя бы миллионы разных классов? Таблица-то общая на весь класс, а в каждом объекте добавляется только ее адрес. Чтобы получить заметное (в нынешних масштабах) увеличение расходов, они должны создавать хотя бы миллионы объектов очень маленького размера (десяток-другой байт), иначе этот прирост теряется на фоне размера самого объекта.
Так примерно оно и есть. Какой-нибудь там Order будет i64 цена, i64 количество, i16 currency pair, и неск байт всяких атрибутов. И в памяти лежат миллионы таких.
Здравствуйте, so5team, Вы писали:
s> Ну так в HFT же криворукие идиоты работают, половину которых набрали по объявлению, а вторую половину -- по блату.
Иди туда, спроектируй всё по правильному — заработаешь миллионы и получишь мировую известность.
Здравствуйте, ·, Вы писали:
s>> Ну так в HFT же криворукие идиоты работают, половину которых набрали по объявлению, а вторую половину -- по блату. ·>Иди туда, спроектируй всё по правильному — заработаешь миллионы и получишь мировую известность.
Не-не-не, у меня с сарказмом все нормально, в отличии от.