Чому C настільки швидкий, і чому інші мови не є такими швидкими чи швидшими? [зачинено]


208

Слухаючи подкаст StackOverflow, джаб продовжує з'являтись, що "справжні програмісти" пишуть на C, і що C настільки швидше, оскільки "близько до машини". Залишаючи колишнє твердження на іншу посаду, що особливого у C, що дозволяє йому бути швидшим за інші мови? Або, кажучи іншим способом: що не дозволяє іншим мовам компілювати до бінарних файлів, які виконуються кожен біт так само швидко, як C?


6
Чи можете ви перелічити, яке саме шоу говорило про це? Я хотів би це почути.
Джованні Гальбо

2
Дійсно здивований тим, як погано дано відповідь на це запитання (більшість відповідей ігнорує принципові відмінності між складеними та інтерпретованими мовами тощо. Я знаю про JIT yada yada yada), і скільки людей займають позицію, «захищаючи» свою фальшиву мову (хлопчик FORTRAN потребує прийняти таблетку).
Тім Рінг

Не забудьте мову складання. Ніщо не є більш швидким і компактним, ніж збірні машини. Збірка майже чиста двійкова, тому вона без упереджень є найшвидшою мовою.
KKZiomek

3
С найшвидший, тому що це швидкість світла та відносність?
Зустріньтесь з Таравією

Звичайно, неправильно, що C - це найшвидша мова програми. Жодна програмна мова будь-якого типу не наближається до швидкості FORTH. FORTH використовується для запуску ядерних бомб, це програмна мова на більшості супутників, основна мова програми на Міжнародній космічній станції, а також у CERN та в ITER. Я порівнював швидкість між Microsoft C (різними версіями) та FORTH.
ЗНАЙТИ

Відповіді:


200

Це не так багато особливого щодо C. Це одна з причин, чому це швидко.

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

Уловлювач є додаткові накладні обробки, які погіршать продуктивність програми. C не має нічого з цього, це означає, що немає накладних витрат, але це означає, що програмісту потрібно мати можливість розподіляти пам'ять і звільняти їх, щоб запобігти витоку пам'яті , і повинен мати справу зі статичним введенням змінних.

Однак, багато мов та платформ, такі як Java (з її віртуальною машиною Java ) та .NET (із загальною мовою виконання), покращували продуктивність протягом останніх років за допомогою advents, таких як щойно вчасно компіляція, яка виробляє власний машинний код з байт-код для досягнення більшої продуктивності.


3
збирання сміття може бути швидшим, ніж ручне управління пам’яттю (для короткочасних програм та / або великої кількості пам’яті). GC дозволяє простий та швидкий розподіл, а програма не витрачає час на розв’язання речей.
Корнель

2
Програми C зазвичай розподіляють і розподіляють пам'ять за потребою. Це неефективно. Хороший ВМ буде виділяти і розміщувати великі шматки, приносячи великі переваги в ефективності у багатьох випадках.
скафман

60
Ніщо не заважає програмі C виконувати ті ж відмінні розподіли та збирання сміття, окрім «важких».
ефемія

Дуже добре сказано, але, як це сказав Роб Аллен, C також забезпечує меншу абстракцію, ніж Java або .NET, що призводить до меншої кількості перекладів (що є менш правдивим сьогодні завдяки компіляції щойно вчасно (JIT), як ви сказали)
Габ Ройер

5
porneL, ручне управління та розумні розподіли завжди перевершують будь-яку систему GC при правильному використанні і приділяється багато уваги, ви володієте абсолютними знаннями про свої схеми використання, GC - ні, плюс GC системи додають накладні витрати
Іон Тодірел,

89

Дизайнери на C торгують торгівлею. Це означає, що вони прийняли рішення поставити швидкість вище безпеки. С не буде

  • Перевірте межі індексу масиву
  • Перевірте наявність неініціалізованих змінних значень
  • Перевірте наявність витоків пам'яті
  • Перевірте наявність нульової вказівки

Коли ви індексуєте масив, у Java він вимагає певного виклику методу у віртуальній машині, прив'язки до перевірки та інших перевірок правильності. Це дійсно і абсолютно чудово , оскільки додає безпеку там, де належить. Але в С, навіть досить тривіальні речі не ставлять у безпеку. Наприклад, C не вимагає memcpy, щоб перевірити, чи перекриваються регіони для копіювання. Він не розроблений як мова для програмування додатків для великого бізнесу.

Але ці дизайнерські рішення не помилки в мові Сі . Вони розроблені, оскільки вони дозволяють компіляторам та бібліотечним письменникам отримувати кожен шматочок продуктивності з комп'ютера. Ось дух С, як пояснюється це документом C Обгрунтування :

Код С може бути не портативним. Незважаючи на те, що він прагнув надати програмістам можливість писати справді портативні програми, Комітет не хотів змушувати програмістів писати портативно, щоб не допустити використання C як `` ассемблера високого рівня '': можливість писати спеціально для машини код - одна з сильних сторін С.

Зберігайте дух С. Комітет вважав головною метою збереження традиційного духу С. Існує багато аспектів духу С, але суть полягає в спільноті основних принципів, на яких ґрунтується мова С. Деякі грані духу С можна узагальнити такими фразами

  • Довіряйте програмісту.
  • Не заважайте програмісту робити те, що потрібно зробити.
  • Зберігайте мову маленькою та простою.
  • Надайте лише один спосіб зробити операцію.
  • Зробіть це швидко, навіть якщо це не гарантовано, щоб бути портативним.

Останнє прислів’я потребує невеликого пояснення. Потенціал для ефективного генерування коду є однією з найважливіших сильних сторін C. Щоб запобігти вибуху коду, що виглядає дуже простою операцією, багато операцій визначаються як те, як це робить апаратне обладнання цільової машини, а не загальне абстрактне правило. Приклад такої готовності жити з тим, що робить машина, можна побачити в правилах, що регулюють розширення об'єктів char для використання у виразах: чи значення об'єктів char розширюються на підписані чи непідписані величини, як правило, залежить від того, який байт більше працює ефективний на цільовій машині.


