Почему C такой быстрый и почему другие языки не такие быстрые и быстрые? [закрыто]

209

При прослушивании подкаста StackOverflow продолжает появляться джеб, который «настоящие программисты» пишут на C, и что C намного быстрее, потому что он «близок к машине». Оставляя прежнее утверждение для другого поста, что особенного в C, что позволяет ему работать быстрее, чем в других языках? Или, другими словами, что мешает другим языкам компилироваться в двоичный файл, который работает так же быстро, как C?

Майк С.
источник
6
Можете ли вы перечислить, какое конкретное шоу говорило об этом? Я хотел бы услышать это.
Джованни Гальбо
2
Действительно удивлен тем, насколько ответили на этот вопрос (большинство ответов игнорируют фундаментальные различия между компилируемыми и интерпретируемыми языками и т. Д., Я знаю о JIT yada yada yada), и сколько людей занимают позицию, «отстаивающую» свой любимый язык (потребности мальчика FORTRAN принять таблетку).
Тим Ринг
Не забудьте ассемблер. Нет ничего более быстрого или компактного, чем сборочные файлы. Сборка - это почти чистый двоичный файл, поэтому он не имеет предвзятости и самый быстрый язык.
KKZiomek
3
C самый быстрый, потому что это скорость света и относительность?
Познакомьтесь с Таравией
Конечно, неправильно, что C - самый быстрый язык программирования. Никакой язык программирования не приближается к скорости FORTH. FORTH используется для запуска ядерных бомб, это язык программирования на большинстве спутников, основной язык программы на Международной космической станции, а также в CERN и в ITER. Я сравнивал скорость между Microsoft C (разные версии) и FORTH. YAWN to C ...
Scoobeedo Прохладный

Ответы:

200

В C. нет ничего особенного. Это одна из причин, почему это быстро.

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

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

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

coobird
источник
3
сборка мусора может быть быстрее, чем ручное управление памятью (для недолговечных программ и / или большого количества памяти). GC обеспечивает простое и быстрое распределение, и программа не тратит время на освобождение объектов.
Корнель
2
Программы на C обычно выделяют и освобождают память по мере необходимости. Это неэффективно. Хорошая виртуальная машина будет выделять и освобождать большими порциями, что во многих случаях приведет к значительному увеличению производительности.
Скаффман
61
Нет ничего, что мешало бы программе на C выполнять такое же частичное распределение и сборку мусора, кроме того, что это было "сложно".
Эфимент
Очень хорошо сказано, но, как сказал Роб Аллен, С также обеспечивает меньшую абстракцию, чем Java или .NET, что приводит к меньшему количеству переводов (что в наши дни менее верно из-за компиляции точно в срок (JIT), как вы сказали)
Габ Ройер
5
porneL, ручное управление и разумное распределение всегда превосходят любую систему GC при правильном использовании и уделении большого внимания, у вас есть абсолютные знания о ваших шаблонах использования, GC нет, плюс системы GC увеличивают накладные расходы
Ion Todirel
89

Есть компромисс между дизайнерами C. То есть они приняли решение поставить скорость выше безопасности. С не будет

  • Проверьте границы индекса массива
  • Проверьте значения неинициализированных переменных
  • Проверьте на утечки памяти
  • Проверьте разыменование нулевого указателя

Когда вы индексируете в массив, в Java он требует некоторого вызова метода в виртуальной машине, проверки привязки и других проверок работоспособности. Это действительно и абсолютно хорошо , потому что это добавляет безопасность, где это необходимо. Но в Си даже довольно тривиальные вещи не в безопасности. Например, C не требует, чтобы memcpy проверял, перекрываются ли копируемые регионы. Он не предназначен для программирования большого бизнес-приложения.

Но эти дизайнерские решения не являются ошибками в языке Си . Они разработаны специально, так как позволяют компиляторам и авторам библиотек получать максимальную производительность от компьютера. Вот дух C, как это объясняется в документе C Rationale :

Код C может быть непереносимым. Хотя он стремился дать программистам возможность писать действительно переносимые программы, Комитет не хотел заставлять программистов писать переносимо, чтобы исключить использование C в качестве «высокоуровневого ассемблера»: возможность писать специфичные для машины код является одной из сильных сторон C.

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

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

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

Йоханнес Шауб - Литб
источник
51
C проверяет наличие нулевого указателя с помощью сбоя :-). Он также иногда проверяет индексы массива вне диапазона и неинициализированные переменные, облажая ваши кадры стека и данные. К сожалению, это проверяет их во время выполнения.
paxdiablo
18
Я бы не сказал, что C небезопасен, на что вы намекаете. Предполагается, что вы не идиот. Если вы нацелите пистолет и выстрелите себе в ногу, С с радостью сделает вам это, потому что предполагает, что вы умнее, чем есть. Это не обязательно плохо.
Боб Сомерс
19
@ Боб: Точно. Сказать, что С небезопасно, потому что позволяет делать опасные вещи, все равно, что говорить, что машина небезопасна, потому что она позволит вам сойти с обрыва. C так же безопасен, как и человек, занимающийся вождением (но там много небезопасных водителей).
Роберт Гэмбл
5
Боб, делать ошибки, такие как переполнение буфера, не значит, что ты идиот. Это просто означает, что вы все еще человек. Я понимаю, что C и C ++ неплохие (они мне очень нравятся).
Йоханнес Шауб -
4
@ JohannesSchaub-litb C идеально подходит для крупномасштабного программирования приложений. Если кому-то трудно делать проекты больше, чем привет, то проблема в программисте, а не в языке ...
75

