LINUX.ORG.RU

Производительность C++

 ,


7

13

Как насчёт производительности у C++ по сравнению с C? Мои предположения на текущий момент:

1) Код, не использующий возможности C++ (то есть по сути plain C), скомпилированный C++ компилятором будет иметь абсолютно ту же производительность, что и код на С.

2) Исключения и dynamic_cast медленные. Если нам важна производительность, лучше их не использовать.

3) Класс без виртуальных методов, должен быть по производительности эквивалентен сишной структуре и функциям, обрабатывающим её. Не считая копирования. Нужно везде, где можно использовать передачу по указателю или ссылке (собственно, если в Си делать memmove при передаче структуры в качестве аргумента, то это вызовет примерно такой же оверхед, как дефолтный конструктор копирования С++. Верно?).

4) Класс с виртуальными методами полностью аналогичен пункту 3, но при вызове виртуальных методов добавляется небольшой оверхед. Сишный эквивалент obj->vtable->func(obj, ...). Если сравнивать с plain C кодом, реализующим ООП в той же манере (каждая структура-объект имеет поле, указывающее на структуру, содержащую адреса функций работы с ней), то оверхеда по сравнению с plain C не должно быть.

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

6) Шаблоны могут привести к разбуханию кода. Впрочем, #define-ы и inline-функции в C++ могут устроить то же самое. Вопрос: будет ли использование шаблона с одинаковыми параметрами создавать 2 копии реализации или же всё-таки компилятор догадается сделать её лишь один раз. А если шаблон используется с одинаковыми параметрами в нескольких объектных файлах? Будет ли реализация расшариваться между ними или у каждого своя?

7) Что насчёт inline-методов класса? (те, которые описываются прямо в момент определения самого класса, внутри блока class). Может ли их реализация расшариваться между модулями или в каждом будет своя копия (допустим, метод слишком длинный, чтобы инлайнится в момент вызова)?

Я не претендую на правоту, какие-то утверждения могут быть ложными. Хотел бы узнать, как обстоят дела на самом деле. А также какие подводные камни я ещё не знаю. Разумеется, речь идёт о последних версиях gcc/clang с включённой оптимизацией не ниже -O2.

★★★★★

Последнее исправление: KivApple (всего исправлений: 1)

Еще деструкторы, деструкторы забыли. Ну те, которые будут вызываться у объектов при выходе из области видимости...

А вообще наброс знатный, внушаить. Запасаюсь попкорном.

eao197 ★★★★★
()

Тут недавно обсуждали переход с gcc на g++ (ну как недавно, лет 10 назад) для сборки gcc с целью обеспечения более строгих проверок — эффективность упала на 1/10.

anonymous
()

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

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

8) Если у объекта есть деструктор, то он будет вызван и это добавить оверхед. С другой стороны, если у plain C структуры есть функция destroySomeStructure и по документации её обязательно нужно вызывать, когда структура больше не нужна, то оверхеда по сравнению с таким plain C кодом нет. Верно?

Это не троллинг, меня интересует именно технические особенности.

Проигрывает ли C++ код plain C коду, если в последнем реализовать ООП на структурах с поддержкой виртуальных методов.

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

Я не помню, наверное результирующего кода.

anonymous
()

будет ли использование шаблона с одинаковыми параметрами создавать 2 копии реализации или же всё-таки компилятор догадается сделать её лишь один раз.

Один.

А если шаблон используется с одинаковыми параметрами в нескольких объектных файлах? Будет ли реализация расшариваться между ними или у каждого своя?

Будет расшариваться.

tailgunner ★★★★★
()

Я не спец по С, но с чего вдруг С++ стал медленнее сам по себе?

Имхо: 1) Да.

2) Да.

3) Да.

4) Только если компилятор не смог разобрать ваш код и заменить прямыми вызовами.

5) Разве final не компайлтайм проверка, что наследники не могут переопределять этот метод? В рантайме разницы с обычным virtual быть не должно.