51
C перевіряє наявність нульового покажчика дереф, жорстоко розбиваючись :-). Він також періодично перевіряє наявність індексів масиву поза діапазоном та неініціалізованих змінних, накручуючи кадри та дані стека. На жаль, він перевіряє це під час виконання.
paxdiablo

18
Я б не сказав, що C не є безпечним, що звучить як те, на що ви натякаєте. Це передбачає, що ви не ідіот. Якщо ви вкажете пістолет вниз і стріляєте собі в ногу, C із задоволенням зобов’яже вас і дозволить вам зробити це, оскільки він передбачає, що ви розумніший за нього. Це не обов'язково погано.
Боб Сомерс

19
@Bob: Саме так. Говорячи C не є безпечним, тому що він дозволяє робити небезпечні речі, це як сказати, що автомобіль не є безпечним, тому що це дозволить вам їхати зі скелі. C настільки ж безпечний, як людина, яка займається керуванням автомобілем (але там дуже багато небезпечних водіїв).
Роберт Гембл

5
Боб, те, що помилки схожі на перевищення буфера, не означає, що ти ідіот. Це просто означає, що ти все ще людина. Я розумію, що C і C ++ непогані (мені дуже подобаються).
Йоханнес Шауб - запалений

4
@ JohannesSchaub-litb C ідеально підходить для масштабного програмування додатків. Якщо важко робити проекти більші, ніж привіт, тоді проблема полягає в програмісті, а не з мовою ...

75

Якщо ви витрачаєте місяць, щоб створити щось на C, яке працює за 0,05 секунди, і я витрачаю день, пишучи те саме на Java, і це працює за 0,10 секунди, то чи справді 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 я цього не бачу. У C я вирішив, що це буде вказівник на масив цілих чисел ASCII, який ми називаємо символами. У C ми перевіряємо наявність персонажів один за одним. У Delphi я використовую Pos.

І це лише невеликий приклад. У великій програмі програміст C повинен приймати подібні рішення низького рівня з кожніми рядками коду. Це доповнює ручний, оптимізований вручну виконуваний файл.


45
Якщо бути справедливим, не так багато, що зайняв би один місяць в C, який би зайняв лише один день в Java, на виконання якого знадобиться всього 0,05 секунди (тобто невелика програма).
dreamlax

12
Я програмував на 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?

Після кількох років досліджень моя відповідь - Python (на С). Можливо, ви захочете його подивитися. До речі, ви також можете перейти до Асамблеї з Python (за допомогою незначної допомоги спеціальної бібліотеки).

З іншого боку, поганий код може бути написаний будь-якою мовою . Тому C (або збірка) код не відбувається автоматично швидше. Так само деякі оптимізаційні прийоми можуть наблизити частини мовного коду вищого рівня, близькі до рівня продуктивності C. Але, для більшості програм ваша програма витрачає більшу частину часу на очікування людей або обладнання, тому різниця насправді не має значення.

Насолоджуйтесь.


10
Це дійсно не стосується мов, складених JIT. Це не так, як мій C # збирається в IL, який перекладається на C, який компілюється в машинний код. Ні, IL - зібраний JIT - і мова реалізації JIT не має значення на той момент. Це просто створення машинного коду.
Джон Скіт

3
Не дай Бог мені поставити під сумнів легендарного Джона Скіта, але це видається цілком актуальним, що машинний код, який виробляється, призначений для C # замість C, тому він "вищого рівня", має більше функціональних можливостей, перевірки безпеки тощо і буде бути, отже, повільніше, ніж "еквівалент" C.
Роб Вільямс

3
@Jon: Я збирався сказати щось подібне, але справа насправді дещо справедлива, тому що багато основних компонентів бібліотеки .NET насправді написані на C і, таким чином, мають обмеження швидкості С. Буде цікаво подивитися, як це зміниться в майбутньому.
Конрад Рудольф

1
Це здається неправильним, інший компілятор / інтерпретатор / vms іншої мови часто, але не завжди пишеться c (або принаймні для найнижчого шару), оскільки c досить швидкий (а в багатьох випадках найшвидший).
Роман А. Тейчер