Если вы потратите месяц на создание чего-то в C, который будет работать за 0,05 секунды, а я потрачу целый день на то, чтобы написать то же самое на Java, и это будет выполняться за 0,10 секунды, то действительно ли C быстрее?

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

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

Редактировать:

Множество комментариев напоминает: «Я пишу на C и не думаю об оптимизации».

Но взять конкретный пример из этого поста :

В Delphi я мог бы написать это:

function RemoveAllAFromB(a, b: string): string;
var
  before, after :string;
begin
  Result := b;
  if 0 < Pos(a,b) then begin
    before := Copy(b,1,Pos(a,b)-Length(a));
    after := Copy(b,Pos(a,b)+Length(a),Length(b));
    Result := before + after;
    Result := RemoveAllAFromB(a,Result);  //recursive
  end;
end;

и в CI напишите это:

char *s1, *s2, *result; /* original strings and the result string */
int len1, len2; /* lengths of the strings */
for (i = 0; i < len1; i++) {
   for (j = 0; j < len2; j++) {
     if (s1[i] == s2[j]) {
       break;
     }
   }
   if (j == len2) {  /* s1[i] is not found in s2 */
     *result = s1[i]; 
     result++; /* assuming your result array is long enough */
   }
}

Но сколько оптимизаций в версии C? Мы принимаем много решений о реализации, о которых я не думаю в версии Delphi. Как реализована строка? В Delphi я этого не вижу. В Си я решил, что это будет указатель на массив целых чисел ASCII, который мы называем символами. В C мы проверяем существование персонажа по одному. В Delphi я использую Pos.

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

JosephStyons
источник
46
Чтобы быть справедливым, хотя, не так уж много, что потребует один месяц в C, который занял бы только один день в Java, который занимает всего 0,05 секунды для выполнения (то есть небольшая программа).
Dreamlax
13
Я программировал на C годами, и мне почти никогда не приходилось выполнять какие-либо оптимизации, на которые вы намекаете. Я перенес несколько программ на C (в основном из Perl) и в целом вижу увеличение скорости в 10 раз плюс и значительное сокращение использования памяти без какой-либо оптимизации вручную.
Роберт Гэмбл
1
Конечно, есть некоторые вещи, которые могут занять значительно больше времени для программирования на C из-за нехватки существующих средств, поэтому это компромисс между производительностью компьютера и производительностью программиста (среди прочего), поэтому мы не все программируем все на C.
Роберт Гэмбл
4
Я создал программы на C ++, которые обрабатывают тысячи строк данных за меньшее время, чем программы Java или .NET. Это одно из разочарований, которые у меня возникают с более современными языками. C отлично подходит для бережливых программ, которым требуются минимальные требования времени выполнения. PowerBasic отлично подходит для этого.
Bruceatk
35
Вы говорите, что программа, которая занимает месяц на C и в два раза быстрее, чем программа, написанная на Java, которая занимает всего один день, не стоит? Что если эта программа должна запускаться более 500 000 000 раз в день? Быть в два раза быстрее невероятно важно. Если он работает на тысячах или миллионах процессоров, то экономия затрат на дополнительный месяц разработки для удвоения производительности будет огромной. По сути, вы должны знать / понимать масштаб вашего развертывания, прежде чем выбрать платформу разработки.
nicerobot
49

Я не видел его уже, так что я скажу это: C , как правило, быстрее , потому что почти все остальное написано в C .

Java построен на C, Python построен на C (или Java, или .NET и т. Д.), Perl и т. Д. ОС написана на C, виртуальные машины написаны на C, компиляторы написаны на C, Интерпретаторы написаны на C. Некоторые вещи все еще написаны на ассемблере, что, как правило, происходит еще быстрее. Все больше и больше вещей пишется во что-то еще, что само написано на C.

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

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

Как я могу взять свой пирог и съесть его тоже? Как я могу поиграть с высокоуровневыми абстракциями на моем любимом языке, а затем перейти к мелочам C для скорости?

После нескольких лет исследований мой ответ - Python (на C). Возможно, вы захотите взглянуть на это. Кстати, вы также можете перейти в Assembly из Python (с небольшой помощью специальной библиотеки).

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

Наслаждаться.

Роб Уильямс
источник
10
Это не относится к языкам, скомпилированным JIT. Это не значит, что мой C # компилируется в IL, который переводится в C, который компилируется в машинный код. Нет, IL является JIT-скомпилированным - и язык реализации JIT не имеет значения на этом этапе. Это просто производство машинного кода.
Джон Скит
3
Не дай бог мне задать вопрос легендарному Джону Скиту, но кажется совершенно уместным, что производимый машинный код предназначен для C #, а не для C, так что это «более высокий уровень», он имеет больше функций, имеет проверки безопасности и т. Д. И будет следовательно, будет медленнее, чем «эквивалент» С.
Роб Уильямс
3
@Jon: Я собирался сказать что-то похожее, но этот вопрос на самом деле несколько верен, потому что многие компоненты ядра библиотеки .NET действительно написаны на C и, следовательно, имеют ограничения по скорости в C. Будет интересно посмотреть, как это изменится в будущем.
Конрад Рудольф
1
Это кажется неправильным, компилятор / интерпретатор / vms на других языках часто, но не всегда, пишется на c (или, по крайней мере, для самого нижнего уровня), потому что c довольно быстрый (и во многих случаях самый быстрый).
Роман А. Тайчер
2
Этот ответ не соответствует действительности. Как указывалось выше, он не применяется к языкам JIT, но также он не применяется к языкам с их собственными компиляторами (которые, если бы в них были приложены исключительные усилия, могли бы генерировать более быстрый код, чем современные компиляторы C). Единственный другой оставшийся класс языков - это интерпретируемые языки, и они не медленнее, чем C, только потому, что они написаны на самом C, а также из-за накладных расходов на интерпретацию, независимо от того, как вы это делаете, и даже если интерпретатор был написан на ассемблере. , огромный.
Score_Under
38