6) Шаблоны генерируются для каждого типа один раз, а не 100500. Шаблоны обычно пишутся в хедерах, и будут общими.

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

RazrFalcon ★★★★★
()

А также какие подводные камни я ещё не знаю.

-Ofast включает -ffast-math, который довольно опасен, если много работы с float/double.

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

8) Да. Сам по себе деструктор не имеет оверхеда.

RazrFalcon ★★★★★
()

Нужно везде, где можно использовать передачу по указателю или ссылке

man RVO, NRVO, C++11

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

Да не нужен никакой final, девиртуализацию в этой ситуации только совсем тупой компилятор не сделает

будет ли использование шаблона с одинаковыми параметрами создавать 2 копии реализации или же всё-таки компилятор догадается сделать её лишь один раз

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

Может ли их реализация расшариваться между модулями или в каждом будет своя копия

Если где-то удалось заинлайнилось, то это естественно не будет расшариваться, все незаинлайнившиеся линкер соберет в одну

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

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

Зря сомневаешься

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

В каких компиляторах?

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

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

Разве final не компайлтайм проверка, что наследники не могут переопределять этот метод?

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

Шаблоны обычно пишутся в хедерах, и будут общими.

Это не должно быть важно. Препроцессор сливает cpp и h в единое целое и компилятор работает уже с этим кодом. Вопрос в том, что если в двух объектных файлах будет использован шаблон с одинаковыми параметрами, то сможет ли линкер оставить в исполняемом файле лишь одну реализацию.

7)

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

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

Это не троллинг, меня интересует именно технические особенности.

Без привязки к конкретной задаче это как раз и выглядит как троллинг. Ибо есть факторы, которые могут склонять чашу весов в ту или иную сторону. Например, конструкторы/деструкторы могут добавлять оверхед по сравнению с C. А шаблоны, напротив, выигрывать за счет генерации кода, оптимального под конкретные структуры данных (Страуструп давно приводит пример, как шаблонный std::sort выигрывает у С-шного qsort). Даже исключения, в зависимости от того, как они используются, могут давать либо выигрыш, либо проигрыш.

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

Далеко не всегда. Если объекты «легкие», то передача их по значению может приводить к увеличению производительности из-за лучшей локальности данных.

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

Однако эта опция точно также повлияет и на plain C код, генерируемый тем же компилятором. Верно? Меня интересуют именно отличия между C и C++.

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

Да не нужен никакой final, девиртуализацию в этой ситуации только совсем тупой компилятор не сделает

Не совсем верно. Если мы получаем указатель/ссылку на объект из функции, чей код компилятору не известен (она в другом модуле), либо это глобальная переменная, то объект всегда может оказаться не того класса (порождённый от нашего) с другими виртуальными методами. Так что в этом случае иного выхода, как обращаться к методам через vtable нет. А вот использование final позволяет компилятору предположить, что этот объект не может быть другого класса, откуда бы он не взялся.

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

Не пойму о каком «другом модуле» идёт речь. И как компилятор о нём не знает.

Ваш пример с final сработает и без этого атрибута.

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

Да.

Сможет ли линкер оставить в исполняемом файле лишь одну реализацию?

Да.

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

Допустим, функция описана прототипом:

SomeClass *myFunc();

Мы делаем что-то вроде:

SomeClass *obj = myFunc();
obj->someVirtualMethod();

При этом реализации myFunc в этом модуле нет (оно отсутствует как в cpp-файле, так и в заголовках). myFunc описана в другом модуле (в другом cpp-файле, который скомпилируют потом). В этом случае компилятор не может делать предположения относительно класса переменной obj. Внутри myFunc вполне может быть что-то вроде:

return new SomeAnotherClass;

(SomeAnotherClass - потомок SomeClass, но с переопределённым методом someVirtualMethod).

Этот случай достаточно част при использовании библиотек.