2
Ця відповідь не відповідає дійсності. Як було зазначено вище, він не застосовується до мов JIT, але також не застосовується і для мов із власними компіляторами (що, якщо до них докладеться надзвичайних зусиль, може генерувати швидший код, ніж сучасні компілятори 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-код компілюється в байт-код, який виконується VM. VM перетворює байтовий код у рідні інструкції під час виконання.
Роберт Гембл

3
Це дуже важливо відзначити , що Java та іншими мовами OO абстракції вплинули набори інструкцій процесора. Нові процесори мають інструкції, які дозволяють Java працювати швидше, якщо java VM знає про ці оптимізації та використовує їх. Це не величезна, але корисна.
Адам Девіс


35

Це не стільки, що C швидкий, скільки модель вартості C прозора . Якщо програма С повільна, вона повільна очевидним чином: виконуючи безліч операторів. Порівняно з вартістю операцій на C, операції високого рівня на об'єктах (особливо на відображенні) або рядках можуть мати витрати, які не є очевидними.

Дві мови, які зазвичай компілюються у двійкові файли, які так само швидко, як і C, - це стандартний ML (використовуючи компілятор MLton ) та об'єктивний Caml . Якщо ви перевірите гру з орієнтирами, ви побачите, що для деяких орієнтирів, як, наприклад, бінарні дерева, версія OCaml швидша за C. (я не знайшов жодних записів MLton.) Але не сприймайте перестрілку занадто серйозно; це, як мовиться, гра, результати часто відображають, скільки зусиль люди доклали для настройки коду.


Можна писати не очевидно дорогий код будь-якою мовою. Просто в деяких мовах ви повинні спершу написати внутрішній варіант Лісп або Форт…
Дональд

Також Іржа відповідає C на еталонах.
стартував

18

З не завжди швидше.

C повільніше, ніж, наприклад, Modern Fortran.

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

C дозволяє дозволити введення вказівника, що означає, що деякі хороші оптимізації неможливі. Це особливо спричиняє наявність декількох одиниць виконання. Ов.

Припущення про те, що арифметичні роботи покажчика дійсно викликає повільну продуктивність у деяких сімействах процесорів (особливо PIC!), Використовувало для висмоктування великого на сегментованому x86.

В основному, коли ви отримуєте векторну одиницю або паралельний компілятор, C смердить і сучасний Fortran працює швидше.

Прийоми програміста на C, наприклад, громозахис (модифікація виконуваного файлу на ходу), спричиняють зупинки попереднього вибору CPU

Ви отримаєте дрейф?

І наш хороший друг, x86, виконує набір інструкцій, який сьогодні мало стосується до фактичної архітектури процесора. Тіньові регістри, оптимізатори завантаження, все в процесорі. Тож C потім близький до віртуального металу. Справжній метал, Intel не дає вам бачити. (Історично процесори VLIW були трохи розпалені, можливо, це не так вже й погано.)

Якщо ви програмуєте на C на високоефективному DSP (можливо, TI DSP?), Компілятору необхідно виконати кілька складних речей, щоб розгорнути C через кілька паралельних одиниць виконання. Так що в цьому випадку C не є близьким до металу, але він близький до компілятора, який буде робити оптимізацію всієї програми. Дивно.

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


1
Коли востаннє було написано громозахис, у С? Сучасний x86 - це інтерфейс, в основному дизайн RISC, але це мало стосується VLIW ...
Calyth

7
Значна частина вашого повідомлення ігнорує існування C99. Крім того, багато компіляторів C / C ++ пропонують ключове слово обмеження C99 (забезпечує відсутність псевдоніму вказівника) як розширення.
Еван Теран

Я припускаю, що всі слідують / переходять до наступних топових CWE / SANS та уникають створення нових проектів у C. Отже, жодних зелених полів C, так мало, щоб ні C99.
Тім Вілліскрофт

2
Чи можете ви показати приклад, коли c повільніше, ніж сучасний Fortenberry?
Адам

Я не впевнений, що коли-небудь був час, коли компілятори C дуже добре конкурували з найкращими компіляторами Fortran. Звичайно, існує багато коду, який ви б не хотіли писати у FORTRAN 77 (не кажучи вже про 66), але останні стандарти Fortran ставали все більш приємними.
tfb

11

що не дозволяє іншим мовам збиратись до бінарних файлів, які виконуються кожен біт так само швидко, як C?

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


Я б заперечував портативність тут. Якщо ви хочете дійсно портативний код, ви напишете його на C, а не на будь-якій іншій мові. У нас є код, який працює приблизно в 25 операційних системах. Починаючи з dos і threadX і закінчуючи Linux / XP, покажіть мені іншу мову, яка може це зробити :)
Ілля,

1
@Ilya Я не згоден. Так само просто писати не портативний код у C. Подивіться, як боляче було переносити на 64-розрядні порти. Язики байт-кодів можуть працювати на всій платформі, якщо у вас є правильний інтерпретатор байт-коду.
Calyth

1
@IIya, портативний код C - це швидше виняток, ніж правило, я переніс код C між різними апаратними / програмними платформами і знаю, що це кошмар.
аку

Навіть для ПК слово це не так. Загляньте в реальність більшість кросплатформних додатків, написаних на c / c ++, трохи в Java. Для вбудованого розвитку низького рівня просто немає інших випадків. C є фактично найбільш портативною мовою.
Ілля

@aku -> неправильний код ПОРТУВАННЯ може бути катастрофою я згоден. Написання портативного коду в ADVANCE - C - найкращий вибір. Я б сказав, що C ++ - це варіант, але перейшовши на вбудовану платформу, ви завжди знайдете гідний компілятор C, для c ++ ви можете знайти себе без компілятора.
Ілля

8

Основні чинники полягають у тому, що мова є статично введеною і вона компілюється в машинний код. Крім того, оскільки це мова низького рівня, вона, як правило, не робить нічого, чого ви їй не говорите.

Це ще деякі фактори, які приходять на думку.

  • Змінні не ініціалізуються автоматично
  • Немає меж перевірки масивів
  • Неперевірена маніпуляція вказівником
  • Немає цілочислової перевірки переповнення
  • Статичні типи змінних
  • Виклики функцій є статичними (якщо ви не використовуєте вказівники функції)
  • Авторам-упорядникам було багато часу для вдосконалення оптимізуючого коду. Крім того, люди програмують на C для досягнення найкращої продуктивності, тому існує тиск для оптимізації коду.
  • Частини мовної специфікації визначені реалізацією, тому компілятори вільні робити речі найоптимальнішим чином

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


C низький рівень? Я думаю, це зараз відносне значення, порівняно з Java так, але порівняно з збіркою немає. Гарний пост, мене задумалося.
Марк

Ти маєш рацію, це однозначно відносно. Що я маю на увазі, що це "близько до машини" і не допомагає робити такі речі, як управління пам'яттю або відстеження розмірів масиву.
Меттью Крамлі

2
C - мова низького рівня. C завжди була мовою низького рівня. Ви перекладаєте вручну код C в асемблер без особливих труднощів.
Роберт К. Барт

2
@Robert: C вважався мовою високого рівня, тому що порівняно зі складанням (що було дуже часто), це було. Він вважається мовою низького рівня порівняно з більшістю мов, які сьогодні використовуються.
Роберт Гембл

Чесно кажучи, це дуже упереджена відповідь. Блін біля всіх програмістів на C, вони перевіряють межі і т. Д. Однак C все ще набагато швидший, ніж C ++.
MarcusJ

8

Я думаю, ви забули, що мова складання - це також мова :)

Але якщо серйозно, програми C швидше, лише коли програміст знає, що робить. Ви можете легко написати програму C, яка працює повільніше, ніж програми, написані іншими мовами, які виконують ту саму роботу.