Там много вопросов - в основном те, на которые я не могу ответить. Но для этого последнего:

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

Одним словом, Абстракция.

C - это только один или два уровня абстракции от машинного языка. Языки Java и .Net находятся как минимум на 3 уровнях абстракции от ассемблера. Я не уверен насчет Python и Ruby.

Как правило, чем больше программист играет (сложные типы данных и т. Д.), Тем дальше вы переходите от машинного языка и тем больше требуется перевода.

Я здесь и там, но это основной смысл.

Обновление ------- Есть несколько хороших комментариев к этому сообщению с более подробной информацией.

Роб Аллен
источник
3
Технически, Java и .Net бесконечно абстрагированы от машинного языка машины, на которой они работают. Они работают в виртуальных машинах. Даже с JIT исходный код должен массироваться, чтобы получить нечто похожее на машинный код.
jmucchiello
1
.net код не запускается в виртуальной машине. Он работает как встроенная инструкция на любой процессорной платформе (32-битная x86, 64-битная x86 или IA64).
Роберт С. Барт
11
@Robert: .net делает использование VM. .net код компилируется в байт-код, который выполняется виртуальной машиной. ВМ преобразует байт-код в собственные инструкции во время выполнения.
Роберт Гэмбл
3
Это очень важно отметить , что Java и другие языки OO абстракции повлияли наборы инструкций процессора. Более новые процессоры имеют инструкции, которые заставляют Java работать быстрее, если виртуальная машина Java знает об этих оптимизациях и использует их. Это не огромный, но это полезно.
Адам Дэвис
1
Может быть, ThumbEE en.wikipedia.org/wiki/…
Роман А. Тайчер
35

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

Два языка, которые обычно компилируются в двоичные файлы, которые так же быстры, как C, - это Standard ML (с использованием компилятора MLton ) и Objective Caml . Если вы посмотрите игру с тестами, то обнаружите, что для некоторых тестов, таких как бинарные деревья, версия OCaml быстрее, чем C. (Я не нашел записей MLton.) Но не принимайте перестрелку слишком серьезно; это, как говорится, игра, результаты которой часто отражают то, сколько усилий люди приложили для настройки кода.

Норман Рэмси
источник
Можно написать неочевидно дорогой код на любом языке. Просто в некоторых языках сначала нужно написать внутренний вариант Lisp или Forth…
Donal Fellows
Также Rust соответствует C в тестах.
Суровая
18

С не всегда быстрее.

C медленнее, чем, например, Modern Fortran.

C часто медленнее, чем Java для некоторых вещей. (Особенно после того, как JIT-компилятор попробовал ваш код)

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

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

По сути, когда вы получаете векторный модуль или распараллеливающий компилятор, C воняет, и современный Fortran работает быстрее.

Уловки программиста на C, такие как thunking (изменение исполняемого файла на лету), приводят к остановке предварительной загрузки ЦП.

Вы получаете дрейф?

И наш хороший друг, x86, выполняет набор инструкций, которые в наши дни мало связаны с реальной архитектурой процессора. Теневые регистры, оптимизаторы загрузки-хранения, все в процессоре. Так что С тогда близок к виртуальному металлу. Настоящий металл, Intel не дает тебе увидеть. (Исторически процессоры VLIW были чем-то вроде бюста, так что, может быть, это не так уж и плохо.)

Если вы программируете на C на высокопроизводительном DSP (может быть, TI DSP?), Компилятор должен сделать некоторые хитрые вещи, чтобы развернуть C на нескольких параллельных исполнительных блоках. Таким образом, в этом случае C не близок к металлу, но близок к компилятору, который будет выполнять оптимизацию всей программы. Weird.

И, наконец, некоторые процессоры (www.ajile.com) выполняют аппаратные байт-коды Java. C будет PITA для использования на этом процессоре.

Тим Виллискрофт
источник
1
Когда был последний раз, когда был написан Thunking, в C? Современный x86 - это интерфейс в основном RISC-дизайна, но это не имеет ничего общего с VLIW ...
Calyth
7
Большая часть вашего поста игнорирует существование C99. Кроме того, многие компиляторы C / C ++ предлагают ключевое слово restrict C99 (гарантирует отсутствие наложения указателей) в качестве расширения.
Эван Теран
Я предполагаю, что все следуют / переходят к следованию топ-25 CWE / SANS и избегают создания новых дизайнов в C. Так что никаких зеленых полей C, так мало или нет C99.
Тим Уиллискрофт
2
Не могли бы вы показать пример, когда с медленнее, чем современный Fortenberry?
Адам
Я не уверен, что когда-либо было время, когда компиляторы Си очень хорошо конкурировали с лучшими компиляторами Фортрана. Конечно, есть много кода, который вы не хотели бы писать на Фортране 77 (не говоря уже о 66), но более поздние стандарты на Фортране становились все более приятными.
ТФБ
11

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

Ничего. Современные языки, такие как Java или .NET, больше ориентированы на производительность труда программиста, чем на производительность. Оборудование дешевое в наши дни. Также компиляция в промежуточное представление дает много бонусов, таких как безопасность, переносимость и т. Д. .NET CLR может использовать преимущества другого оборудования - например, вам не нужно вручную оптимизировать / перекомпилировать программу, чтобы использовать набор инструкций SSE.