KivApple ★★★★★
() автор топика

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

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

Ну так и речь о том, что не стоит искать квадратные миллиметры в кубических километрах.

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

отзывчивость айфона на слабеньком железе и объектив-сишке

Айфоны - это единственное, что важно.

tailgunner ★★★★★
()

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

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

Правда отзывчивость айфона на слабеньком железе

это тех самых айфонов, которые не умели в многозадачность?

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

Не могут они это обрабатывать, разве что при LTO и встраивании такой функции.

xaizek ★★★★★
()

1) Код, не использующий возможности C++ (то есть по сути plain C), скомпилированный C++ компилятором будет иметь абсолютно ту же производительность, что и код на С.

В некоторых случаях может и быстрее, если поможет отсутствие стирания типа (касты к void *) и встраивание. Но вообще да, не медленнее.

2) Исключения и dynamic_cast медленные. Если нам важна производительность, лучше их не использовать.

Современные же zero-cost, если исключения нет. А если есть, то какое-то время займут, но немного.

dynamic_cast будет сравнивать строки, что линейно от самой короткой из их длин.

Надо понимать, что эти вещи «медленные» в том смысле, что не такие быстрые как «a = b + c», т.е. относительно базовых операций. Эффект от них есть, но он узким местом не будет, если не злоупотреблять до степени фанатизма.

3) Класс без виртуальных методов, должен быть по производительности эквивалентен сишной структуре и функциям, обрабатывающим её. Не считая копирования.

Зависит он полей, но в целом да.

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

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

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

4) ... Если сравнивать с plain C кодом, реализующим ООП в той же манере, то оверхеда по сравнению с plain C не должно быть.

Оверхеда может и не быть, если компилятор достоверно знает тип переменной (если она локальная, например).

5) При использовании атрибута класса final ...

Да, должно быть так.

6) ... будет ли использование шаблона с одинаковыми параметрами создавать 2 копии реализации или же всё-таки компилятор догадается сделать её лишь один раз.

Он не может гадать так как обрабатывает одну единицу компиляции за раз.

А если шаблон используется с одинаковыми параметрами в нескольких объектных файлах? Будет ли реализация расшариваться между ними или у каждого своя?

Сгенерируется дважды, методы будут weak символами и линкер оставит один.

7) Что насчёт inline-методов класса? ...

Аналогично пункту №6.

xaizek ★★★★★
()

Как насчёт производительности у C++ по сравнению с C?

А насчёт количества реально качественных и годных программ у на Си++ по сравнению с C? :-)

1) Код, не использующий возможности C++ (то есть по сути plain C), скомпилированный C++ компилятором будет иметь абсолютно ту же производительность, что и код на С.

Т.е. цепепе особо и не нужен :-)

2) Исключения и dynamic_cast медленные. Если нам важна производительность, лучше их не использовать.

Люди делали делали всякие там RTTI и возможности строить классовые иерархии, старались, а оно и не нужно особо, ибо «медленно» :-) Хахаха :-)

3) Класс без виртуальных методов, должен быть по производительности эквивалентен сишной структуре и функциям, обрабатывающим её.
4) Класс с виртуальными методами полностью аналогичен пункту 3

Самый большой оверхед и проблема в Си++ - дальнейшее сопровождение программного продукта, его развитие, а не какие-то там структуры и классы с их виртуальными методами в сравнении с Си :-)

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

Хороший компилятор любой вызов o.f() транслирует в банальный f(o), если тип o ему известен :-)

6) Шаблоны могут привести к разбуханию кода. Впрочем, #define-ы и inline-функции в C++ могут устроить то же самое. Вопрос: будет ли использование шаблона с одинаковыми параметрами создавать 2 копии реализации или же всё-таки компилятор догадается сделать её лишь один раз. А если шаблон используется с одинаковыми параметрами в нескольких объектных файлах? Будет ли реализация расшариваться между ними или у каждого своя?