Причина, чому C швидша, полягає в тому, що вона сконструйована таким чином. Це дозволяє робити багато матеріалів "нижчого рівня", що допомагає компілятору оптимізувати код. Або, скажімо так, ви програміст відповідальний за оптимізацію коду. Але це часто досить хитро і схильно до помилок.

Інші мови, як і інші згадані, більше зосереджуються на продуктивності програміста. Поширена думка, що програмістський час набагато дорожчий, ніж машинний час (навіть у старі часи). Тож має сенс мінімізувати час, який програмісти витрачають на написання та налагодження програм, а не час роботи програм. Для цього ви трохи пожертвуєте тим, що можете зробити, щоб програма була швидшою, тому що багато речей автоматизовані.


3
Хоча якби ви писали програму один раз на C і знову в асамблеї, версія C, ймовірно, була б швидшою, оскільки компілятор розумніший за вас.
mk12

7

Здебільшого кожна інструкція C відповідає дуже мало інструкцій асемблера. Ви по суті пишете машинний код вищого рівня, тому у вас є контроль над майже всім процесором. У багатьох інших мовах компіляції, таких як C ++, є безліч простих інструкцій, які можуть перетворитись у набагато більше коду, ніж ви думаєте, що це робить (віртуальні функції, конструктори копій тощо). інструкції, які ви ніколи не бачите - віртуальна машина або перекладач.


І деякі з цих мов високого рівня пишаються тим, що вони здатні вилучити більшість частин, які вони додали в першу чергу. Такі речі, як копіювання elision, оптимізація повернутого значення, побудова / присвоєння переміщення тощо у C ++.
cmaster - відновити моніку

Таким чином, все зводиться до кількості інструкцій по збірці, згенерованих з коду. Чим більше інструкцій по збірці в рядку коду високого рівня, тим більше страждає продуктивність?
ENDEESA

Це може бути надмірним спрощенням, але існує пряма залежність між кількістю інструкцій по збірці та швидкістю програми. Існує мінімальна кількість часу для виконання процесором кожної інструкції. IMHO, мова, яка дозволяє вам легко зрозуміти, скільки інструкцій ви пишете, допомагає написати більш ефективний код. (Є й інші витрати часу на процесор, наприклад, відсутність розгалуження та кеш-пам'яті, але навіть там менша абстракція допомагає зрозуміти, що робить процесор).
AShelly


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 був найшвидшим у Хаскеллі.

Решта еталонів були найшвидшими на C або 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 ++.
ПП

1
@PP У стандарті C ++ немає нічого, що bashнадає програму командного рядка. Якби він і включив, яку версію / специфікацію bash потрібно підтримувати, я вважав би це суперсеть.
Пітер Лоурі

1
тривиально легко написати код C, який не є кодом C ++, наприкладstruct foo { int: this; }; typedef float foo;
Ясен

6

Багато з цих відповідей дають вагомі причини, чому C швидше чи ні (в цілому або в конкретних сценаріях). Безперечно, що:

  • Багато інших мов надають автоматичні функції, які ми приймаємо як належне. Наприклад, перевірка меж, перевірка типу роботи та автоматичне управління пам'яттю, наприклад, не приходять безкоштовно. Існує хоча б якась вартість, пов’язана з цими функціями, про яку ми можемо не думати - а то й усвідомлювати - під час написання коду, який використовує ці функції.
  • Крок від джерела до машини часто не є таким прямим в інших мовах, як на C.
  • OTOH, якщо сказати, що компільований код C виконується швидше, ніж інший код, написаний іншими мовами, - це узагальнення, яке не завжди відповідає дійсності. Контрприклади легко знайти (або надумати).

Незважаючи на все це, є щось інше, що я помітив, що, я думаю, впливає на порівняльну ефективність С порівняно з багатьма іншими мовами сильніше, ніж будь-який інший фактор. А саме:

Інші мови часто спрощують писати код, який виконується повільніше. Часто його навіть заохочують дизайнерські філософії мови. Слідство: програміст на С більше схильний писати код, який не виконує зайвих операцій.

Як приклад, розглянемо просту програму Windows, в якій створено єдине головне вікно. Версія змінного струму заповнює 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) зробила багато роботи для нас, звільнивши час на написання ще багатьох фрагментів коду, які нам здаються короткими, але залучають багато кроків за кадром.

Але багата бібліотека, що дозволяє легко та швидко розкривати код, насправді не моє значення. Моя думка є більш очевидною, коли ви починаєте вивчати, що насправді відбувається, коли насправді виконує наш маленький лайнер. Для задоволення колись увімкніть доступ до джерела .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-рамки піддається безлічі повторень виконання, що часто уникається процедурним підходом.

Я не намагаюся вибирати на C # або .NET Framework. Я також не кажу, що модуляризація, узагальнення, особливості бібліотеки / мови, OOP тощо - це погані речі . Більшу частину своєї роботи я раніше робив на C, пізніше на C ++, а останнім часом на C #. Аналогічно до C я використовував переважно збірку. І з кожним кроком "вище" мою мову йде, я пишу кращі, більш рентабельні, більш надійні програми за менший час. Однак вони, як правило, виконують трохи повільніше.


2
Це проблема API, а не мова.
Арафангіон

1
@Arafangion: Я розумію, що ти кажеш, але це якось пропускає суть. Бібліотека, багатою функціями, увімкнена (і, певним чином, вимагається) мовою, багатою функціями. І це не лише бібліотека. Бібліотека - лише приклад поширеного використання мови. Типовий код програми будь-якою мовою, як правило, нагадує бібліотеки, зазвичай використовувані на цій мові. Це справді більше мислення, що сприяє мові. Наприклад, мови OO зазвичай витрачають більше часу на виділення, побудову, руйнування та розміщення об'єктів, ніж мови з меншою підтримкою OOP.
P тато

