LINUX.ORG.RU

Почему программы на с++ тормозят :)

 ,


1

3

https://www.computerenhance.com/p/welcome-to-the-performance-aware

ах, наконец-то кто-то заметил слона в посудной лавке :-)

Видео, 22 минуты https://m.youtube.com/watch?v=tD5NrevFtbU

Заменяем крутой полиморфизм на тупой свитч - получаем 1.5 ускорения :) Я так понял конечно тут еще компилятор виновен, может ему можно как-то явно указать кто и куда морфирует в данной программе .. но результат пока (под вин, судя по notepad++ и оформлению окон) явно не в пользу красивого программирования.

В тред приглашаются программисты со своими (анти)примерами :)

едит: исправил ссылку на видео

★★★★★

Последнее исправление: Andrew-R (всего исправлений: 1)
Ответ на: комментарий от Kogrom

Если надо поддерживать большой проект, то найти ошибку в «конкретном» коде обычно проще

Вспомнил случай из практики про поддержку кода с полиморфизмом. Был абстрактный класс с двумя наследниками и кучей методов. И при навигации по коду в IDE сталкивался с проблемой при переходе и сверху вниз и при переходе снизу вверх. Если идёшь сверху, то упираешься в пустой виртуальный метод. Если идёшь снизу, то упираешься в то, что нет ссылок на использование метода с реализацией.

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

Kogrom
()
Ответ на: комментарий от Kogrom

Если идёшь снизу, то упираешься в то, что нет ссылок на использование метода с реализацией.

так это правильно. статически нельзя определить в общем случае, какой актуальный метод будет в данном случае использован. потому абстрактному методу ставят заглушку с ексепшеном, и ты туда при исполнении упадешь если что не так.

а если ты что-то там придумал, что можно и статически это понять, значит ты используешь не виртуальные методы.

alysnix ★★★
()
Ответ на: комментарий от Enthusiast

с++ транслируется в си, и потому говорить что он «тормозней» - это наводить тень на плетень. как он может быть тормозней, если будучи транслированным в си, ты уже не отличишь его от си?

alysnix ★★★
()
Ответ на: комментарий от alysnix

так это правильно

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

Роберт Мартин в своей книжке говорит только о преимуществах полиморфизма. О недостатках умалчивает. Вероятно, пишет для разработчиков, которые сдали проект и забыли. Не им же поддерживать все эти иерархии абстрактных классов.

Kogrom
()
Ответ на: комментарий от Kogrom

БОльшая сложность для понимания - типичная черта крестовых проектов (сколько неудобств от одной лишь перегрузки), хотя, наверное, чистую сишку просто берут в болле простых случаях, поэтому так кажется. Вспоминаю как пытался въехать в гцц через анализ исходников - это ппц, макрос на макросе с указателя куда-то в небо. Честно говоря, я его тогда так и не осилил и забил. С другой стороны шланг - хорошенько обмазан всей этой высокоуровщеной, но понимать его много легче.
Вообще вопрос к тулингу и навыкам, к ide, которая толком кресты не умеет, а без неё юзер не научился.

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 3)
Ответ на: комментарий от Kogrom

IDE сталкивался с проблемой при переходе и сверху вниз и при переходе снизу вверх. Если идёшь сверху, то упираешься в пустой виртуальный метод

В Qt Creator при клике на вызове виртуального метода, который ииеет несколько вариантов определений, отображается выпадающей список с доступнымм определениями. Или ты не про это?

rumgot ★★★★★
()
Ответ на: комментарий от Kogrom

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

в чем мучения, я так и не понял. какая конкретно вызывается функция смотрится отладчиком, а не статически, ровно так же как значения переменной смотрятся отладчиком в рантайме, если это не константа.

не должны быть «связи между частями». чем меньше внешний код знает об особенностях твоего кода, тем лучше. идеально, если он не знает вообще ничего.

alysnix ★★★
()
Ответ на: комментарий от Forum0888

О какой реализации C++ речь?

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

alysnix ★★★
()
Ответ на: комментарий от rumgot

при клике на вызове виртуального метода, который имеет несколько вариантов определений, отображается выпадающей список

Ок. Что-то такое и в студии вроде бы есть сейчас. То есть сверху вниз спуститься можно. А снизу вверх?

