Пишете в C для производительности? [закрыто]

32

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

Я планировал написать библиотеку с некоторым кодом для визуализации в OpenGL, чтобы я мог использовать его повторно. Я планировал написать библиотеку на C, так как любое повышение производительности приветствуется, когда дело доходит до графики.

Но стоит ли это того? Код, использующий библиотеку, вероятно, будет написан на C ++, и я предпочитаю кодировать на C ++ в целом.

Однако, если это приведет к небольшой разнице в производительности, я, скорее всего, остановлюсь на C.

Можно также отметить, что эту библиотеку я бы использовал для работы в Windows / OS X / Linux, и я, скорее всего, скомпилирую все изначально (MSVC для Windows, Clang или GCC для OS X и GCC для Linux). или, возможно, компиляторы Intel для всего).

Я посмотрел вокруг и нашел несколько тестов и тому подобное, но все, что я видел, касалось GCC, а не MSVC и Clang. Кроме того, в тестах не упоминаются стандарты используемых языков. У кого-нибудь есть мысли по этому поводу?

РЕДАКТИРОВАТЬ:Я просто хотел поделиться своей точкой зрения на этот вопрос после пары лет опыта. Я закончил тем, что написал проект, для которого я задавал этот вопрос на C ++. Примерно в то же время я начал другой проект в C, так как мы стремились добиться какой-то небольшой производительности, которую мы могли бы и хотели, чтобы проект был связываемым в C. Пару месяцев назад я достиг той точки, когда мне действительно нужны карты и расширенные возможности. манипуляции со струнами. Я знал о возможностях для этого в стандартной библиотеке C ++ и в итоге пришел к выводу, что эти структуры в стандартной библиотеке, вероятно, будут превосходить и быть более стабильными, чем карты и строки, которые я мог бы реализовать в C за разумное количество времени. Требование быть связываемым в C было легко выполнено путем написания интерфейса C для кода C ++, что было быстро выполнено с непрозрачными типами. Переписывание библиотеки в C ++, казалось, прошло намного быстрее, чем при написании ее на C, и было менее подвержено ошибкам, особенно утечкам памяти. Я также смог использовать стандартную библиотеку потоков библиотеки, которая была намного проще, чем использование реализаций для конкретной платформы. В конце концов, я считаю, что написание библиотеки на C ++ привело к большим преимуществам, возможно, с небольшими затратами на производительность. Я еще не тестировал версию C ++, но я полагаю, что даже возможно, что я достиг некоторой производительности, используя стандартные структуры данных библиотеки, чем те, которые я написал. Я полагаю, что написание библиотеки на C ++ привело к большим преимуществам с, возможно, небольшой производительностью. Я еще не тестировал версию C ++, но я полагаю, что даже возможно, что я достиг некоторой производительности, используя стандартные структуры данных библиотеки, чем те, которые я написал. Я полагаю, что написание библиотеки на C ++ привело к большим преимуществам с, возможно, небольшой производительностью. Я еще не тестировал версию C ++, но я полагаю, что даже возможно, что я достиг некоторой производительности, используя стандартные структуры данных библиотеки, чем те, которые я написал.

danielunderwood
источник
9
Новейшая поддержка MSVC - это C89.
детально
4
@detly В Visual Studio 2013 поддерживается подавляющее большинство функций C99 . Это не полная поддержка, но я бы поспорил, что на практике можно использовать его для написания C99.
congusbongus
4
@ danielu13 - Где именно вы узнали, что C имеет преимущество в производительности по сравнению с C ++?
Ramhound
1
@ Sebastian-LaurenţiuPlesciuc Я не думаю, что эти ссылки действительно полезны. Первый может быть хорошо встречен почти таким же вопросом programmers.stackexchange.com/q/113295/76444, но в пользу c ++ вместо c, как в вашей ссылке. Для вашей второй ссылки, это просто напыщенная речь Линуса Торвальдса. Я надеюсь, что к настоящему времени все знают, что он действительно любит ненавидеть c ++ и не стал бы трогать его палкой, но его высказывания о c ++ вряд ли объективны, они полны личного мнения и предвзятости и не отражают реальность языка , По крайней мере, это мое мнение .
user1942027
1
@RaphaelMiedl Также я упомянул, что это было написано в 2007 году, довольно давно, компиляторы C ++ и язык C ++ развились с тех пор. Независимо от этого, программист сам выбирает, какой язык использовать.
Себастьян-Лоренцю Плещук