Ака
источник
я бы поспорил переносимость здесь. Если вам нужен действительно переносимый код, вы напишите его на C, а не на любом другом языке. У нас есть код, работающий примерно на 25 операционных системах. Начиная с dos и threadX и заканчивая в Linux / XP, покажи мне другой язык, который может это сделать :)
Илья
1
@ Илья, я не согласен. Так же просто написать непереносимый код на C. Посмотрите, как больно было некоторым переносить на 64-битную версию. Языки байт-кода могут работать на всех платформах, если у вас есть правильный интерпретатор байт-кода.
Калит
1
@IIya, переносимый код C - это скорее исключение, чем правило, я портировал код C между различными аппаратными / программными платформами и знаю, что это кошмар.
Ака
Даже для PC Word это не так. Посмотрите на реальность большинства кроссплатформенных приложений, написанных на c / c ++, немного на java. Для встроенных низкоуровневых разработок просто нет других случаев. C является наиболее переносимым языком де-факто.
Илья
@aku -> Портирование плохого кода может быть катастрофой, я согласен. Написание переносимого кода в ADVANCE - C - лучший выбор. Я бы сказал, что C ++ - это вариант, но, переходя на встроенную платформу, вы всегда найдете приличный компилятор C, для c ++ вы можете найти себя без компилятора.
Илья
8

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

Это некоторые другие факторы, которые приходят на ум.

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

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

Мэтью Крамли
источник
С низкого уровня? Я думаю, что это относительное значение сейчас, по сравнению с Java да, но по сравнению с сборкой нет. Хороший пост, заставил меня задуматься.
Mark
Вы правы, это определенно относительно. Я имею в виду, что он «близок к машине» и не помогает вам делать такие вещи, как управление памятью или отслеживание размеров массивов.
Мэтью Крамли
2
C является языком низкого уровня. С всегда был языком низкого уровня. Вы без труда переводите код C на ассемблер.
Роберт С. Барт
2
@Robert: C раньше считался языком высокого уровня, потому что по сравнению со сборкой (которая была очень распространена) это так. Он считается языком низкого уровня по сравнению с большинством языков, используемых сегодня.
Роберт Гэмбл
Честно говоря, это очень предвзятый ответ. Черт, почти все программисты на C выполняют проверку границ и т. Д. Тем не менее, C все еще НАМНОГО быстрее, чем C ++.
MarcusJ
8

Я думаю, вы забыли, что ассемблер также является языком :)

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

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

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

PolyThinker
источник
3
Хотя, если вы написали программу один раз на C и снова на Assembly, версия C, вероятно, будет быстрее, потому что компилятор умнее вас.
mk12
7

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

AShelly
источник
И некоторые из этих языков высокого уровня гордятся тем, что они способны убрать большую часть ненужных им слов, которые они добавили в первую очередь. Такие вещи, как исключение копирования, оптимизация возвращаемого значения, построение / назначение перемещения и т. Д. В C ++.
cmaster - восстановить монику
Так что все сводится к количеству инструкций по сборке, сгенерированных из кода. Чем больше инструкций по сборке в строке кода высокого уровня, тем больше страдает производительность?
ENDEESA
Это может быть упрощением, но существует прямая связь между количеством инструкций по сборке и скоростью программы. У процессора есть минимальное время для выполнения каждой инструкции. ИМХО, язык, который позволяет вам легко понять, сколько инструкций вы пишете, помогает вам писать более эффективный код. (Существуют и другие затраты времени процессора, такие как ошибки ветвления и кэша, но даже в этом случае меньшая абстракция помогает прояснить, что делает процессор).
AShelly
7

Я знаю, что многие люди говорили это многословно, но:

С быстрее, потому что он делает меньше (для вас).

Daemin
источник
Это. Так верно для духа C.
cmaster - восстановить монику
7

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

https://benchmarksgame-team.pages.debian.net/benchmarksgame/

fannjuch-redux был самым быстрым в Скале

n-bodyи fastaбыли быстрее в Аде.

spectral-norm был самым быстрым в Фортране.

reverse-complement, mandelbrotИ pidigitsбыли самым быстрым в САР.

regex-dna был самым быстрым в JavaScript.

chameneou-redux была самая быстрая Java 7.

thread-ring был самым быстрым в Haskell.

Остальные тесты были самыми быстрыми в C или C ++.

Питер Лори
источник
«как это супер набор C» - Нет, C ++ это не надстройка С.
PP
1
extern "C"не имеет ничего общего с C ++, являющимся надмножеством C.
PP
2
Это как сказать, system("bash script.sh");работает для любого скрипта bash, и, следовательно, C является надмножеством bash. extern "C"обеспечивает связь C в C ++ из-за искажения имени. Принимая во внимание, что вызов X как расширенного набора Y означает, что все, что может быть сделано в Y, также может быть сделано в X, что не относится к C ++. Существует довольно много языковых конструкций, которые допустимы в C, но не в C ++.
PP
1
@PP В стандарте C ++ нет ничего, что предписывало bashбы доступную программу командной строки. Если бы он сделал и включил, какую версию / спецификацию bash нужно было поддерживать, я бы посчитал это надмножеством.
Питер Лоури
1
это тривиально легко писать C код , который не C ++ кода, напримерstruct foo { int: this; }; typedef float foo;
Ясно
6