Это задача редактора связей (в простонародье - «линкера») :-)

7) Что насчёт inline-методов класса? (те, которые описываются прямо в момент определения самого класса, внутри блока class). Может ли их реализация расшариваться между модулями или в каждом будет своя копия (допустим, метод слишком длинный, чтобы инлайнится в момент вызова)?

Встраивание по определению подразумевает «интеллектуальную» (т.е. на усмотрение компилятора) подстановку тела функции в место вызова :-)

Я не претендую на правоту, какие-то утверждения могут быть ложными. Хотел бы узнать, как обстоят дела на самом деле. А также какие подводные камни я ещё не знаю.

Подводный камень - стоимость и трудоёмкость создания и сопровождения реально качественного и полезного продукта на Cи++ :-) Думать лучше не о производительности C против C++, ибо нет там разницы особой что с RTTI, что без RTTI, а о своём драгоценном времени :-)

anonymous
()

6 и 7) Это все разводит линковщик, при интанциировании шаблонов (и классов) существует ABI правило какие имена генерируются в объектнов файле (для методов, RTTI и тд) - далие в процессе линковки линкер видит что в объектных файлах A, B, C определены одинаковые символы - поэтому в результирующий elf будет помещена только одна имплементация метода (или шаблона).

Это очень хорошо видно по сумарному размеру obj файлов и слинкованому бинарнику, так как в каждом obj файле есть RTTI таблици всех используемых классов, VTABLE всех используемых классов, и все методы из хедеров (включая используемые шаблоны например std::string). Но при линковке вся дублирующаяся информация убирается как результат исполняемый файл в разы меньше чем все объектники.

zaz ★★★★
()

Мои соображения:
- Идеальный код на С++ и идеальный код на С, при условии функциональной тождественности, будут работать абсолютно одинаково.
- То же, но написанные людьми - на С++ будет быстрее, так как код на С++ дает больше информации компилятору для оптимизаций.
- То же, но заменяем функциональную тождественность на просто одинаковый результат в 99% юз кейсов - на С++ будет чуть медленне, но качественнее, так как в С++ будет больше проверок и вспомогательных механизмов (например, исключения).

Итого, в реальной жизни скорость работы программы на С++ (vs С) будет примерно одинакова или чуть медленней на незначительный процент.

Kroz ★★★★★
()

Код, не использующий возможности C++ (то есть по сути plain C), скомпилированный C++ компилятором будет иметь абсолютно ту же производительность, что и код на С

Где-то читал парадоксальные на первый взгляд результаты тестов что зачастую даже выше. Автор пожимал плечами в недоумении, единственное предложенное разумное объяснение - на компиляторы для С давно забили хер. Ну он конечно формулировал это более корректно, но общий смысл в том что компиляторы С++ ушли намного дальше в плане оптимизации сложного кода и дают несколько более быстрый код. Разница конечно доли %, да.

mbivanyuk ★★★★★
()

Я бы ещё добавил:

8) скорость компиляции

9) размер получаемого бинарника

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

gag ★★★★★
()

1) Код, не использующий возможности C++ (то есть по сути plain C), скомпилированный C++ компилятором будет иметь абсолютно ту же производительность, что и код на С.

Не верно, во-первых потому, что С++ не является си - т.е. конпелятор С++ просто не сконпелирует С++ код.

Си-код, который собирается С++ конпелятором называется С/С++ диалектом без классов. На таком диалекте написано 50% кода на «С++» и на си. Самые яркие признаки это (int *)malloc(), ибо в С++ нет void *, f() - как функция без аргументов, хотя анолог её в С++ это f(...), а аналог крестовой f() в си - это f(void) - хотя это то же поддерживается в С++, но в С/С++ не используется.

Банальный пример f(){static x;}.

2) Исключения и dynamic_cast медленные. Если нам важна производительность, лучше их не использовать.