Я визнаю, що даний вибір мови загалом передбачає певну платформу та бібліотеку, тому я зробив цей коментар (щоб читач був більш обізнаний), але сказав, що, використовуючи (наприклад) C ++ у Windows, це дуже різний звір, скажімо, на C ++ у Linux та знову різний із C ++ на Android. Інший приклад - Python - у нас є CPython, Jython, PyPy та IronPython - всі вони використовують дуже різні бібліотеки.
Арафангіон

Але, використовуючи будь-який з цих пітонів, розробники прагнуть писати програми певним чином. Наприклад, вони можуть читати та писати з текстового файлу, створюючи нові об’єкти з прочитаними даними. У C, з іншого боку, розробник скоріше буде одноразово виділити масив структур і прочитати та записати ці структури з двійкового файлу. Це, звичайно, просто надуманий приклад, який намагається проілюструвати точку, яку я намагаюся скласти щодо мислення .
P тато

6

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

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

Я думаю, що існує принаймні одна мова кандидата, яка з зусиллям може бути оптимізована краще, ніж C, і, таким чином, ми могли побачити реалізацію, яка виробляє швидші бінарні файли. Я думаю про цифровий мар D, тому що творець подбав про створення мови, яка могла б бути оптимізованою краще, ніж C. Можуть бути інші мови, які мають таку можливість. Однак я не можу уявити, що будь-яка мова матиме компілятори більш ніж на кілька відсотків швидше, ніж найкращі компілятори C. Я хотів би помилитися.

Я думаю, що справжній "низько висячий плід" буде в мовах, розроблених ЛІЗНО для людей, щоб оптимізувати їх. Кваліфікований програміст може змусити будь-яку мову піти швидше - але іноді доводиться робити смішні речі або використовувати неприродні конструкції, щоб це сталося. Хоча це завжди потребуватиме зусиль, хороша мова повинна створювати відносно швидкий код, не потребуючи нав'язливості щодо того, як саме написана програма.

Також важливо (принаймні, для мене), що найгірший код має тенденцію бути швидким. В Інтернеті є численні "докази" того, що Java швидше або швидше, ніж C, але це засновано на прикладах збору вишень. Я не великий фанат С, але я знаю, що будь-що, що я пишу на С, буде добре працювати. З Java вона буде "ймовірно" працювати в межах 15% швидкості, як правило, в межах 25%, але в деяких випадках це може бути набагато гірше. Будь-які випадки, коли це так само швидко або в межах декількох відсотків, як правило, пов’язані з тим, що більшість часу витрачається на код бібліотеки, який все одно сильно оптимізований на C.


5

Це насправді трохи увічнена неправда. Хоча це правда, що програми на C часто бувають швидшими, це не завжди так, особливо якщо програміст на C не дуже добре в цьому.

Одне велике кричуще отвір, про яке люди, як правило, забувають, це коли програма повинна блокувати якийсь IO, наприклад, введення користувача в будь-яку програму GUI. У цих випадках не дуже важливо, якою мовою ви користуєтесь, оскільки ви обмежені швидкістю надходження даних, а не швидкістю їх обробки. У цьому випадку мало значення, якщо ви використовуєте C, Java, C # або навіть Perl; ви просто не можете пройти швидше, ніж можуть надходити дані.

Інша основна річ полягає в тому, що використання збору сміття та не використання належних покажчиків дозволяє віртуальній машині зробити ряд оптимізацій, недоступних для інших мов. Наприклад, JVM здатний переміщувати об'єкти навколо купи для дефрагментації. Це робить майбутні асигнування набагато швидшими, оскільки наступний індекс можна просто використовувати, а не шукати його в таблиці. Сучасним JVM також не потрібно фактично розміщувати пам'ять; натомість вони просто переміщують живі об’єкти навколо, коли вони GC, а відпрацьована пам'ять із мертвих об'єктів відновлюється по суті безкоштовно.

Це також викликає цікавий момент щодо 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 - Аргументація "Введення / виведення робить продуктивність меншою" не зводить заяву "C швидше, ніж інші мови". Це не кричущий отвір, це солом'яний аргумент.
Том

Краще було б використати обробку струн C (а також стандартну бібліотеку C) як приклад, оскільки це область, де C бідна. Більшість інших мов краще, навіть із спрощеним стартовим кодом.
Стипендіати Дональ

@DonalFellows функції mem * можуть бути швидшими, ніж функції str * у деяких завданнях, але обробка рядків є ефективною, якщо обережно. Ви маєте на увазі конкретний орієнтир?
Ясен

4

Мова йде не стільки про мову, скільки про інструменти та бібліотеки. Наявні бібліотеки та компілятори для C набагато старші, ніж для нових мов. Ви можете подумати, що це зробить їх повільніше, але au contraire.

Ці бібліотеки були написані в той час, коли потужність обробки та пам’ять були надбавними. Вони повинні були бути написані дуже ефективно, щоб взагалі працювати. Розробникам компіляторів C також давно довелося працювати у всіляких розумних оптимізаціях для різних процесорів. Зрілість C та широке сприйняття дає значну перевагу перед іншими мовами того ж віку. Це також дає перевагу швидкості C у порівнянні з новими інструментами, які не підкреслюють продуктивність настільки сильно, як C.


4

Відсутність абстракції - це те, що робить C швидшим. Якщо ви пишете вихідний оператор, ви точно знаєте, що відбувається. Якщо ви пишете вихідний оператор у java, він збирається у файл класу, який потім запускається на віртуальній машині, представляючи шару абстракції. Відсутність об'єктно-орієнтованих функцій як частини мови також збільшує її швидкість, щоб менше генерувати код. Якщо ви використовуєте C як об'єктно-орієнтовану мову, то ви робите все кодування для таких речей, як класи, живучість тощо. Це означає, а потім зробити щось достатньо узагальнене для всіх із кількістю коду та пенетридою виконання, що вимагає лише запису що потрібно, щоб виконати роботу.


4

Дивовижне бачити старий "C / C ++ повинно бути швидше, ніж Java, тому що Java трактується" міф все ще живий і б'є. Є статті, що повертаються на кілька років , а також новіші , які пояснюють поняттями або вимірюваннями, чому це просто не завжди так .

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

  • надання частих методів машинного коду,
  • викладення малих методів,
  • регулювання блокування

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