Kogrom
()
Ответ на: комментарий от Kogrom

А снизу вверх?

в том и смысл ВИРТУАЛЬНОСТИ, что в реальности вы не знаете, что будет вызвано. вы лишь знаете, что вы вызовете функцию именно с такой декларацией и вызов будет корректен.

для чего это надо? а чтобы совершенно абстрагироваться от конкретики и не забивать себе голову лишними смыслами. чтобы покончив со смыслами нижнего уровня, перейти к смыслам следующей ступени и не болеть головой. это же здорово!

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

чем меньше внешний код знает об особенностях твоего кода, тем лучше. идеально, если он не знает вообще ничего.

При чём тут внешний код? IDE имеет право знать об особенностях кода? Я сам имею право знать об особенностях кода?

Анализировать код отладчиком можно, конечно. Хороший аргумент. Но это никак не отменяет навигацию по коду без отладки. Тем более, что в отладчике мы смотрим какой-то один сценарий, если не переписываем код.

Kogrom
()
Ответ на: комментарий от Kogrom

При чём тут внешний код? IDE имеет право знать об особенностях кода? Я сам имею право знать об особенностях кода?

ide максимум что может знать - статические параметры твоего кода. она совершенно не может знать откуда вообще взялся вот этот обьект и какой конкретно виртуальный метод он с собой притащил. Например, если этот обьект пришел из dll, со своей реализацией данного метода. Откуда ide знать, что ты грузишь dll и как ты ее грузишь?

или откуда ide знать, какой адрес реально лежит в функциональной переменной? где ты его присвоил и как, а может вообще не присвоил - это чистый рантайм.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

Объясняю. Например, есть большой проект, весь из себя абстрактный, а документации минимум или её вообще нет. Заказчик хочет новую фичу. Весь код перелопачивать времени нет. Ищешь нужное место по ключевым словам. Нашёл на низком уровне. Как подняться? Через отладчик? А если отладчик до этого низкого уровня не доходит, идёт по другой ветке?

Kogrom
()
Ответ на: комментарий от Kogrom

Как подняться?

поднимаются в данном случае по иерархии классов. она статична и показывает, как потомок доопределяет или переопределяет предка. и какие классы как друг с другом совместимы. или несовместимы.

alysnix ★★★
()
Ответ на: комментарий от Kogrom

Например, есть большой проект, весь из себя абстрактный, а документации минимум или её вообще нет.

вот тут собака и порылась. приложили немеряно усилий для написания кода, не пойми какого качества и вовсе проигнорировали вопрос сопровождения данного кода. а потом пришел чел, и не может в нем разобраться… это вопрос культуры разработки

alysnix ★★★
()
Ответ на: комментарий от alysnix

Ну вот скачи по всей иерархии. А потом смотри на миллион ссылок с верхнего уровня на этот абстрактный класс и думай, какая их них приведёт к нужному наследнику.

Опять же, я не говорю, что такая архитектура - абсолютное зло. Но если можно упростить - лучше упростить.

Kogrom
()
Ответ на: комментарий от alysnix

Ну да. Только я же в реальном мире живу. А не в том, который придумали писатели книг про ООП. Может у ПО крупных банков и корпораций как-то по другому (в чём сомневаюсь). Но разработка ПО не ограничивается только этими областями.

Kogrom
()
Ответ на: комментарий от rumgot

Если писать низкоуровнево, на указателях и т.п., то с чего бы плюсам быть тоормознее C?

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

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

  • Почему товар грязный? - недоумевает покупатель.
  • Да потому что продавцу сегодня некогда было руки помыть после хождения в туалет «по-большому» и туалетная бумага внезапно закончилась! - отвечает кассир.

Изначально грязнуль не нужно привлекать к работам, где требуется чистота.

Enthusiast ★★★
()
Ответ на: комментарий от alysnix

с++ транслируется в си, и потому говорить что он «тормозней» - это наводить тень на плетень. как он может быть тормозней, если будучи транслированным в си, ты уже не отличишь его от си?