dynamic_cast не просто медленный - он адски медленный в связи с убогой реализацией type-abi во всех реализациях крестов(на строках).

Исключения медленные не самими исключениями, в идиоме «исключение - исключительная ситуация», а самим exception-safe кодом.

3) Класс без виртуальных методов, должен быть по производительности эквивалентен сишной структуре и функциям, обрабатывающим её.

Не верно - так будет происходить только до тех пор, пока у тебя в классе не появится деструктор.

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

Это имеет мало смысла - все пугалки связанные с копированием - это проблема не самого копирования, а раишного копирования.

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

Опять же - не верно. Структура - это копируемый тип, поэтому он сам копируется в функцию итак, как и в С++. Оверхед - это сама раишная семантика копирования, которое есть не копирование структуры, а копирование данных.

Обычные методы у классов - это просто сихар для f(this, ...) - не более того и разницы нет.

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

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

Сишный эквивалент obj->vtable->func(obj, ...). Если сравнивать с plain C кодом, реализующим ООП в той же манере (каждая структура-объект имеет поле, указывающее на структуру, содержащую адреса функций работы с ней), то оверхеда по сравнению с plain C не должно быть.

Не верно. Реализация на С может быть затюнена, а на С нет.

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

Не знаю никаких документов, которые бы это гарантировали.

6) Шаблоны могут привести к разбуханию кода.

Не могут а приводят, если они не инлайн.

Впрочем, #define-ы и inline-функции в C++ могут устроить то же самое.

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

Т.е. если в си у тебя, допустим, один маллок, а в С++ ты написал шаблонный малок под каждый тип - у тебя будет 100500 инстансов малока, при этом все они будут использоваться постоянно.

https://godbolt.org/g/qN27rE - наглядный пример. Ноинлайн там только потому, что он заинлайнит её - в реализациях подлиннее - он её уже не заинлайнит.

Вопрос: будет ли использование шаблона с одинаковыми параметрами создавать 2 копии реализации или же всё-таки компилятор догадается сделать её лишь один раз.

Интанс для каждого случая создаётся один раз.

А если шаблон используется с одинаковыми параметрами в нескольких объектных файлах? Будет ли реализация расшариваться между ними или у каждого своя?

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

7) Что насчёт inline-методов класса? (те, которые описываются прямо в момент определения самого класса, внутри блока class). Может ли их реализация расшариваться между модулями или в каждом будет своя копия (допустим, метод слишком длинный, чтобы инлайнится в момент вызова)?

Ну по идее все методы глобалы - будет один.

Я не претендую на правоту, какие-то утверждения могут быть ложными.

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

Далее - надо понимать чем отличается С++ от си и от С/С++. С/С++ - это то, о чём говоришь ты и 70% кода на С++ - это код С/С++ - т.е. си с классами, либо просто С++-огрызок си.

Остальные 30% кода - это так называемый современный С++ - где подавляющая часть языка - готовые либы. И все оверхеды связаны с их логикой, а не с самим языком. Тот же раи, аллокатор без реалока и прочее.

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

Не верно. Вернее это не имеет смысла, т.к. во-первых основное ядро в С++ компеляторе - сишное, ибо С++ не привнёс в язык почти ничего, кроме сахара. Т.е. это один и тот же компилятор. Во-вторых в гцц, как в самом сильном компиляторе С/С++ - основной компилятор - это си и никто и никак и никогда на него «забить» не сможет и не захочет.

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

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

- Идеальный код на С++ и идеальный код на С, при условии функциональной тождественности, будут работать абсолютно одинаково.

Не верно. Идеальный код на С++, который будет аналогом кода на си - будет является кастрированным до С/С++ клоном сишного аналога.

Под кодом на С++ подразумевается как минимум С/С++ с классами, а по честному «современный С++». Которые не является конкурентом си ни разу.

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

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