Многие из этих ответов дают веские причины того, почему C быстрее или нет (в целом или в определенных сценариях). Это бесспорно, что:

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

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

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

В качестве примера рассмотрим простую программу Windows, в которой создается одно главное окно. Версия AC будет заполнять WNDCLASS[EX]структуру, которая будет передана RegisterClass[Ex], затем вызывать CreateWindow[Ex]и вводить цикл сообщений. Ниже приведен очень упрощенный и сокращенный код:

WNDCLASS wc;
MSG      msg;

wc.style         = 0;
wc.lpfnWndProc   = &WndProc;
wc.cbClsExtra    = 0;
wc.cbWndExtra    = 0;
wc.hInstance     = hInstance;
wc.hIcon         = NULL;
wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.lpszMenuName  = NULL;
wc.lpszClassName = "MainWndCls";

RegisterClass(&wc);

CreateWindow("MainWndCls", "", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
             CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

while(GetMessage(&msg, NULL, 0, 0)){
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

Эквивалентная программа на C # может быть всего одной строкой кода:

Application.Run(new Form());

Эта единственная строка кода предоставляет все функциональные возможности, которые сделали почти 20 строк кода на C, и добавляет некоторые вещи, которые мы пропустили, такие как проверка ошибок. Более богатая и полная библиотека (по сравнению с библиотеками, используемыми в типичном C-проекте) проделала большую работу для нас, освободив наше время для написания еще большего количества фрагментов кода, которые нам не хватает, но требуют много шагов за кулисами.

Но богатая библиотека, обеспечивающая легкое и быстрое раздувание кода, на самом деле не моя точка зрения. Моя точка зрения становится более очевидной, когда вы начинаете изучать, что на самом деле происходит, когда наша маленькая однострочная строка действительно выполняется. Для развлечения иногда включите доступ к исходным кодам .NET в Visual Studio 2008 или более поздней версии и перейдите к простой строчке выше. Одна из забавных маленьких жемчужин, с которыми вы столкнетесь, - это комментарий в получателе Control.CreateParams:

// In a typical control this is accessed ten times to create and show a control.
// It is a net memory savings, then, to maintain a copy on control.
// 
if (createParams == null) {
    createParams = new CreateParams(); 
} 

Десять раз . Информация, приблизительно эквивалентная сумме того, что хранится в WNDCLASSEXструктуре и того, что передано, CreateWindowExизвлекается из Controlкласса десять раз, прежде чем она сохраняется в WNDCLASSEXструктуре и передается в RegisterClassExи CreateWindowEx.

В общем, количество инструкций, выполняемых для выполнения этой основной задачи, на 2–3 порядка больше в C #, чем в C. Отчасти это связано с использованием многофункциональной библиотеки, которая обязательно обобщается по сравнению с наш простой C-код, который делает именно то, что нам нужно, и ничего более. Но отчасти это связано с тем, что модульная, объектно-ориентированная природа .NET Framework поддается многократному повторению выполнения, которого часто избегает процедурный подход.

Я не пытаюсь выбрать на C # или .NET Framework. Также я не говорю, что модульность, обобщение, функции библиотеки / языка, ООП и т. Д. - это плохие вещи . Я делал большую часть своей разработки на C, позже на C ++, а в последнее время на C #. Точно так же до C я использовал в основном сборку. И с каждым шагом "выше" мой язык, я пишу лучше, более удобные в обслуживании, более надежные программы за меньшее время. Они, однако, имеют тенденцию исполняться немного медленнее.

P папа
источник
2
Это проблема API, а не проблема языка.
Арафангион
1
@Arafangion: я понимаю, что вы говорите, но это как-то не так. Многофункциональная библиотека включена (и, в некотором смысле, востребована) с помощью многофункционального языка. И это не только библиотека. Библиотека является лишь примером общего использования языка. Типичный код приложения на любом языке обычно имеет сходство с библиотеками, обычно используемыми на этом языке. Это действительно больше мышления, поддерживаемого языком. Например, языки ОО обычно тратят больше времени на выделение, конструирование, разрушение и освобождение объектов, чем языки с меньшей поддержкой ООП.
P Daddy
Я признаю, что данный выбор языка обычно подразумевает определенную платформу и библиотеку, именно поэтому я сделал этот комментарий (чтобы читатель был более осведомленным), но это сказало, что использование (например) C ++ в Windows скажем, совсем другой зверь, скажем, для C ++ в linux, и опять другой для C ++ в Android. Другим примером является Python - у нас есть CPython, Jython, PyPy и IronPython - все они используют очень разные библиотеки.
Арафангион
Но используя любой из этих Pythons, разработчики будут стремиться писать приложения определенным образом. Например, они могут читать и писать из текстового файла, создавая новые объекты с данными, которые они читают. В C, с другой стороны, разработчик, скорее всего, сделал бы одноразовое выделение массива структур и прочитал бы и записал эти структуры из двоичного файла. Это, конечно, просто надуманный пример, который пытается проиллюстрировать мысль, которую я пытаюсь сформулировать в отношении мышления .
P Daddy
6

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

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

Я думаю, что есть, по крайней мере, один язык-кандидат, который с усилием мог бы быть оптимизирован лучше, чем C, и, таким образом, мы могли видеть реализации, которые производят более быстрые двоичные файлы. Я думаю о digital mars D, потому что создатель позаботился о создании языка, который потенциально мог бы быть лучше оптимизирован, чем C. Возможно, есть другие языки, которые имеют такую ​​возможность. Однако я не могу себе представить, что любой язык будет иметь компиляторы более чем на несколько процентов быстрее, чем лучшие компиляторы Си. Я хотел бы быть неправым.

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

Также важно (по крайней мере для меня), что код наихудшего случая имеет тенденцию быть быстрым. В Интернете есть множество «доказательств», что Java работает быстрее или быстрее, чем C, но это основано на примерах выбора вишни. Я не большой поклонник C, но я знаю, что НИЧЕГО, что я пишу на C, будет работать хорошо. С Java он «вероятно» будет работать в пределах 15% от скорости, обычно в пределах 25%, но в некоторых случаях это может быть намного хуже. Любые случаи, когда это происходит так же быстро или в пределах нескольких процентов, обычно связаны с тем, что большая часть времени затрачивается на библиотечный код, который в любом случае сильно оптимизирован.

Дон
источник
5

Это на самом деле немного увековеченной лжи. Хотя это правда, что программы на C часто бывают быстрее, это не всегда так, особенно если программист на С не очень хорош в этом.

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

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

Это также поднимает интересный вопрос о C и тем более о C ++. Есть что-то вроде философии дизайна: «Если тебе это не нужно, ты не платишь за это». Проблема в том, что, если вы этого хотите, вы платите за это через нос. Например, реализация vtable в Java имеет тенденцию быть намного лучше, чем реализации C ++, поэтому вызовы виртуальных функций намного быстрее. С другой стороны, у вас нет выбора, кроме как использовать виртуальные функции в Java, и они все еще стоят чего-то, но в программах, которые используют много виртуальных функций, сниженная стоимость складывается.

Джеймс
источник
1
«Реализация vtable в Java имеет тенденцию быть намного лучше, чем реализации C ++, поэтому вызовы виртуальных функций намного быстрее». Как же ты можешь идти быстрее, чем MOV EAX, [ECX]; CALL [EAX + someindex]; ? Если вы не можете вызвать функцию, не просматривая ее, это выглядит довольно оптимально.
Франс-Виллем
@Frans - JIT-компилятор (такой как Java HotSpot) может встроить поиск vtable, если он определяет, что данный объект всегда имеет заданный тип. C ++ также сделает это, если он знает ту же информацию во время компиляции, но эту оптимизацию легче выполнить с помощью байт-кода Java, чем с машинными инструкциями x86.
Том
6
@James - Утверждение «I / O делает производительность менее значимым» не отменяет утверждение «C быстрее, чем другие языки». Это не явная дыра, это аргумент бессмысленного человека.
Том
Было бы лучше использовать в качестве примера обработку строк в Си (а также стандартную библиотеку Си), так как это область, где Си плох. Большинство других языков работают лучше, даже с упрощенным стартовым кодом.
Donal Fellows
@DonalFellows Функции mem * могут быть быстрее, чем функции str * в некоторых задачах, но обработка строк эффективна, если проявлять осторожность. у вас есть конкретные ориентиры?
Ясен
4

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

Эти библиотеки были написаны в то время, когда вычислительная мощность и память были на высоте. Они должны были быть написаны очень эффективно, чтобы вообще работать. Разработчикам компиляторов Си также пришлось долго работать над всевозможными умными оптимизациями для разных процессоров. Зрелость и широкое распространение C делает его значительным преимуществом перед другими языками того же возраста. Это также дает С преимущество в скорости перед более новыми инструментами, которые не подчеркивают необработанную производительность так, как С.

Дейв Сверски
источник
4

Отсутствие абстракции делает C быстрее. Если вы напишите выходное утверждение, вы точно знаете, что происходит. Если вы напишите выходной оператор в java, он будет скомпилирован в файл класса, который затем будет запущен на виртуальной машине с введением уровня абстракции. Отсутствие объектно-ориентированных функций как части языка также увеличивает его скорость и уменьшает количество генерируемого кода. Если вы используете C в качестве объектно-ориентированного языка, тогда вы делаете все кодирование для таких вещей, как классы, унаследование и т. Д. Это означает, вместо того, чтобы сделать что-то достаточно обобщенное для всех с объемом кода и оценкой производительности, которая требует только написания что вам нужно, чтобы сделать работу.

Джаред
источник
4

Удивительно видеть, что старый «C / C ++ должен быть быстрее, чем Java, потому что Java интерпретируется», миф все еще жив и развязан. Есть статьи, выпущенные несколько лет назад , а также более свежие , которые объясняют концепциями или измерениями, почему это не всегда так .

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

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

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

joel.neely
источник
Я согласен , что Java сделал значительные улучшения производительности в течение последних нескольких лет , которые приносят его гораздо ближе к С точки зрения чистой производительности , но это займет некоторое время , чтобы загладить тот факт , что это было так медленно для так долго. Но кто вообще говорил о Java?
Роберт Гэмбл
Java - это «другой язык», на который ссылается OP, не так ли?
Роберт С. Барт
@ Роберт: «другие языки», множественное число, нет упоминания о каком-либо конкретном языке, кроме C. Как вы, возможно, читаете «Java» из этого?
Роберт Гэмбл
@Roberd: Несколько ответов, которые были опубликованы до того, как я столкнулся с этим вопросом, касались Java (или других языков, реализация которых часто осуществляется через интерпретатор или VM).
joel.neely
3
@Joel - Если вы знаете свое целевое оборудование, большинство оптимизаций, которые JVM может выполнить во время выполнения, также можно выполнить с помощью профилированной оптимизации с C или C ++. Это имеет огромное значение и, как правило, подталкивает C и C ++ к лидерству, поскольку им не нужно «учиться» во время выполнения.
Том
4

Самый быстрый работающий код - это тщательно созданный вручную машинный код. Ассемблер будет почти таким же хорошим. Оба очень низкого уровня, и для написания кода требуется много кода. С немного выше ассемблера. У вас все еще есть возможность контролировать вещи на очень низком уровне в реальной машине, но есть достаточно абстракции, чтобы писать ее быстрее и проще, чем на ассемблере. Другие языки, такие как C # и JAVA, еще более абстрактны. В то время как Ассемблер и машинный код называются языками низкого уровня, C # и JAVA (и многие другие) называются языками высокого уровня. С иногда называют языком среднего уровня.

Джим С
источник
Читая ваш ответ, всего два слова во всем абзаце притягивали мои глаза к ним, как магнит, тянущий металлы. Слово JAVA, дважды в абзаце. Никогда раньше не видел его написанным во всех заглавных
буквах
3

Не верьте кому-то на слово, посмотрите на дизассемблирование C и вашего языка в любой важной части вашего кода. Я думаю, что вы можете просто посмотреть в окне разборки во время выполнения в Visual Studio, чтобы увидеть разобранный .Net. Это должно быть возможно, если сложно использовать Java с помощью windbg, хотя, если вы делаете это с .Net, многие проблемы будут одинаковыми.

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

В первый раз я сравнил код JIT с собственным кодом и решил все без исключения вопросы, может ли код .Net работать сравнимо с кодом C. Дополнительный уровень абстракции и все проверки безопасности сопряжены со значительными затратами. Те же затраты могут быть применимы и к Java, но не поверьте мне на слово, попробуйте что-нибудь, где производительность критична. (Кто-нибудь знает достаточно о JITed Java, чтобы найти скомпилированную процедуру в памяти? Это, безусловно, должно быть возможно)


источник
2

1) Как уже говорили другие, С делает для вас меньше. Нет инициализирующих переменных, нет проверки границ массива, нет управления памятью и т. Д. Эти функции в других языках стоят памяти и циклов ЦП, которые C не тратит.

2) Ответы о том, что C менее абстрактен и, следовательно, быстрее, только наполовину правильны, я думаю. Технически говоря, если у вас был «достаточно продвинутый компилятор» для языка X, то язык X мог бы приближаться или равняться скорости C. Разница с C состоит в том, что, поскольку он отображается так очевидно (если вы прошли курс архитектуры), и непосредственно на ассемблере, что даже наивный компилятор может сделать достойную работу. Для чего-то вроде Python вам нужен очень продвинутый компилятор, чтобы предсказывать вероятные типы объектов и генерировать машинный код на лету - семантика C достаточно проста, чтобы простой компилятор мог преуспеть.

