Вопрос по c++, exception, c, polymorphism, virtual – Какие вещи (или в каких случаях) могут сделать C ++ медленнее, чем C?

14

Это вопрос интервью, интервью было сделано.

Что может сделать C ++ медленнее, чем C?

Интервьюер спросил это очень глубоко и всегда спрашивал «что-нибудь еще? & Quot; всякий раз, когда я что-то говорил.

Мои идеи:

Возможности C ++, недоступные в C, могут иметь определенную стоимость.

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

Виртуальные функции необходимо вызывать с помощью поиска указателя виртуальной функции. Это накладные расходы.

Есть идеи получше?

Любая помощь будет оценена.

Спасибо !!!

@EtiennedeMartel: Ваш комментарий "там не работает". интригует меня! Почему нет, пожалуйста? (Ваш ответ может помочь мне избежать неудачной ошибки в карьере, поэтому ваше внимание к ней ценится.) thb
Мой ответ будет: «не работай там». Но это не совсем тема, поэтому я оставлю это как комментарий. Etienne de Martel
Пожалуйста, откройте заново. У меня есть большой, действительный ответ на этот вопрос, который отвечает не аргументированным образом, и я не могу представить его, потому что он был закрыт, когда я писал. R..
@thb Приходи кLounge<C++>он будет лучше подходить для такого рода обсуждений. Etienne de Martel
для виртуальных функций не медленнее, чем для c, у c нет понятия, которое можно было бы сравнить. Ближе всего вы получаете указатели на функции, и они не поддерживают половину виртуальных функций без реализации vtable в c. josefx

Ваш Ответ

7   ответов
5

restrictC ++ не поддерживает, хотя большинство компиляторов имеют его как расширение.

Также имеются массивы переменной длины, которых нет в C ++.

@DeadMG: зависит от реализации. На одном с указателем кадра,alloca(n) чисто__asm__ __volatile__ ( "sub %1,%%esp ; mov %%esp, %0" : "=r"(result) : "g"(n)); или эквивалент.
Арена памяти на основе кучи может выделяться быстрее, чемalloca.
6

поэтому я буду немного разглагольствовать как адвокат Дьявола.

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

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

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

Лучший ответ для интервью ... "Когда ваша команда посчитает, что C ++ не является решением проблемы?"

12

std::sort вqsort?

Люди говорят, что виртуальные функции требуют времени для вызова. Они делают. Но то же самое делает эквивалент C поиска в vtable. Если вы напишите эквивалентную логику на обоих языках, версия C ++ будет более удобной в обслуживании, более чистой и быстрой.

Редактировать: О да, вы можете позвонитьprintf из C ++, если хотите, или полностью измените реализацию потока, если хотите.

И я упоминал, что производительность программы, которая падает из-за неуместного NULL-терминатора, довольно несущественна?

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

C ++ этоnot по своей природе быстрее, чем C: C ++ генерики времени компиляции через шаблоны, не обязательно быстрее, чем C исполняющие среды времени через указатели функций, так как хороший оптимизатор может выровнять игровое поле путем клонирования функций (т.е. создания специализированной версии с встроенными виртуальными вызовами); конечно, это возможно, только если функция доступна в блоке перевода: см.news.ycombinator.com/item?id=3717463 о том, как получить C ++ - сопоставимая производительность изqsort() а такжеbitbucket.org/cggaertner/listsort для пользовательского алгоритма сортировки в C, который бьетstd::forward_list::sort()
Я пришел сюда, чтобы дать серьезные ответы, чтобы не участвовать в мелких языковых войнах. Я не сомневаюсь, что с достаточно продвинутым набором инструментов и умным человеком, пишущим код, вы можете создавать быстрые, не раздутые программы на C ++. Но ясно, что большое количество (я бы сказал, подавляющее большинство) программ на C ++ очень медленные и раздутые. Отличным примером является вся работа, направленная на альтернативные реализации malloc, чтобы компенсировать, насколько медленным был KDE (в основном из-за первой проблемы, которую я объяснил в своем ответе).
@nhahtdh люди, жалующиеся на производительность iostream, часто используют это неправильно. Использование std :: endl вместо & quot; \ n & quot; и поддержание синхронизации потоков с cstdio - некоторые из наиболее распространенных ошибок.
Этот ответ просто неправильный. Все, что может быть сделано с помощью шаблонов, может быть сделано с помощью встроенных функций и, возможно, макросов в C. Но более конкретно, OP спросилwhich C++ features tend to make C++ slowerЭто очень хороший вопрос, если вы хотите писать на C ++, но не хотите, чтобы ваш код был стереотипно медленным и раздутым.
Ваш аргумент о "безопасном" и "небезопасно"; это не по теме и отвлекает от вопроса. Вопрос интервью касался не относительной безопасности языков (которая, как правило, преувеличивается; посмотрите все символы в коде JS, PHP и т. Д., Даже если они должны были быть «безопасными» языками без указателей). Это было о скорости.
24

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

Прежде всего, это динамическое распределение памяти. В C вы видите каждый бит динамического выделения памяти, который вы делаете, потому что все это явно (либо в форме обращений кmalloc или вызовы сторонних библиотечных функций, которые возвращают выделенные объекты). В C ++ многие объекты класса, в которых продолжительность хранения объекта является автоматической, будут по-прежнему подвергаться динамическому выделению памяти из-за скрытых выделений, происходящих в их конструкторах. Хорошая реализация C ++ STL (или сторонней библиотеки) может избежать многих из этих затрат, включив небольшие буферы внутри самих объектов и выполняя динамическое распределение, только когда требуется большой буфер, но на практике очень немногие делают это. (Если я не ошибаюсь, libv ++ llvm делает, но libstdc ++ GCC не делает.) Поскольку это проблема качества реализации, которая часто не зависит от вашего собственного кода, главное, что вы можете сделать здесь чтобы минимизировать влияние, следует помнить о возможности того, что автоматические объекты выделяют динамическую память, и избегать создания большего, чем вам нужно (например, с помощью указателей или ссылок, когда это возможно). Это имеет и другие преимущества для вашего кода.