Я погоджуюся з тим, що за останні кілька років Java зробила значні покращення продуктивності, що наблизило її до C з точки зору необмеженої продуктивності, але це займе певний час, щоб знизити той факт, що це було так повільно так довго. Але хто все-таки говорив про Яву?
Роберт Гембл

Java - це "інша мова", на яку посилається ОП, чи не так?
Роберт К. Барт

@Robert: "інші мови", множина, жодна конкретна мова, окрім C., не згадується. Як ви, можливо, читаєте "Java" з цього?
Роберт Гембл

@Roberd: Деякі відповіді, які були опубліковані до того, як я зіткнувся з питанням, говорили про Java (або інші мови, реалізація яких часто здійснюється за допомогою інтерпретатора або VM).
joel.neely

3
@Joel - Якщо ви знаєте цільове обладнання, більшість оптимізацій, які JVM може зробити під час виконання, також можна виконати за допомогою керованої профілем оптимізації за допомогою C або C ++. Це робить величезну різницю, і, як правило, відштовхує C і C ++ назад, оскільки їм не потрібно «вчитися» під час виконання.
Том

4

Найшвидше працюючий код буде ретельно виготовлений машинним кодом. Асемблер буде майже таким же хорошим. Обидва дуже низький рівень, і для цього потрібно багато писати код. C трохи вище асемблера. У вас все ще є можливість контролювати речі на дуже низькому рівні в реальній машині, але є достатня абстракція, щоб зробити її швидше і простіше, ніж асемблер. Інші мови, такі як C # і JAVA, ще більш абстрактні. У той час як Assembler і машинний код називаються мовами низького рівня, C # і JAVA (і багато інших) називаються мовами високого рівня. C іноді називають мовою середнього рівня.


Читаючи вашу відповідь, лише два слова в цілому абзаці тягнули мої очі на них, мов магніт, що тягне метали. Слово - JAVA, двічі в абзаці. Ніколи не бачив, як це написано в усіх шапках, і це добре виглядає :-)
Snađошƒаӽ

3

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

Мені не подобається писати на C, якщо мені цього не потрібно, але я думаю, що багато претензій, висловлених у цих відповідях, про те, що швидкості мов, відмінних від C, можна відкласти, просто розібравши таку ж рутину на C і на мові вищого рівня, що вибирається, особливо якщо задіяно багато даних, як це часто зустрічається в критичних робочих програмах. Фортран може бути винятком у своїй області знань, не знаю. Це вищий рівень, ніж C?

Перший раз я порівнював JITed-код з нативним кодом, вирішував будь-які питання, чи може .Net-код працювати порівняно з кодом С. Додатковий рівень абстракції та всі перевірки безпеки мають значні витрати. Такі ж витрати, мабуть, стосуватимуться Java, але не сприймайте мого слова, спробуйте на тому, де продуктивність є критичною. (Хтось знає достатньо про JITed Java, щоб знайти складену процедуру в пам'яті? Це, безумовно, повинно бути можливим)


2

1) Як інші казали, C робить менше для вас. Ні ініціалізація змінних, ні перевірка меж масиву, ні управління пам'яттю тощо. Ці функції на інших мовах коштують пам’яті та процесорних циклів, які C не витрачає.

2) Відповіді, які говорять про те, що C менш абстрагований і, отже, швидший, є лише наполовину правильним, я думаю. Технічно кажучи, якщо у вас був "досить просунутий компілятор" для мови X, то мова X могла б наблизитись або дорівняти швидкість C. Різниця з C полягає в тому, що вона відображає так очевидно (якщо ви пройшли курс архітектури) і безпосередньо на мові складання, що навіть наївний компілятор може зробити гідну роботу. Для чогось на зразок Python вам потрібен дуже просунутий компілятор, щоб передбачити ймовірні типи об’єктів і генерувати машинний код на льоту - семантика C досить проста, що простий компілятор може зробити добре.


2

Ще в добрі дні існували два типи мов: складена та інтерпретована.

Мови компіляції використовували "компілятор", щоб прочитати синтаксис мови та перетворити його в ідентичний код мови збірки, який міг би бути просто безпосередньо на процесорі. Інтерпретовані мови використовували кілька різних схем, але по суті синтаксис мови був перетворений в проміжну форму, а потім запускається в "інтерпретатор", середовище для виконання коду.

Таким чином, у певному сенсі між кодом і машиною виник ще один «шар» - інтерпретатор. І, як це завжди буває в комп’ютері, більше означає, що більше ресурсів звикне. Перекладачі були повільнішими, бо їм довелося виконувати більше операцій.

Зовсім недавно ми бачили більше гібридних мов, таких як Java, які використовують і компілятор, і інтерпретатор, щоб змусити їх працювати. Це складно, але JVM є швидшим, досконалішим та оптимізованим, ніж старі перекладачі, тому він набагато краща зміна виконання (з часом) ближче до просто складеного коду. Звичайно, нові компілятори також мають більш хитро оптимізовані трюки, тому вони, як правило, генерують набагато кращий код, ніж раніше. Але більшість оптимізацій, найчастіше (хоча і не завжди), роблять певний вид компромісу таким, що вони не завжди швидші за будь-яких обставин. Як і все, нічого не приходить безкоштовно, тому оптимізатори повинні похвалитися звідкись (хоча часто це використовується, використовуючи процесор компіляції, щоб заощадити процесор виконання).

Повертаючись до C, це проста мова, яку можна скласти в досить оптимізовану збірку і потім запустити безпосередньо на цільовій машині. У C, якщо ви збільшуєте ціле число, це більш ніж ймовірно, що це лише один крок асемблера в процесорі, але в Java він може виявитися набагато більше, ніж це (і може включати також трохи збору сміття: -) C пропонує вам абстракцію, яка знаходиться ближче до машини (асемблер є найближчим), але ви, зрештою, повинні зробити більше роботи, щоб зробити це, і воно не настільки захищене, просте у використанні чи не сприймає помилок. Більшість інших мов дають вам більш високу абстракцію та піклуються про більшу частину деталей для вас, але в обмін на їх вдосконалений функціонал вони потребують більше ресурсів для роботи. Узагальнюючи деякі рішення, вам доведеться працювати з більш широким спектром обчислень,