Джозеф Гарвин
источник
2

В старые добрые времена было только два типа языков: скомпилированный и интерпретированный.

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

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

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

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

Павел.

Пол У Гомер
источник
«В C, если вы увеличиваете целое число, более чем вероятно, что это только один шаг ассемблера в процессоре» Не совсем верно. Если это целое число отсутствует в регистре процессора, то вам нужен машинный код, чтобы извлечь его из памяти, увеличить, записать обратно в память. Примерно то же самое можно ожидать при запуске кода Java. И я не понимаю, почему «++ i» запускает цикл GC.
quant_dev
@quant_dev: «Вы… должны… извлечь его из памяти, увеличить, записать обратно…». Может быть, а может и нет. Например, в x86 есть инструкции, которые работают с данными в памяти. ++iможет скомпилировать в «add [ebp - 8], 1». Нельзя сказать, что выборка, приращение, сохранение по-прежнему не происходит, но об этом заботится процессор, и, как сказал Пол, это всего лишь одна инструкция.
P Daddy
... который по-прежнему не имеет отношения к POV производительности, потому что это может быть всего лишь одна инструкция, но все же включает ожидание поступления данных в CPU. Кеш пропускает и все такое.
quant_dev
Нет, я бы не сказал, что это не имеет значения. Одна инструкция ЦП обычно занимает меньше байтов кода, чем несколько инструкций ЦП, что обеспечивает лучшую производительность кэша в сегменте кода. Одна инструкция ЦП также занимает меньше места в конвейере ЦП, и несколько этапов (выборка, приращение, сохранение) могут обрабатываться - возможно, параллельно - отдельными этапами конвейера. Фактически, некоторые части операции могут быть даже пропущены, если они могут быть объединены с другими операциями на конвейере. Например, сохранение значения может быть объединено с последующей загрузкой того же значения.
P Daddy
2

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

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