Мне даже лень спорить - я просто спрошу пруфцы. Какой такой «информацией» обладает С++ код, которой не обладает си-код и как она влияет на «оптимизацию».

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

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

Итого, в реальной жизни скорость работы программы на С++ (vs С) будет примерно одинакова или чуть медленней на незначительный процент.

В реальной жизни этому свидетельств нет. Зато есть свидетельства обратного в результатов микробенчмарков/бенчмарков С++ vs C.

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

основное ядро в С++ компеляторе - сишное

Что такое «ядро в С++ компиляторе»? Почему оно «сишное» и откуда ты это всё взял?

в гцц, как в самом сильном компиляторе С/С++ - основной компилятор - это си и никто и никак и никогда на него «забить» не сможет и не захочет

gcc сам на С++ давно переписан, что как-бы намекает )) По крайней мере разработка переведена на С++, так точнее.

те результаты - следствие низкой квалификации автора и ничего более

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

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

И ещё, разбухание потребляемой памяти за счёт шаблонов и инлайн функций — наоборот, благо, ибо оптимизация производительности.

Не верно. Во-первых разбухание за счёт шаблонов никак не влияет на производительность, а только её портит. Разбухание за счёт инлайна сказывается на производительности только до тех пор, пока кол-во горячего кода не перевалит за l1i, поддержание чего в в дефолтных С++ почти невозможно.

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

Что такое «ядро в С++ компиляторе»? Почему оно «сишное» и откуда ты это всё взял?

Компилятор оптимизирует/транслирует внутренне представление кода, а не С++, либо С код. Поэтому не существует отдельно компилятора для С++ и для си - там просто разные фронтенды, которые транслирует и С++ и си код в одном и то же представление.

Причины тому просты и ты, как типичный балабол, проигнорил этот факт:

ибо С++ не привнёс в язык почти ничего, кроме сахара

Ты можешь с этим попытаться поспорить.

gcc сам на С++ давно переписан, что как-бы намекает

Опять же - обычная балаболка-ретранслятор форумных мифов. Никакой гцц и ни на какие С++ никто не переписывал.

Берём последний тарбол, растариваем, идём в gcc - сносим тесты:

gcc-6.1.0/gcc $ cloc ./

Language                             files          blank        comment           code
---------------------------------------------------------------------------------------
C                                      808         276553         288701        1377900
Ada                                   1971         215341         287660         591735
C/C++ Header                           955          58608          63790         225781
C++                                     32           9587           7552          51189

//Чудо - не меньше.


//
$ find . -name '*.cc'
./config/tilepro/gen-mul-tables.cc
./config/sh/sh_optimize_sett_clrt.cc
./config/sh/sh-mem.cc
./config/sh/sh_treg_combine.cc
./cp/constraint.cc
./cp/logic.cc
./go/go-gcc.cc
./go/gofrontend/types.cc
./go/gofrontend/export.cc
./go/gofrontend/expressions.cc
./go/gofrontend/runtime.cc
./go/gofrontend/gogo.cc
./go/gofrontend/unsafe.cc
./go/gofrontend/statements.cc
./go/gofrontend/dataflow.cc
./go/gofrontend/go.cc
./go/gofrontend/import.cc
./go/gofrontend/parse.cc
./go/gofrontend/go-dump.cc
./go/gofrontend/escape.cc
./go/gofrontend/import-archive.cc
./go/gofrontend/ast-dump.cc
./go/gofrontend/go-optimize.cc
./go/gofrontend/lex.cc
./go/go-linemap.cc
./memory-block.cc
./jit/docs/examples/tut04-toyvm/toyvm.cc
./jit/docs/examples/tut01-hello-world.cc
./jit/docs/examples/tut02-square.cc
./jit/docs/examples/tut03-sum-of-squares.cc
./wide-int-print.cc
./wide-int.cc
//да неужели - го, доки и конфиг - т.е. признаков С++ в гцц  не обнаружено.