Другая большая область - обработка строк. В идиоматическом C строки строятся одним махом сsnprintf или похожие. В C ++ и многих других языках с более мощными строковыми классами / типами конкатенация (поэлементная конструкция) строк является идиоматической. Это очень неэффективно, что приводит к многочисленным шагам выделения / освобождения, копий и т. Д., Не говоря уже о фрагментации памяти. Я не уверен, что будет включать лучшие практики для C ++ (я не очень хорошо разбираюсь в C ++), но должны быть способы минимизировать это влияние.

И в большинстве случаев, конечно, скрытый код. Это своего рода ловушка для всех. На C ++ легко писать код, в результате чего выполняется много лишнего кода, который вы никогда не видите. Конструкторы / деструкторы, перегруженные операторы и шаблоны являются наиболее очевидными причинами. Опять же, если вы хотите сделать что-тоthe same way в C стоимость будет такой же, но разница в том, что в C выsee стоимость сразу, потому что вы должны написать это самостоятельно.

Большая часть вашего ответа не имеет ничего общего с C ++ и связана с тем фактом, что Стандарт универсален, а не конкретен. Например, вы можете написать строковый класс на основе шаблона выражения, который может работать точно так же, как и свернутый вручную C, с одним шагом выделения, если это необходимо. Если у вас действительно есть проблемы с производительностью со стандартным компонентом, просто выберите тот, который отвечает вашим более конкретным потребностям. Это не какая-то чудо-библиотека, это просто начало и охват большинства областей применения.snprintf и друзья настолько ужасно небезопасны, что на самом деле они не являются честным сравнением.
Нету. Вам только нужно написать одну очистку в своем деструкторе, и тогда не будет иметь значения, почему она была вызвана. Вам не нужно писать его везде, где вы его используете (в отличие от безопасности с snprintf). Более уместно, что целая куча подпрограмм ОС и других вещей, вероятно, не сработает, если у вас закончится виртуальная память, поэтому было бы довольно сложно выполнить какую-либо полезную работу в этом случае, независимо от того, какой код пользовательского режима вы пишете. Исключительная безопасность - это не то, что вы пишете в каждую отдельную функцию, это то, что вы разрабатываете за один раз, а потом это делается.
Все, что вам нужно сделать, этоlook at the interface, Проходитьsizeof(src) вместоsizeof(dest) и вы гарантированно получите неприятную проблему, не говоря уже о том, что вы нарушаете DRY, написав ее миллиард раз. Или написать спецификатор формата, а затем не обновлять его при изменении типа переменной. Или слишком много спецификаторов формата. Или случайно вручную перезаписать NULL-терминатор. Все эти ошибки разрешены интерфейсом и случаются в реальных программах на Си. Тем не менее, этоimpossible сделать эквивалентную ошибку, используя стандартную обработку строк C ++.
Я полностью согласен, что вы можете сделать это; я хочу сказать, что если вы хотите избежать ловушек, делающих вещи дорогостоящими в C ++, вам, возможно, придется это сделать. Что касается ваших замечаний оsnprintfэто уже третий раз, когда вы обвинили обвинения в том, что что-то "небезопасно" без обоснования, и это не по теме и граничит с троллингом.
5

STL как таковой в C ++ редко медленнее, чем специально закодированный эквивалент в C. Однакоconvenience из STL может иногда приводить к написанию более медленного кода. Например, предположим, фиксированный набор из 100 элементов, из которых сделан выбор переменной 10 или 15. Предположим, что цикл, критичный ко времени программы, много раз спрашивает, является ли элементi был выбран. Быстрая структура данных для поддержки такого критичного по времени цикла будет представлять собой массив (или вектор или тому подобное) из 100 бул. Тем не менее, чтобы заполнитьstd::set<size_t> может быть проще кодировать на C ++, чем заполнять массив. По этой причине программист C ++ может предпочесть набор над массивом.

Конечно, проблема в том, является ли более медленный код проблемой, зависит от того, сколько службы увидит цикл, критичный ко времени. Если на программирование метода массива требуется дополнительные полчаса, а общая экономия времени выполнения в течение срока службы программы составляет 0,5 секунды, метод установки, вероятно, предпочтительнее. С другой стороны, если общая экономия времени выполнения составляет 30 дней, тогда метод массива может быть предпочтительным.

Многие аналогичные ответы в этом направлении могут быть даны. Удачи в вашем интервью.

@DeadMG: Вы правы или конечно. Благодарю. я имел в видуstd::set<size_t>, Редактируем ответ сейчас.
@DeadMG Может, он имел в виду мультисет ;-)
Это должно быть отредактировано вvector, ноbitset (STL) - более быстрое решение (возможно, лучше, чем массив символов) для примера задачи.
Вы не можете иметьstd::set<bool> с 100 записями. Есть только 2 значения дляbool, true а такжеfalseТаким образом, самое большее, он может содержать только 2 значения.
5

роблемы в C (например: конструкторы для обеспечения достоверности созданных пакетов данных (struct в с)

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

Конечно, вы всегда можете написать неаккуратные программы, которые «быстрее», но не будут работать правильно во всех случаях.

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

но кодовая база LLVM не использует ни RTTI, ни исключения, потому что они рассматриваютсяслишком дорого с точки зрения размера кода.

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

Похожие вопросы