Более фундаментальным преимуществом C (и в аналогичной степени тесно связанный C ++) является сильная зависимость от выделения памяти на основе стека , которая по своей природе является быстрой для выделения, освобождения и доступа. В C (и C ++) первичный стек вызовов может использоваться для выделения примитивов, массивов и агрегатов ( struct/ class).

Хотя C действительно предоставляет возможность динамически распределять память произвольного размера и времени жизни (используя так называемую «кучу»), этого по умолчанию избегают (вместо этого используется стек).

К счастью, иногда можно тиражировать стратегию выделения памяти C в средах выполнения других языков программирования. Это было продемонстрировано в asm.js , который позволяет переводить код, написанный на C или C ++, в подмножество JavaScript и безопасно выполнять его в среде веб-браузера - с почти естественной скоростью.


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

nobar
источник
1
Некоторые из этих преимуществ C в распределении памяти могут быть также воспроизведены умными компиляторами на других языках (см. Здесь ). Однако создается впечатление, что это как-то структурно очень сложно сделать, особенно для непримитивных типов данных. В этом посте упоминается идея неэкранирующего объекта, который может быть размещен в стеке как оптимизация в Java.
nobar
2

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

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

Возникает вопрос: почему? Что в C ++ и Fortran делает их быстрыми и почему они превосходят другие популярные языки, такие как Java или Python?

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

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

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

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

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

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

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

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

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