По крайней мере разработка переведена на С++, так точнее.

Балаболы, они такие балаболы. Переведена-то она переведена, только признаков С++ почему-то не обнаружилось в гцц.

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

Это не важно - важно лишь то, что попроси я у тебя пруфцов - ты их не предоставишь, а если предоставишь - я их помножу на ноль. Но ты всегда можешь попытаться.

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

Не верно. Идеальный код на С++, который будет аналогом кода на си - будет является кастрированным до С/С++ клоном сишного аналога.

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

Мне даже лень спорить - я просто спрошу пруфцы. Какой такой «информацией» обладает С++ код, которой не обладает си-код и как она влияет на «оптимизацию».

Все те же фичи С++. Как будешь раскрывать шаблоны в С? Исключения? Сколько тебе нужно времени для того, чтобы сделать эмуляцию этого всего действительно оптимальной?

Не верное. С++(не С++-диалект си, а именно С++) никогда не даст такой же результат, кроме специфических юзкейсов

Я тебе открою секрет: результат, описанный в ТЗ, можно реализовать на нескольких языках программирования.

Зато есть свидетельства обратного в результатов микробенчмарков/бенчмарков С++ vs C.

Ты правда считаешь результаты синтетических тестов актуальными в данном вопросе?

Kroz ★★★★★
()

2) Исключения и dynamic_cast медленные. Если нам важна производительность, лучше их не использовать.

Неясно зачем в один пункт сгребли совершенно разные вещи. На счёт dynamic_cast всё верно. Про исключения брехня. Скорость обработки исключения роли не играет. Скорость работы good path такая же как и в C, в котором проверяются ошибки. C++ даже быстрей будет, поскольку проверка на исключительную ситуацию делается в вызываемом коде оптимально (через __builtin_expect) и один раз. В C же нужно после каждого вызова делать простыню проверок на каждую ошибку.

Dendy ★★★★★
()

Нужно везде, где можно использовать передачу по указателю или ссылке

А как же move-семантика, T&& и std::move() там всякие?

8. constexpr

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

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

шаблонов это тоже касается

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

Ни в коем случае.

Основание и пруфцы в студию.

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

Это можно уже расценивать как попытку к сливу.

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

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

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

Все те же фичи С++.

Опять же - это можно расценивать только как попытки к сливу.

Как будешь раскрывать шаблоны в С?

Я даже не буду просить ответа на вопрос какая-такая информация для оптимизации содержится в шаблоне, которая не содержится в не шаблоне.

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

Интерфейсе в данном контексте никого не интересуют.

Исключения?

Опять же - какую информацию для оптимизации предоставляют исключения?

Сколько тебе нужно времени для того

А это уже можно и не расценивать как попытки к сливу - это оно и есть. Зачем ты пытаешься взывать к заклинаниям, которые не относятся к контексту? Почему ты не отвечаешь на вопрос с контексте изначального твоего утверждения:

так как код на С++ дает больше информации компилятору для оптимизаций.

Ещё раз - тебе не надо мне выкатывать шаблонные оправдания - тебе надо ответить на конкретные вопрос - «по какой такой причине код на С++ даёт больше информации компилятору для оптимизации?».

чтобы сделать эмуляцию этого

Без доказательств того, что эта эмуляция нужна - взывать к ней не имеет смысла.

Давай попроще. Исключения являются механизмом обработки ошибок - одним из. Он не обладает никакими преимуществами перед иными механизмами. Т.е. они не являются обязательными и твои взывания к необходимости их эмуляции - банальная попытка к сливу.

всего действительно оптимальной?

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

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

Я тебе открою секрет: результат, описанный в ТЗ, можно реализовать на нескольких языках программирования.

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

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

в 99% юз кейсов - на С++ будет чуть медленне

Т.е. ты определил дополнительное условие на приемлемый результат - в 99% случаев всего лишь чуть медленнее Си. Если результат этому условию не соответствует - он не является результатом.