Пол.


"У C, якщо ви збільшуєте ціле число, більш ніж ймовірно, що це лише один крок асемблера в ЦП" Не зовсім вірно. Якщо це ціле число відсутнє в реєстрі процесора, то вам потрібно мати машинний код, щоб отримати його з пам'яті, збільшити, записати його назад в пам'ять. Приблизно те саме, що можна очікувати, станеться під час запуску коду Java. І я не розумію, чому "++ i" запустив би сам цикл GC.
Quant_dev

@quant_dev: "Ви .. маєте ... вийняти його з пам'яті, збільшити, записати назад ...". Можливо, може й ні. Наприклад, x86 має вказівки, які працюють з даними в пам'яті. ++iможе компілювати до "add [ebp - 8], 1". Не кажучи про те, що отримання, збільшення, зберігання все ще не відбувається, але це подбає процесор, і це лише одна інструкція, як сказав Пол.
П тато

... що все ще не має відношення до POV продуктивності, оскільки це може бути лише одна інструкція, але все-таки передбачає очікування надходження даних до процесора. Кеш пропускає і все таке.
Quant_dev

Ні, я б не сказав, що це не має значення. Одна інструкція процесора зазвичай займає менше байтів коду, ніж кілька інструкцій процесора, що забезпечує кращу ефективність кешу на сегменті коду. Одна інструкція процесора також займає менше місця в конвеєрі процесора, і декілька етапів (отримання, збільшення, зберігання) можна обробляти - можливо паралельно - окремими етапами конвеєра. Насправді, деякі частини операції можуть бути навіть пропущені, якщо їх можна зв'язати з іншими операціями на трубопроводі. Наприклад, зберігання значення може бути зв'язане з подальшим завантаженням того ж значення.
P тато

2

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

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

Більш фундаментальна перевага C (і в аналогічній мірі тісно пов'язаний C ++) - велика залежність від розподілу пам’яті на основі стека , який за своєю суттю швидкий для розподілу, розсилки та доступу. У C (і C ++) основний стек викликів може використовуватися для розподілу примітивів, масивів та агрегатів ( struct/ class).

Незважаючи на те, що C пропонує можливість динамічно розподіляти пам'ять довільного розміру та терміну служби (використовуючи так звану "купу"), цього уникати за замовчуванням (замість цього використовується стек).

Вражаючи, що іноді можливо повторити стратегію розподілу пам'яті C в середовищах виконання інших мов програмування. Це було продемонстровано asm.js , який дозволяє коду, написаному на C або C ++, переводити в підмножину JavaScript і безпечно запускатись у середовищі веб-браузера - з майже рідною швидкістю.


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


1
Деякі з цих переваг розподілу пам'яті C, ймовірно, також можуть бути реплікані на інших мовах розумними компіляторами (див. Тут ). Однак у мене складається враження, що це якось структурно зробити дуже складно - особливо для непомітних типів даних. У цій публікації згадується ідея невідмінюваного об'єкта, який може бути виділений стеком як оптимізація на Java.
nobar

2

Я знайшов відповідь за посиланням про те, чому деякі мови швидші, а деякі повільніші, я сподіваюся, що це зрозуміє більше про те, чому C або C ++ швидше інших, Існують і інші мови, які швидші за C, але ми не можемо використовувати їх усі. Деякі пояснення -

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

Тут виникає питання: чому? Що з C ++ та Fortran робить їх швидкими, і чому вони перевершують інші популярні мови, наприклад, Java або Python?

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

Поділ не важкий; скоріше, є спектр. З одного боку, у нас є традиційні складені мови - група, що включає Fortran, C та C ++. У цих мовах є дискретна стадія компіляції, яка переводить вихідний код програми у виконувану форму, яку може використовувати процесор.

Цей процес компіляції має кілька етапів. Вихідний код аналізується та аналізується. На цьому етапі можуть бути виявлені основні помилки кодування, такі як помилки друку та орфографії. Проаналізований код використовується для створення уявлення в пам'яті, яке теж може бути використане для виявлення помилок - цього разу, семантичні помилки, наприклад, функції виклику, які не існують, або намагання виконувати арифметичні операції над рядками тексту.

Це уявлення в пам'яті потім використовується для приводу генератора коду, тієї частини, яка виробляє виконуваний код. Оптимізація коду, щоб поліпшити продуктивність сформованого коду, проводиться в різні періоди в рамках цього процесу: оптимізація високого рівня може проводитися на представлення коду, а оптимізація нижчого рівня використовується на виході генератора коду.

Фактично виконання коду відбувається пізніше. Весь процес компіляції просто використовується для створення чогось, що можна виконати.

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

Найпростіший інтерпретатор має в собі виконуваний код, що відповідає різним функціям, які підтримує мова, тому він матиме функції для додавання чисел, з'єднання рядків, що б там не було в даній мові. Коли він аналізує код, він шукатиме відповідну функцію та виконує її. Змінні, створені в програмі, зберігатимуться в якійсь таблиці пошуку, яка відображає їх імена до їх даних.

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

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

Якщо ви хочете дізнатися більше, перевірте джерело


1

Деякі алгоритми C ++ швидші, ніж C, а деякі реалізації алгоритмів або моделей дизайну іншими мовами можуть бути швидшими, ніж C.

Коли люди кажуть, що C швидкий, а потім переходять до розмови про якусь іншу мову, вони, як правило, використовують продуктивність C як орієнтир.


2
"Деякі алгоритми C ++ швидші за C" не мають сенсу. Будь-який "алгоритм С ++" може бути записаний на С та навпаки. Алгоритми - це агностики мови. C ++ по суті додає функції C - і жодна з цих нових функцій не призводить до швидших алгоритмів (хоча вони, можливо, простіше писати).
Джозеф Гарвін

1
Класичний докор - це std :: алгоритм сортування .. std :: сортування швидше, ніж будь-який алгоритм сортування в C - єдиний спосіб отримати таку саму продуктивність на C - це жорсткий код, куди ви хочете, або використання макросів - і навіть тоді компілятор має менше інформації для оптимізації.
Арафангіон

1

З сучасними оптимізаційними компіляторами малоймовірно, що чиста програма С буде набагато швидшою, ніж компільований .net-код, якщо він є. З покращенням продуктивності, що такі рамки, як .net надає розробнику, ви можете робити щодня за день, який займав тижні чи місяці в звичайному C. У поєднанні з дешевою вартістю апаратних засобів порівняно із зарплатою розробника, писати просто БОЛЬШЕ дешевше речі мовою високого рівня та викидати апаратне забезпечення при будь-якій повільності.

Причиною, що Джефф і Джоел говорять про те, що мова С є "справжнім програмістом", полягає в тому, що в ній немає руки, яка належить до C. Ви повинні виділити власну пам'ять, розставити цю пам'ять, зробити власні межі перевірки тощо. Такого немає як новий об’єкт (); Тут немає збирання сміття, класів, OOP, фреймворків об'єктів, LINQ, властивостей, атрибутів, полів тощо. Ви повинні знати такі речі, як арифметика вказівника та як знецінювати покажчик. І, з цього приводу, знайте і розумійте, що таке покажчик. Ви повинні знати, що таке кадр стека та що таке покажчик інструкцій. Ви повинні знати модель пам'яті архітектури процесора, над якою працюєте. Існує багато неявного розуміння архітектури мікрокомп'ютера (зазвичай ,мікрокомп'ютер, над яким ви працюєте) при програмуванні на C, якого просто немає ні потрібного, ні для програмування на щось на зразок C # або Java. Вся ця інформація була завантажена програмістом компілятора (або VM).