Если вы хотите узнать больше, пожалуйста, проверьте источник

uniqueNt
источник
1

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

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

Arafangion
источник
2
«Некоторые алгоритмы C ++ быстрее, чем C» не имеет смысла. Любой «алгоритм C ++» может быть написан на C и наоборот. Алгоритмы не зависят от языка. C ++ существенно добавляет функции в C - и ни одна из этих новых функций не приводит к более быстрым алгоритмам (хотя их, возможно, легче написать).
Джозеф Гарвин
1
Классический упрек - это алгоритм std :: sort. Std :: sort работает быстрее, чем любой алгоритм сортировки в C - единственный способ добиться такой же производительности в C - это жестко закодировать его везде, где вы хотите, или использовать макросы - и даже тогда у компилятора меньше информации для оптимизации.
Арафангион
1

С современными оптимизирующими компиляторами очень маловероятно, что чистая программа на Си будет намного быстрее, чем скомпилированный .net код, если вообще будет. Благодаря повышению производительности, которое предоставляют разработчикам такие среды, как .net, вы можете делать вещи за день, который обычно занимал недели или месяцы в обычном C. В сочетании с более низкой стоимостью аппаратного обеспечения по сравнению с зарплатой разработчика, просто НАПИСАТЬ дешевле материал на высокоуровневом языке и аппаратное выбрасывание на любой скорости.

Причина, по которой Джефф и Джоэл говорят о том, что C является языком «настоящего программиста», заключается в том, что в C. нет ручного удержания. Вы должны выделить свою собственную память, освободить эту память, выполнить собственную проверку границ и т. Д. Нет такой вещи как новый объект (); Здесь нет сборки мусора, классов, ООП, структур сущностей, LINQ, свойств, атрибутов, полей или чего-либо подобного. Вы должны знать такие вещи, как арифметика указателя и как разыменовать указатель. И, в этом отношении, знать и понимать, что такое указатель. Вы должны знать, что такое стековый фрейм и каков указатель инструкции. Вы должны знать модель памяти архитектуры процессора, над которой вы работаете. Существует много неявного понимания архитектуры микрокомпьютера (обычномикрокомпьютер, над которым вы работаете) при программировании на C, которого просто нет и не нужно при программировании на C # или Java. Вся эта информация была выгружена программисту компилятора (или ВМ).

Роберт С. Барт
источник
«Больше оборудования в проблеме» работает только в тех средах, где это действительно возможно. Встраиваемый рынок является идеальным контрпримером (и это огромный рынок).
Боб Сомерс
Джефф и Джоэл ведут блог исключительно о бизнес-системах, а не о встроенных системах, поэтому разумно предположить, что именно в этом контексте был задан этот вопрос.
Роберт С. Барт
1) .net код работает так же быстро, как код C? Вы когда-нибудь писали программу на C? 2) Менталитет «брось больше оборудования на проблему» - это то, почему моя двухъядерная 2 ГБ машина с частотой 1,3 ГГц едва справляется с Windows XP, а моя 800 МГц машина с 512 МБ работает с последней версией Ubuntu.
Роберт Гэмбл
Да, я написал C. Это не так великолепно, как это делают люди. И проекты стоят слишком дорого. Это простой случай экономики. Я запускал Win2k на Pentium Pro 180 МГц с 768 МБ оперативной памяти в течение многих лет в качестве почтового и веб-сервера. Это бежало просто отлично. Неофициальные данные ничего не значат.
Роберт С. Барт
C не «великолепен», но он быстр, я написал достаточно кода на C и C #, чтобы знать, что C почти всегда намного быстрее, чем C #, при выполнении той же задачи. Для некоторых задач требуется больше времени для разработки, чем для языков более высокого уровня, но все дело в использовании правильного инструмента для работы, и иногда это так.
Роберт Гэмбл
1

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

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

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

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

Val
источник
1

C быстр, потому что это нативно скомпилированный язык низкого уровня. Но С не самый быстрый. В Benchmark Рекурсивных Фибоначчи показывает , что Руст, Crystal и Nim могут быть быстрее.

Билл Зеленко
источник
1

Есть много причин, в том числе:

  • Компилируется в ассемблер.
  • Это статически типизировано.
  • Нет мусора.
  • Нет обработки ошибок.
  • Многие языки программирования добавляют новые функции. Частью философии C является простота, а не добавление дополнительных функций.
Sapphire_Brick
источник
Почему это должно быть быстрее, потому что оно не является объектно-ориентированным?
Сб 27
A) объектно-ориентированное программирование создает необходимость в сборке мусора, B) большие функции, такие как объектно-ориентированное программирование, усложняют компилятор,
Sapphire_Brick
А) Нет. Смотрите C ++ или Rust. Б) Да, но это также дает компилятору возможности для новых оптимизаций.
Сб 27
А) В Rust есть сборка мусора во время компиляции, а в С ++ - сборка мусора для классов, поэтому у него есть деструкторы, B) Оптимизированная сложность все еще сложна
Sapphire_Brick
A) Это не сборка мусора, управление памятью должно выполняться, даже если вы делаете свою программу в сборке. B) Нет. Больше абстракции позволяет оптимизатору делать лучшие предположения.
sb27