Здравствуйте, Stanislav V. Zudin, Вы писали:
SVZ>Предполагаю, что разница между прямым и косвенным вызовом функции происходит с тех времён.
Не знаю как сейчас, но еще в 00х разница между прямым вызовом и виртуальным была значительная — в разы.
Там обычно не просто косвенность, а пробег по таблицам vtbl для корректировки this если, скажем, наследование множественное.
Подозреваю, компиляторы научились избегать большую часть этого всего, и процессоры предсказывают это гораздо эффективнее.
Такое будет заметно разумеется не на "тысячах вызовов" как пишет стартер, а когда количество вычислений в пересчете на операцию ничтожно, чтото типа i++, при огромном количестве таких операций.
Сейчас модно все вычисления выталкивать в компилятор, так что виртуальный вызов остается самым медленным
Здравствуйте, karbofos42, Вы писали:
K>Вместо прямого обращения к коллекции идёт прослойка в виде итератора, который и создать нужно и его методы дёрнуть.
Жесть
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, karbofos42, Вы писали:
K>И вот теперь вопрос: опытные плюсовики в итоге у себя хардкодят конкретные контейнеры и не парятся о универсальности?
У меня появились сомнения что ты вообще программист.
Контейнер почти однозначно определяется сценарием использования.
Наприсмер, если мне нужно быстро искать элемент в коллекции, то прикинь, у меня будет контейнер поддерживающий поиск, у него по любому будет метод find, и в каком бреду мне понадобится универсальный интерфейс?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, пффф, Вы писали:
П>Я про inserter и говорю. Для вектора там надо end() передать, тогда будет класть в конец. Или begin(), если решишь, что add должен класть в начало. Весь остальной код останется без изменений
Можно даже reverse_iterator и begin(), тогда из вектора можно fifo замутить, вынимая из хвоста
Здравствуйте, andyp, Вы писали:
П>>Я про inserter и говорю. Для вектора там надо end() передать, тогда будет класть в конец. Или begin(), если решишь, что add должен класть в начало. Весь остальной код останется без изменений
A>Можно даже reverse_iterator и begin(), тогда из вектора можно fifo замутить, вынимая из хвоста
Да много чего можно интересного сделать. И главное, что никакой лишней платы в рантайме за это нет
Здравствуйте, пффф, Вы писали:
П>Примеры "промышленных, реально массовых, 8- и 16-разрядных МК", на которых всё сейчас сделано, как вы утверждаете.
Да их хренова гора разных. Из наиболее известных — PIC/AVR, менее известны Holtek, а уж клонов 8051 кто только не выпускал. Вот перечень.
Они реально везде, где есть хоть какое-то "цифровое" поведение, но не требуется серьезных вычислений, обработки больших объемов данных и прочего. Осмотритесь внимательно вокруг — легко насчитаете десяток-другой таких устройств.
П>И что именно вам не нравится в трех разных утверждениях? Я вообще много разных утверждений пишу, гораздо больше трёх
Я, если что, говорил о моих утверждениях, а не Ваших.
П>>>В 8ми битный контроллер с килобайтом памяти нельзя запихнуть какую-то сложную задачу.
П>Если есть десятки килобайт памяти программ, и килобайты ОЗУ — под это вполне можно писать на плюсах
Ну да, я знаю. Но большинство сишников, пишущих под такие контроллеры, и не знающих плюсов, в это не верит.
П>результат, скорее всего, будет компактнее и быстрее.
Если сишный код сам по себе не избыточный, и компилятор тот же, то плюсовый результат не будет ни компактнее, ни быстрее. Просто писать будет удобнее, код будет более читаемым, ошибки делать сложнее, а вылавливать — легче.
П>Только не надо говорить, что все сишечники только такие.
Я и не говорю. Но большинство сишников нынче упертые, потому они и не слезают с сей. Кроме, конечно, частных случаев, когда для платформы есть хороший сишный компилятор, а плюсового или нет, или он плохой.
В конце 80-х, когда TC под DOS летал даже на XT, и давал очень приличный код, а TC++ заметно тормозил даже на AT286, и во ряде случаев давал код хуже, я сам был таким упертым. На BC++ в начале 90-х еще поглядывал с опаской, а потом попробовал VC++ под винду, и сразу перелез туда на плюсы.
ЕМ>>На асме как раз проще, там все под контролем.
П>Только до-о-о-о-о-о-о-о-о-го
Да ладно. На хорошем макроассемблере, если наделать функций и макросов на все случаи жизни, можно вполне комфортно писать даже прикладной софт.
П>Вот на днях читал на хабре какую-то статью, там чел двоичным поиском занимался, оптимизировал. На плюсах, с шаблонами, и прочим. И вполне смотрел выхлоп компилятора. Наверное, тоже бывший сишечник. П>У сишечника, кстати, уверен, будет всё гораздо хуже в подобном случае.
Если "хуже" — это "не так удобно, изящно и надежно" то да, а если "медленнее и/или объемнее", то нет.
П>Так может, тогда не надо ничего категорично утверждать, не имея достоверных сведений о предмете?
А где их взять, достоверные? Ну вот схожу я в десяток разных контор собеседоваться, выложу описание, и что помешает Вам заявить, что я был "не там" и беседовал "не с теми"? Какой материал есть, на том статистику и собираю.
Здравствуйте, Nuzhny, Вы писали:
N>Восемь лишних байт на класс в каждом экземпляре.
Если объектов единицы-десятки миллионов, и в каждом до десятков байт, то можно обойтись и 32-разрядными адресами, это будет по четыре байта. Если в объекте сорок байт, то четыре байта — 10%. Это считается много?
Здравствуйте, Pauel, Вы писали:
P>Не знаю как сейчас, но еще в 00х разница между прямым вызовом и виртуальным была значительная — в разы.
В разы могла быть разница только у самих команд вызова (прямой call по сравнению с загрузкой в регистр адреса vtbl, с последующим косвенным call). Если же сравнивать полную стоимость вызова (загрузку параметров, собственно вызов, создание стекового кадра, сохранение/восстановление регистров, возврат результата), то разница очень сильно уменьшается. Не следует забывать, что все стековые операции — косвенные.
P>Там обычно не просто косвенность, а пробег по таблицам vtbl для корректировки this если, скажем, наследование множественное.
Корректировка this выполняется при любом приведении указателя к производному типу. Сам вызов, что прямой, что виртуальный, выполняется на this конкретного объекта. Ну и множественное наследование — таки частный случай, а не общий.
P>Подозреваю, компиляторы научились избегать большую часть этого всего
Код генерится по сути такой же, как и раньше.
P>Такое будет заметно разумеется не на "тысячах вызовов" как пишет стартер
Про "тысячи" стартер писал в отношении всяких чисто системных косвенных вызовов, без привязки к C++. В отношении виртуальных функций стартер писал о "сотнях тысяч".
P>а когда количество вычислений в пересчете на операцию ничтожно
Не ничтожно, а хотя бы сравнимо.
P>Сейчас модно все вычисления выталкивать в компилятор
И как компилятор вычисляет то, что ему во время компиляции неизвестно?
Здравствуйте, Евгений Музыченко, Вы писали:
П>>Примеры "промышленных, реально массовых, 8- и 16-разрядных МК", на которых всё сейчас сделано, как вы утверждаете.
ЕМ>Да их хренова гора разных. Из наиболее известных — PIC/AVR, менее известны Holtek, а уж клонов 8051 кто только не выпускал. Вот перечень.
Я ждал, я ждал, что ты начнёшь про PIC и MSC-51 (AVR сам не трогал, не знаю, что за шляпа). Я тебе так скажу — те, кто под них пишет — конченные люди, их уже только могила исправит. И пишут они лютое говно.
Я знавал одного сишника/асмщика с PIC — на его код без слез смотреть было нельзя. Под MSC-51 сам писал. И много читал сишного кода. Тоже тоже очень хотелось плакать. Всё это было написано людьми, явно далекими от программирования, и ни о какой эффективности там говорить не приходилось.
MSC-51 вообще лютая штука. 16 бит адреса, и 128 байт оперативы, часть из которой — регистры проца, и стек. Мне ещё повезло — у меня был девайс с переключением страниц, туда до мегабайта этого кала можно было напихать.
Ни о каком промышленном серьёзном применении этого говна речи идти не может. Это уродцы, которые обычно выступают в роли вспомогательных чипов на больших чипах, для их настройки, и предназначено это для электронщиков, которые чутка освоили сишечку, и могут наговнякать управляющую программу для чипа, который они вкорячили в свою схему. Ну и код у всего этого именно такого качества. Конкретно я ковырял MSC-51, который был встроен в видеочип вроде от риалтека, и функционал MSC-51 там был для того, чтобы правильно настроить регистры видеочипа, которых там под тысячу, для различных режимов работы. Это нельзя называть серьёзным промышленным применением, ничего сложного там и не делается. И там нахрен не нужны ни высокие частоты, ни большие объёмы ОЗУ. Хотя, конечно, 128 байт у MSC-51 — это явный перебор, в смысле, недобор. Эти говноконтроллеры используются для возможности этакой кастомизации функционала основного чипа под свои применения. На самом деле, лучше бы, чтобы все уже выкинули это древнее говнецо на помоечку, и вкорячивали бы STMку какую, туда бы прошивали Lua, и для кастомизации нанимали бы студента-хипстера, чем издеваться над бедными электронщиками.
Другое применение этих уродцев — это контроллеры стиральных машин, где оно решает мега сложные задачи по измерению температуры воды и её нагреву (там аж ПИД-регулятор надо делать, это да), да по смене режимов работы, включения мотора барабана и насосов на двухчасовом цикле стирки. Да, тут 5 Мгц частоты точно мало, тут 160 надо.
Ещё применение — выполнять какую-то примитивнейшую функцию, потребляя как можно меньше энергии. Не спорю, полезно иногда, но квалификации тут не нужно никакой от слова вообще.
Я вам по секрету скажу — прошивки к этим устройствам пишут не какие-то супер-эффективные сишники-программисты, прошивки к ним пишут инженеры-электронщики, которые эти устройства ставят в свои схемы. "- Петров, ты схему развел? — Развёл. — Отлично. Петров, у вас же в институте язык C давали? — Давали, один семестр. — Отлично, Петров, вот ты прошивку и напишешь".
ЕМ>Они реально везде, где есть хоть какое-то "цифровое" поведение, но не требуется серьезных вычислений, обработки больших объемов данных и прочего. Осмотритесь внимательно вокруг — легко насчитаете десяток-другой таких устройств.
См выше. Я знаю об этом немного больше, изнутри, чем вы, витающие в своих фантазиях.
П>>Если есть десятки килобайт памяти программ, и килобайты ОЗУ — под это вполне можно писать на плюсах
ЕМ>Ну да, я знаю. Но большинство сишников, пишущих под такие контроллеры, и не знающих плюсов, в это не верит.
А вы много вообще сишников знаете, и ещё и пишущих под контроллеры? Нету таких сишников, как класса. Есть электронщики, осилившие немного сишечку, вот они и пишут эти говно-прошивки.
П>>результат, скорее всего, будет компактнее и быстрее.
ЕМ>Если сишный код сам по себе не избыточный, и компилятор тот же, то плюсовый результат не будет ни компактнее, ни быстрее.
Очень спорное утверждение. У плюсового компилятора гораздо больше информации, которую можно использовать для оптимизации. И плюсовый компилятор умеет многое делать на этапе компиляции, на сишечке это всё будет в рантайме.
ЕМ>Просто писать будет удобнее, код будет более читаемым, ошибки делать сложнее, а вылавливать — легче.
Это вы про сишечку? Сериесли? Вы сами на сишечке много писали? Я, как минимум, написал один HTTP-сервер на сишечке, а что написали на сишечке вы?
П>>Только не надо говорить, что все сишечники только такие.
ЕМ>Я и не говорю. Но большинство сишников нынче упертые, потому они и не слезают с сей. Кроме, конечно, частных случаев, когда для платформы есть хороший сишный компилятор, а плюсового или нет, или он плохой.
Да, упёртые, как глав-пингвин. Это ничего не говорит об их уровне, как программистов, только лишь говорит об их узколобости.
ЕМ>В конце 80-х, когда TC под DOS летал даже на XT, и давал очень приличный код, а TC++ заметно тормозил даже на AT286, и во ряде случаев давал код хуже, я сам был таким упертым. На BC++ в начале 90-х еще поглядывал с опаской, а потом попробовал VC++ под винду, и сразу перелез туда на плюсы.
Что это должно доказать? Что сырой плюсовый компилятор 40 лет назад выдавал код хуже вылизанного сишного? А другие крмпиляторы пробовали? Например, Watcom? Первую версию для плюсов они вроде в 88ом выкатили. Году в 95ом это был самый крутой плюсовый компилятор
ЕМ>>>На асме как раз проще, там все под контролем.
П>>Только до-о-о-о-о-о-о-о-о-го
ЕМ>Да ладно. На хорошем макроассемблере, если наделать функций и макросов на все случаи жизни, можно вполне комфортно писать даже прикладной софт.
Можно. И даже возможно, как-то вполне комфортно. Но до-о-о-о-о-о-о-о-о-лго. Вы так говорите, как будто я на масме никогда ничего не писал. Я могу примерно прикинуть мою производительность на масме, и на плюсиках. Субъективно разница где-то от 1000 до 10000 раз
П>>Вот на днях читал на хабре какую-то статью, там чел двоичным поиском занимался, оптимизировал. На плюсах, с шаблонами, и прочим. И вполне смотрел выхлоп компилятора. Наверное, тоже бывший сишечник. П>>У сишечника, кстати, уверен, будет всё гораздо хуже в подобном случае.
ЕМ>Если "хуже" — это "не так удобно, изящно и надежно" то да, а если "медленнее и/или объемнее", то нет.
Будет неудобно, неизящно, возможно компактнее, но медленее — тут к бабке не ходи. Код среднего плюсовика всегда выиграет у кода среднего сишечника по скорости.
П>>Так может, тогда не надо ничего категорично утверждать, не имея достоверных сведений о предмете?
ЕМ>А где их взять, достоверные? Ну вот схожу я в десяток разных контор собеседоваться, выложу описание, и что помешает Вам заявить, что я был "не там" и беседовал "не с теми"? Какой материал есть, на том статистику и собираю.
Не понял, причем тут собеседования?
Ну и вы для начала сходите пособеседуйтесь, будет хоть какой-то фактический материал, а то насосут из пальца, и приходят тут кванторы всеобщности расставлять
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>C++ тем и хорош, что на нем можно делать "своеобразно и очень быстро", так и "единообразно и менее быстро" (хотя иногда и столь же быстро, оптимизаторы насобачились). Если в него и добавлять еще более единообразные абстракции, то лишь сбоку, чтоб подключались только по запросу.
Единообразно может быть только при условии наличия этого в стандартных библиотеках. В плюсах этого нет, поэтому куча велосипедов.
ЕМ>Не, у ТС сомнений как раз никогда не было. Сомнения у тех, кто никогда не интересовался, как работает механизм виртуальных вызовов, но "где-то слышал, что он сильно снижает быстродействие".
Так тут в итоге все пишут, что виртуальные функции медленные
Здравствуйте, пффф, Вы писали:
П>А сейчас что мешает написать общий код?
Что это нужно делать задом-наперёд, многословно и в итоге я уверен, что большинство этим не занимается, ибо это так "удобно".
П>Да, и это веская причина
Ещё раз: я поэтому данный пример и привёл, ибо смотрим тему.
П>В текущей ситуации вызов size'а будет только один, если контейнер не меняется в ходе работы. В случае виртуального size оптимизатор скорее всего не сможет доказать, что size всегда возвращает одно и то же, и будет честно дёрuать виртуальный size на каждой итерации
Интересный оптимизатор. Определять неизменность состояния объекта умеет, а что у объекта тип внезапно не поменялся — нет.
П>Нормально выглядит. Я не понимаю, что ты докопался до плюсов? Иди в шарповый форум, раз на шарпе пишешь, не надо предлагать переделать плюсики в шарп
Я не предлагаю ничего менять. Наглядно показываю как в плюсах ради экономии на тех же виртуальных вызовах пишут задом-наперёд и радуются.
Опять же смотрим название темы.
Здравствуйте, пффф, Вы писали:
П>Мне коллекции C++ за счет одинаковых интерфейсов тоже видятся удобными.
Так у них нет одинаковых интерфейсов.
Есть частичное совпадение, благодаря которому работает утиная типизация и можно навертеть шаблоны.
В своих классах тоже используете не наследование и виртуальные методы, а просто одинаково называете их и потом через шаблоны вызываете?
П>Не вижу проблем. Более того, такое не слишком нужно. Ну не могут алгоритмы одинаково хорошо работать со всеми типами контейнеров. У каждого контейнера свои преимущества и недостатки, и их надо выбирать, исходя из решаемых задач. А у вас, как я понимаю, дерьмово выходит с любыми контейнерами. Зато — удобно.
У меня то нормально всё получается. И когда кто-то выбирает неправильный контейнер или использование уже существующего меняется, то без проблем меняю тип контейнера и это не приводит к лавинообразным изменениям.
Ну, вот в жизни пишется как мне тут в теме несколько примеров с шаблонами привели?
Или всё же чаще хардкодится конкретный условный std::vector и методы все его принимают, т.к. нафиг надо шаблоны городить?
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ> Но почитал описании истории языка от Страуструпа (я читал несколько его книг, но истории раньше почему-то не попадалось), где он пишет, что многим программистам, в том числе системным, "было трудно поверить в то, что виртуальные функции могут быть достаточно быстрыми".
ЕМ>Откуда вообще могло взяться такое опасение в профессиональной-то среде? Ведь каждый системщик должен знать, что в любой ОС тьма косвенных вызовов
Всегда важно учитывать контекст. А каком компиляторе C++ тогда шла речь,
может о cfront, который просто в C преобразовывал?
Тогда даже банальное создание объекта на стеке
могло вызвать лишние виртуальные вызовы:
ClassWithVirtualDestructor obj;
Сейчас любой С++ компилятор вызовет для "auto" объекта конкретный деструктор,
и обойдется без всяких виртуальный вызовов,
мог ли тогда такую "оптимизацию" сделать cfront?
А сколько еще очевидных оптимизаций делает современный C++ компилятор
по сравнению с cfront?
Здравствуйте, karbofos42, Вы писали:
S>>Нельзя было, в C++ не было общего базового класса Object. S>>На этом все остальные разговоры можно прекращать поскольку они переходят в плоскость если бы у бабушки что-то было...
K>Так я привёл пример наследования шаблонного класса от шаблонного же абстрактного класса.
А я вам указал на то, что кроме лишних затрат это ни к чему хорошему не ведет. Но вы, видимо, чужие аргументы не хотите воспринимать.
K>Технически никто не запрещает на C++ реализовать для коллекций интерфейсы по аналогии с C# или Java.
Тут нужно вспомнить еще пару вещей.
---
Во-первых, в C++ в шаблонах инстанциируются только те методы, которые используются. Т.е. если в конкретной реализации process у контейнера вызываются только begin/end, то генерироваться код будет только для begin/end и все. Что позволит мне для конкретной частной задачи сделать свой контейнер, у которого будут только begin/end и свой тип итератора (и то я могу в качестве итератора использовать голый указатель). И все. Такой мой урезанный контейнер уже будет пригоден для передачи process.
В случае наследования от условного collection<T> мне пришлось бы в своем урезанном контейнере кроме begin/end реализовывать еще и другие методы (size, empty и, может быть, еще что-то). Ну и нафига платить за то, что мне не нужно?
---
Во-вторых, в качестве output-итераторов в process могут быть переданы и не итераторы для контейнеров. Это могут быть итераторы, которые пишут значения в файл, в пайп или в COM-порт. И, благодаря самой концепции итераторов это возможно. А напиши мы process на ваших условных collection<T>, то кроме коллекций туда ничего нельзя было бы передать. И если бы нам потребовалось сохранять результат фильтрации в пайп, то это была бы уже другая функция process.
Причем, замечу, за счет шаблонов мы вообще не имеем никаких накладных расходов, даже если мы оборачиваем в output-итератор объект для работы с COM-портом.
===
Ну и пару слов по поводу того, что в C++ шаблоны базируются на утиной типизации здорового человека (т.е. проверки на наличие нужных методов делаются в compile-time). Главную проблему этой утиной типизации в C++ уже победили в C++20 за счет концептов, которые могут проверить гораздо более формально специфицированный "контракт". Чего не было в C++98 и что вело к простыням ошибок, если у какого-то параметра шаблона не оказывалось нужного метода.
Вот так выглядит пример, который вы хотели видеть, в рамках C++20 с концептами: https://wandbox.org/permlink/Plv2rFZlwbFFgQ1i
Обратите внимание на третий случай -- там в качестве output-итераторов отнюдь не контейнеры. И все равно это работает для process-а.
Здравствуйте, B0FEE664, Вы писали:
ЕМ>>>Но удобство C++ как раз в том, что на нем можно писать всё. S>>Евгений, блин, уже лет 20 как пора забыть про эту чушь.
BFE>Почему?
Экономическая целесообразность.
BFE>Разве есть что-то, что невозможно написать на C++?
Я бы хотел посмотреть на C++ный вариант, скажем, ActiveRecord из Ruby-On-Rails.
Здравствуйте, Евгений Музыченко, Вы писали:
S>>Простите, что сделали в C++11?
ЕМ>Добавили пользовательские литералы. Правда, задачу создания таблиц строк вида "длина+текст" это все равно не решает, приходится извращаться, как и раньше.
Теперь смотрим на вашу исходную фразу:
В сам язык имело смысл изначально завезти кодирование строковых литералов в виде "длина+текст", наравне со стандартным нулем в конце, что в итоге сделали в C++11 для string.
Т.е. вы сперва утверждаете, что кодирование в виде "длина+текст" буквально "в итоге сделали в C++11 для string", а затем, следом, говорите, что "задачу создания таблиц строк вида "длина+текст" это все равно не решает"
Евгений, а вы не просто гуманитарий, а гуманитарий с признаками шизофренического расстройства.
Здравствуйте, karbofos42, Вы писали:
K>Так тут в итоге все пишут, что виртуальные функции медленные
Кто это пишет? Я здесь пока не увидел ни одного бенчмарка.
Хотя как раз ТС должен был бы начать тему с того, что "вот я провел такие-то замеры и они показывают вот такую-то разницу между обычными и виртуальными вызовами, разница настолько мала, что ей можно пренебречь".