А ты уверен в том, что ассемблерный выхлоп из обеих программ получился одинаковый? Да и процессор по-разному отрабатывает ветвления по значению и по адресу в зависимости от работы внутреннего предсказателя ветвлений. Итог вычислений показан наглядно на видео. О чём спор?

Enthusiast ★★★
()
Ответ на: комментарий от Enthusiast

А ты уверен в том, что ассемблерный выхлоп из обеих программ получился одинаковый?

когда говорят - «писать на си», никто ж не говорит, что там не будут использовать указатели на функции. или в си нет таких указателей?

абстракции через виртуальные функции можно и на Си написать, и выхлоп будет тем же. а если на Си писать примитивно, то аналогично примитивно можно и на плюсах написать. То есть дело не в языках, а в методах. Значит утверждение, что с++ медленней си - неверно.

alysnix ★★★
()
Ответ на: комментарий от Enthusiast

если Джава выдаст более устойчивоработающий код при одинаковом уровне программистов?

) тоже мне эталон нашёл. Как только вижу какую-нибудь скриптуху, так сразу воротит. Из последнего - qubeMx, который конкретно подвешывает систему при долгом простое. Скриптуха - клеймо, проект на ней - приговор в долгосроке.

kvpfs ★★
()
Ответ на: комментарий от rumgot

Это как?

Кликаешь по методу (функции, переменной) и выбираешь «Найти все ссылки» (Find all references). Приводит все строки кода, откуда вызывается метод. Затем переходишь по ссылкам вверх.

Такой функционал почти во всех IDE есть.

Kogrom
()
Ответ на: комментарий от Enthusiast

Ну это не проблема языка. Никто не утверждает, что высокоуровневые контейнеры быстрее низкоуровневых. Но и не нужно врать, что при желании на C++ нельзя писать быстрые низкоуровневые конструкции.

rumgot ★★★★★
()
Ответ на: комментарий от rumgot

Не то. Есть «Перейти к определению (реализации)». Это вниз. А есть перейти от реализации к ссылкам (вызовам). Кстати, в последних студиях прямо над любым методом мелким шрифтом пишется число ссылок. Можно кликнуть по этому числу и приведёт список ссылок. По крайней мере для C# так.

То есть для простейшего конкретного примера, если из main() переходим в func(), то это вниз, а если из func() в main(), то это вверх.

Kogrom
()
Ответ на: комментарий от fluorite

Компиляторы давно научились нормально инлайнить лямбды. Только надо научиться жить с -Og -g для Debug.

Может я что-то делаю не так, но наивный вариант выдаёт:

-------------------------------------------------------------------------------
Benchmark                     Time             CPU   Iterations UserCounters...
-------------------------------------------------------------------------------
BM_inheritance/100          233 ns          233 ns      3003971 items_per_second=429.984M/s
BM_inheritance/128          299 ns          299 ns      2338023 items_per_second=427.841M/s
BM_inheritance/256          748 ns          748 ns       880067 items_per_second=342.3M/s
BM_inheritance/512         2356 ns         2356 ns       296524 items_per_second=217.351M/s
BM_inheritance/1024        6549 ns         6549 ns       106724 items_per_second=156.356M/s
BM_inheritance/2048       15020 ns        15020 ns        45862 items_per_second=136.352M/s
BM_inheritance/4096       30614 ns        30614 ns        22878 items_per_second=133.797M/s
BM_inheritance/8192       61469 ns        61468 ns        11184 items_per_second=133.272M/s
BM_inheritance/10000      75835 ns        75832 ns         9142 items_per_second=131.871M/s
BM_variant/100              111 ns          111 ns      6308300 items_per_second=901.749M/s
BM_variant/128              141 ns          141 ns      4973921 items_per_second=910.297M/s
BM_variant/256              275 ns          275 ns      2539702 items_per_second=930.479M/s
BM_variant/512              546 ns          546 ns      1262741 items_per_second=936.978M/s
BM_variant/1024            1109 ns         1109 ns       621225 items_per_second=923.277M/s
BM_variant/2048            2286 ns         2286 ns       301701 items_per_second=895.899M/s
BM_variant/4096           10727 ns        10726 ns        64448 items_per_second=381.871M/s
BM_variant/8192           39067 ns        39066 ns        17992 items_per_second=209.697M/s
BM_variant/10000          52776 ns        52773 ns        13481 items_per_second=189.491M/s
BM_switch/100               102 ns          102 ns      6806888 items_per_second=982.463M/s
BM_switch/128               132 ns          132 ns      5248501 items_per_second=971.257M/s
BM_switch/256               268 ns          268 ns      2568049 items_per_second=954.267M/s
BM_switch/512               552 ns          552 ns      1262749 items_per_second=928.081M/s
BM_switch/1024             1154 ns         1154 ns       607073 items_per_second=887.058M/s
BM_switch/2048             2296 ns         2296 ns       301153 items_per_second=892.15M/s
BM_switch/4096             9852 ns         9852 ns        71336 items_per_second=415.744M/s
BM_switch/8192            34041 ns        34040 ns        20683 items_per_second=240.655M/s
BM_switch/10000           46348 ns        46347 ns        15110 items_per_second=215.764M/s