Любые результаты полученные с нарушением этого условия, в частности на иных ЯП(которые по умолчанию условию не соответствуют) ими не ялвяются.

Твоя ответ смысла не имеет.

Ты правда считаешь результаты

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

синтетических тестов актуальными в данном вопросе?

Зачем ты рассуждаешь в тех темах, в которых ты ничего не понимаешь? Ты заучил заклинание, смысла которого не понимаешь - зачем ты мне его ретранслируешь?

Синтетические тесты - это нереальные условия работы при тестировании чего-то. Микробенчмарк же работает в реальных условиях и не является синтетическим тестом.

Давай попроще, чтобы ты понял. Мы берём часть рантайма, допустим to_string() и делаем бенчмарк. В реальном программе to_string() работает с int и в микробенчмарке. Т.е. он делает то же и в тех же условиях.

Естественно - программа не состоит из одного to_string(), либо вообще из него не состоит, но это не важно. Микробенчмарк отвечает только на «какая реализация to_string() быстрее?» и ничего более. Далее собирается информация по всем частям рантайма.

Далее смотрится какую часть во времени исполнения программы занимают эти части рантайма и вычисляет суммарный профит.

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

Да и на основании микробенчмарков можно банальной индукцией вывести то, что а лучше б. Программа состоит из частей, которые измеряются микробенчмарками. Если мы знаем, что реализации от пацанов А в подавляющем большинстве случаев быстрее реализаций пацанов Б, то суммарное время выполнение программы, которая является совокупностью этих частей будет меньше, даже если она состоит из других частей - это верно по той причине, что из этого так же следует то, что пацаны А лучше пацанов Б.

Давай ещё проще. Если я построил дом, баню и сортир лучше, чем вася, то с большей вероятностью я постою 25сараев для пети лучше, чем вася.

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

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

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

Ладно, оставим состоятельность этих древних поверий про затраты на вызов. Разберём мой ответ.

Эффективность оптимизации любой части кода определяется процентом участия этой части в суммарном времени исполнения. Смысл функции в том, чтобы использовать кусок кода из разных мест. Инлайн-функции маленькие(т.е. чтобы чувствовался «оверхед на вызов»).

В конечном итоге получается что-то типа:

f();//инлайн-функция.

while() {/*100500 вызовов всяких функций*/}//очень высока вероятность, что горячая интлайн-функция будет иметь множественный инлайн.
//И так может получится(да и это очень частый сценарий), что горячий блок с 99попугаев, которые вмещались в кеш - стал 101 и уже в кеш не вместился. И уже всё становится не важным на фоне этой проблемы.

шаблонов это тоже касается

Не касается. Это даже показывает то, что ты не смог описать профит, которые дают шаблоны.

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

AnonCxx
()

В C99 есть restrict, а в плюсах его нет. Так что сишечка в некоторых случаях может быть сильно быстрее плюсов.

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

Какое же ты тупое!

Главный смысл inline в том, что все оптимизации, включая constant folding и dce делаются на уровне тела функции. Если константное значение аргумента может удалить до хрена ненужного ветвления в функции, то этого не произойдет пока она не заинлайнена.

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

Берём последний тарбол, растариваем, идём в gcc - сносим тесты:

Эта твоя cloc звиздит как дышит и видать по расширению эти самые языки опознаёт.

Идём почти в любой *.с файл этого вашего GCC, https://gcc.gnu.org/svn/gcc/trunk/gcc/passes.c

Ctrl+F "class"
Ctrl+F "namespace"

GCC восхитителен.

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

Страуструп давно приводит пример, как шаблонный std::sort выигрывает у С-шного qsort

За счет простого инлайна функции сравнения. Вот тебе сишный сорт на макросах, который также инлайнит функции сравнения и работает также быстро.

http://web.mit.edu/dosathena/sandbox/emacs-19.28/lib-src/qsort.c

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

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.