Ответы:

89

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

Давайте возьмем следующее утверждение:

foo->doSomething(a + 5, *c);

Далее предположим, что doSomethingимеет следующую подпись:

void doSomething(int a, long b);

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

В C последствия вполне понятны. fooможет быть только указателем на структуру и doSomethingдолжен быть указателем на функцию. *cразыменовывает длинное и a + 5является целочисленным сложением. Единственная неопределенность проистекает из типа a: если это не int, будет некоторое преобразование. но кроме этого, легко измерить влияние этого единственного утверждения на производительность.

Теперь давайте переключимся на C ++. Одно и то же утверждение теперь может иметь очень разные характеристики производительности:

  1. doSomethingэто может быть не виртуальная функция-член (дешево), виртуальная функция-член (немного дороже) std::function, лямбда-выражения и т. д. Что еще хуже, это fooможет быть перегрузка типа класса operator->с некоторой операцией неизвестной сложности. Таким образом, чтобы количественно оценить стоимость звонка doSomething, теперь необходимо знать точную природу fooи doSomething.
  2. aможет быть целым числом, или ссылкой на целое число (дополнительная косвенность), или типом класса, который реализует operator+(int). Оператор может даже вернуть другой тип класса, который неявно конвертируется в int. Опять же, стоимость производительности не очевидна из одного заявления.
  3. cможет быть реализация типа класса operator*(). Это также может быть ссылка на и long*т. Д.

Вы получаете картину. Из - за С ++ особенностями языка, гораздо сложнее определить затраты на производительность одной инструкции, чем это в C. Теперь в дополнении, абстракция , как std::vector, std::stringобычно используются в C ++, которые имеют характеристики своих собственные, и распределение шкуры динамической памяти ( также см. ответ @ Яна).

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

летальный-гитара
источник
1
Превосходный ответ. Это то, на что я ссылался в своем ответе, но вы объяснили это гораздо лучше.
Ян Голдби
4
Это действительно должен быть принятый ответ. Это объясняет, почему существуют такие утверждения, как «C быстрее, чем C ++». C может быть быстрее или медленнее, чем C ++, но обычно гораздо проще понять, почему конкретный фрагмент кода C работает быстро / медленно, что обычно также облегчает оптимизацию.
Лев
Не брать что-нибудь из этого превосходного ответа (для которого +1 от меня), но компилятор (ы) могут быть яблоками и апельсинами в этом сравнении. Это / они могут генерировать идентичный код для C против C ++, или нет. Конечно, то же самое можно сказать о любых двух компиляторах или опциях компилятора, даже когда компилируется физически одна и та же программа с одними и теми же предположениями на исходном языке.
JRobert
4
Я бы добавил, что большинство сред выполнения C ++ огромны по сравнению с эквивалентным временем выполнения C, что будет иметь значение, если вы ограничены в памяти.
Джеймс Андерсон
@JamesAnderson Если вы действительно что память ограничена, вы , вероятно , не требуется выполнения на всех :)
Навин
30

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

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

как зовут
источник
6
Это может быть быстрее, но не может быть быстрее по той же причине.
Роб
Можете ли вы привести несколько примеров оптимизированного кода, написанного на C ++, который работает быстрее, чем оптимизированный C?
1
@TomDworzanski: один из примеров заключается в том, что с помощью шаблонов решения о путях кода могут быть определены во время компиляции и в конечном итоге жестко закодированы в конечном двоичном файле, а не в условных выражениях и ветвлениях, как это требовалось бы, если бы оно было написано в c, а также в способностях. чтобы избежать вызовов функций через встраивание.
whatsisname
23

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

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

Грег Хьюгилл
источник
3
Затраты на отправку виртуальной функции практически ничтожны, если только вы не пошли НАДЕЖДУ за разложение и виртуализацию. Таблицы будут небольшими по сравнению с остальным кодом и данными, а индексированная ветвь через таблицу добавляет несколько часов к каждому вызову процедуры. Принимая во внимание, что обычные вызовы, все до вызова, чтобы вернуться, будут где-то от нескольких сотен до нескольких миллионов часов, ветвь vtable будет похоронена в минимальном уровне шума.
Джон Р. Штром
6
Структуры - это классы в C ++.
правостороннее
2
@rightfold: Конечно, но вы все равно можете написать код C ++, который передает указатели на структуры без использования thisязыка указателей. Это все, что я говорил.
Грег Хьюгилл
4
@John Реальная стоимость не косвенная (хотя я почти уверен, что это также несколько усложнит предварительную выборку некоторых процессоров), а тот факт, что вы не можете встроить виртуальные функции (по крайней мере в C ++), что запрещает многие другие возможные варианты оптимизаций. И да, это может оказать гигантское влияние на производительность.
Во
2
@ Справедливости ради, то же самое можно сказать и об эквивалентном коде C (код, который вручную эмулирует полиморфизм во время выполнения). Самым большим отличием является то, что я считаю, что компилятору будет легче определить, может ли указанная функция быть встроенной в C ++.
Томас Эдинг
14

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

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

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

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

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