AlexVR ★★★★★
()
Ответ на: комментарий от AlexVR

Компиляторы давно научились нормально инлайнить лямбды

Вообще-то лямбда ничем, кроме синтаксиса, не отличается от функтора (класса с operator ()). А вызовы функций компиляторы научились инлайнить очень давно.

annulen ★★★★★
()
Ответ на: комментарий от annulen

А еще лучше с -O2 -g, если интересует производительность

В Debug интересует возможность дебажить.

-Og

Optimize debugging experience. -Og should be the optimization level of choice for the standard edit-compile-debug cycle, offering a reasonable level of optimization while maintaining fast compilation and a good debugging experience. It is a better choice than -O0 for producing debuggable code because some compiler passes that collect debug information are disabled at -O0.

Like -O0, -Og completely disables a number of optimization passes so that individual options controlling them have no effect. Otherwise -Og enables all -O1 optimization flags except for those that may interfere with debugging:

AlexVR ★★★★★
()
Ответ на: комментарий от annulen

Вообще-то лямбда ничем, кроме синтаксиса, не отличается от функтора

Я больше к тому, что в данном случае, наивный вариант:

using Shape = std::variant<Square, Rectangle, Triangle, Circle>;

float area(const Shape &shape)
{
    return std::visit([](const auto &s) -> float { return s.area(); }, shape);
}

float sum_area(const std::vector<Shape> &shapes)
{
    float sum = 0;
    for (const auto &shape : shapes) {
        sum += area(shape);
    }
    return sum;
}

Вполне достаточно оптимизируется. А при виде вызываемых методов «фигур», так и вообще не будет содержать call.

AlexVR ★★★★★
()
Ответ на: комментарий от AlexVR

Тут задействована «магия» std::variant, обеспечивающего статический полиморфизм малой кровью. А у автора код на старом С++, и полиморфизм только динамический рассматривается

annulen ★★★★★
()
Последнее исправление: annulen (всего исправлений: 1)
Ответ на: комментарий от annulen

Брейкпойнты на функциях и с -O2/-O3 работают. И бэктрейс в любой момент можно получить

Развёртывание циклов, векторизация, перестановка операций и т.п. могут доставлять неизгладимые ощущения.

AlexVR ★★★★★
()
Ответ на: комментарий от AlexVR

Мораль басни, наверное, можно сформулировать так:

  1. Если писать на C++ как на Java, получится плохая Java
  2. Не надо писать вычислительный код на C++ в ООП-стиле
annulen ★★★★★
()

Прежде чем закапываться в оптимизации нужно ответить на вопрос, для кого пишутся программы на языках высокого уровня если компы обходятся битами и байтами?

Ответ: для кожаных уб людей работающих с разрабатываемым кодом. Назначение софта это решение прикладных задач, а не тешить самолюбие олипмиадников. На длительной дистанции код написанный просто приносит больше пользы потому что его проще модифицировать и тем самым переиспользовать. Time to market имеет значение.

Ну а фанаты байтослесарьства могут поднять себе настроение сей музыкальной композицией.

dvetutnev
()
Ответ на: комментарий от hobbit

в больших проектах на классическом Си не так уж редко делают тот же полиморфизм, руками создавая структуры с указателями на коллбэки

Ядро онтопика тому яркий пример. ООП тотальный.

hatred ★★★
()