"Тим більше апаратного забезпечення проблеми" працює лише в середовищах, де це реально можливо. Вбудований ринок є ідеальним зустрічним прикладом (і це масовий ринок).
Боб Сомерс

Блог Джеффа та Джоеля ведеться виключно про бізнес-системи, а не вбудовані системи, тому розумно припустити, що саме такий контекст був заданий цим питанням.
Роберт К. Барт

1) .net-код працює так само швидко, як і код C? Ви коли-небудь насправді писали програму C? 2) Менталітет "кинути більше обладнання на проблему" - це те, чому мій двоядерний 2 Гб машина з частотою 1,3 ГГц ледь не може йти в ногу з Windows XP, в той час як моя 800 МГц 512 МБ працює з останньою версією Ubuntu.
Роберт Гембл

Так, я написав C. Це не так славно, як це роблять люди. І проекти коштують занадто дорого. Це простий випадок економії. Я працював Win2k на Pentium Pro на 180 МГц із 768 Мб оперативної пам'яті протягом багатьох років як поштовий та веб-сервер. Це пройшло просто чудово. Анекдотичні докази нічого не означають.
Роберт К. Барт

C не "славний", але це швидко, я написав достатньо C і C # код, щоб знати, що C майже завжди набагато швидше, ніж C # під час виконання одного і того ж завдання. Для деяких завдань розробляється більше мов вищого рівня, але це стосується використання правильного інструменту для роботи, а іноді і C.
Роберт Гембл

1

Різниця між автоматичною та ручною мовами вищого рівня - таким чином автоматизовані абстракції. C / C ++ контролюються та обробляються вручну, навіть код перевірки помилок іноді - це ручна праця.

C і C ++ також є компільованими мовами, а це означає, що жоден із цих компаній не працює, і ці мови повинні бути чітко налаштовані на обладнання, з яким ви працюєте, додаючи додатковий шар gotcha. Хоча це зараз трохи припиняється, оскільки компілятори C / C ++ стають все більш поширеними на всіх платформах. Ви можете робити перехресні компіляції між платформами. Це все ще не запуск скрізь ситуації, ваш в основному інструктаж компілятора A компілювати проти компілятора B того ж коду різної архітектури.

Мови нижнього рядка C не повинні бути легко зрозумітими чи аргументованими, тому також їх називають системними мовами. Вони вийшли перед усією цією нісенітницею абстракції високого рівня. Це також, чому вони не використовуються для переднього веб-програмування. Вони просто не підходять до завдання, означають вирішення складних проблем, які неможливо вирішити за допомогою звичайних мовних засобів.

Ось чому ви отримуєте божевільні речі на кшталт (мікро-архітектури, драйвери, квантова фізика, ігри AAA, операційні системи). Є речі C і C ++ просто добре підходять. Швидкість і кількість хрускіт є головними областями.



1

Причин багато, зокрема:

  • Вона компілюється в асемблерну мову.
  • Він набраний статично.
  • Заборонено збирати сміття.
  • Немає обробки помилок.
  • Багато мов програмування додають нові функції. Частина філософії C полягає в тому, щоб просто робити речі, а не додавати більше функцій.

Чому він повинен бути швидшим, оскільки він не є об'єктово-оріонованим?
sb27

А) об'єктно-орієнтоване програмування створює потребу в збиранні сміття; В) великі функції, такі як об'єктно-орієнтоване програмування, додають складності компілятору,
Sapphire_Brick

А) Ні. Дивіться C ++ або Rust. Б) Так, але це також дає можливість Компілятору для нових оптимізацій.
sb27

A) Rust має збір сміття під час збирання, а c ++ має збір сміття для класів, тому він має деструктори ,, B) оптимізована складність все ще є складністю
Sapphire_Brick

A) Це не збирання сміття, керування пам’яттю повинно здійснюватися, навіть якщо ви складаєте програму в зборі B) Ні. Більше абстрагування дозволяє оптимізатору робити більш якісні припущення.
sb27
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.