Ян Голдби
источник
5

Что бы это ни стоило, я склонен писать свои библиотеки на C ++ 11 для расширенного набора функций. Мне нравится, когда я могу использовать в своих интересах такие вещи, как общие указатели, исключения, общее программирование и другие функции только C ++. Мне нравится C ++ 11, потому что я обнаружил, что большая его часть поддерживается на всех платформах, которые мне интересны. Visual Studio 2013 имеет множество основных языковых функций и реализаций библиотек, готовых к работе, и предположительно работает над добавлением оставшейся части. Как вы хорошо знаете, Clang и GCC поддерживают и весь набор функций.

С учетом вышесказанного я недавно прочитал о действительно великолепной стратегии развития библиотек, которая, на мой взгляд, имеет прямое отношение к вашему запросу. Статья называется «Стиль обработки ошибок переменного тока, который хорошо сочетается с исключениями C ++». Стефану Дю Туа (Stefanu Du Toit) называет эту стратегию шаблоном «песочных часов». Первый абзац статьи:

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


Теперь для решения вашей основной задачи: производительность.

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

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


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

std :: unique_ptr имеет нулевые накладные расходы

constexp позволяет вычислить во время компиляции

перемещение семантики предотвращает ненужные временные объекты

vmrob
источник
std::unique_ptr has zero overheadЭто не может быть правдой (технически говоря), потому что он должен вызывать свой конструктор, если стек разматывается из-за исключения. Необработанный указатель не имеет таких издержек и все равно будет корректным, если ваш код, вероятно, не сгенерирует. Компилятор не сможет доказать это в общем случае.
Томас Эдинг
2
@ThomasEding Я имел в виду размер и время выполнения в отношении кода без исключений. Поправьте меня, если я ошибаюсь, но есть модели выполнения, которые несут нулевые накладные расходы времени выполнения, когда исключения не создаются, которые все еще позволяют распространять исключения при необходимости. Тем не менее, когда исключение может быть брошено в конструктор unique_ptr? Он объявлен noexcept, и поэтому, по крайней мере, он обрабатывает все исключения, но я не могу представить, какой тип исключения вообще может быть выдан.
vmrob
vmrob: простите меня ... я хотел написать "деструктор" вместо "конструктор". Я также хотел написать «доказуемо не будет бросать» тоже. EEEK!
Томас Эдинг
2
@ThomasEding Знаете, я не думаю, что было бы даже важно, если бы деструктор бросил исключение. Пока деструктор не вводит никаких новых исключений, он по-прежнему уничтожает лишние накладные расходы. Более того, я считаю, что весь деструктор встроен в один вызов удаления / освобождения с оптимизацией.
vmrob
4

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

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

Виды дорогостоящих вещей, которые соблазняет C ++, - это чрезмерное управление памятью, программирование в стиле уведомлений, доверие счетчика программ к многоуровневым библиотекам абстракции (как сказал @Ian), скрытие медлительности и т. Д.

Майк Данлавей
источник
2

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

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

gnasher729
источник
1
кажется, это не дает ничего существенного по сравнению с тем, что было объяснено в 6 предыдущих ответах
комнат
Я думаю, что в этом ответе упоминается важный момент, который никто не упомянул. На первый взгляд кажется, что C ++ является надмножеством C, поэтому, если вы можете написать быструю реализацию на C, вы сможете написать реализацию на C ++, которая эквивалентна. Однако C99 поддерживает ключевое слово restrict, которое позволяет избежать непреднамеренного наложения указателей. C ++ не имеет такой поддержки. Возможность избежать наложения указателей является важной особенностью Fortran, которая делает его полезным для высокопроизводительных приложений. Я ожидаю, что также возможно выжать лучшую производительность из C99, чем C ++ в подобных доменах.
user27539