Пишіть на С для виконання? [зачинено]


32

Я знаю, що досить часто чую, що C, як правило, має перевагу у порівнянні з C ++. Я не думав нічого іншого про це, поки не зрозумів, що MSVC навіть не підтримує найновіший стандарт C, але найновіший підтримує його C99 (наскільки я знаю).

Я планував написати бібліотеку з деяким кодом для візуалізації у OpenGL, щоб я міг її повторно використовувати. Я планував написати бібліотеку на мові C, оскільки будь-яке підвищення продуктивності вітається, коли мова йде про графіку.

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

Однак, якщо це призведе навіть до невеликої різниці у продуктивності, я, швидше за все, пішов би з C.

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

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

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


9
Найновіша підтримка MSVC - це фактично C89.
detly

4
@detly У Visual Studio 2013 підтримується переважна більшість функцій C99 . Це не повна підтримка, але я ставлю на практику це нормально, щоб використовувати його для написання C99.
congusbongus

4
@ danielu13 - Де саме ви чули, що C має перевагу в порівнянні з C ++?
Рамхаунд

1
@ Sebastian-LaurenţiuPlesciuc Я не думаю, що ці посилання насправді корисні. Перший міг би протистояти майже тому самому питанню programmers.stackexchange.com/q/113295/76444, але на користь c ++ замість c як у вашому посиланні. Для Вашого 2-го посилання це лише зухвалий обрив торвальдів. Я сподіваюся, що до цього часу всі знають, що він справді любить ненавидіти c ++ і не торкнеться його палицею, але його розпуки щодо c ++ навряд чи об’єктивні, вони сповнені особистої думки та упередженості і насправді не відображають реальність мови . Принаймні, це моя думка .
користувач1942027

1
@RaphaelMiedl Також я згадав, що це було написано в 2007 році, що було дуже давно, компілятори C ++ та мова C ++ розвивалися з тих пір. Незалежно від того, програміст повинен вибрати, якою мовою користуватися.
Себастьян-Лоренцю Плесьюк

Відповіді:


89

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

Візьмемо наступне твердження:

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

Давайте припустимо, що він doSomethingмає такий підпис:

void doSomething(int a, long b);

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

У З наслідки цілком зрозумілі. fooможе бути лише вказівником на структуру і doSomethingповинен бути вказівником на функцію. *cdereferences довгий і a + 5є цілим числом. Єдина невизначеність походить від типу a: Якщо це не int, відбудеться деяке перетворення. окрім цього, легко оцінити вплив цього продукту на ефективність.

Тепер перейдемо до C ++. Це ж твердження тепер може мати дуже різні характеристики продуктивності:

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

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

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


1
Чудова відповідь. Це я натякав у своїй відповіді, але ви пояснили це набагато краще.
Ян Голдбі

4
Це справді має бути прийнятою відповіддю. Це пояснює, чому такі твердження, як "C швидше, ніж C ++", існують. C може бути швидшим або повільніше, ніж C ++, але, як правило, набагато простіше розібратися, чому конкретний фрагмент коду С є швидким / повільним, що зазвичай також полегшує оптимізацію.
Лев

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

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

@JamesAnderson Якщо ви дійсно що пам'ять обмежена, ви , ймовірно , не потрібно виконання на всіх :)
Навин

30

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

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


6
Це може бути швидше, але може не бути швидшим з тієї ж причини.
Роб

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

1
@TomDworzanski: один із прикладів полягає в тому, що за допомогою шаблонів рішення про шляхи коду можна визначити під час компіляції і в кінцевому підсумку бути твердо кодованим у кінцевому бінарному, а не умовному та розгалужуваному, як це потрібно, якщо це було написано в c, а також уміння щоб уникнути викликів функцій за допомогою вбудовування.
whatsisname

23

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

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


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

6
Структури - це класи на C ++.
праворуч

2
@rightfold: Звичайно, але ви все одно можете записати код C ++, який передає вказівники структурам, не використовуючи thisмову мови вказівника. Це все, що я казав.
Грег Хьюгілл

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

2
@ Чесно кажучи, те ж саме можна сказати і про еквівалентний код C (код, який вручну імітує поліморфізм виконання). Найбільша відмінність полягає в тому, що я вважаю, що компілятору було б простіше визначити, чи може ця функція бути вбудованою в C ++.
Томас Едінг

14

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

Будь-яка мова (або бібліотека, API та ін.), Яка відводить деталі низького рівня, потенційно може приховувати дорогі операції. Наприклад, у деяких мовах просто обрізання пробілу пробілу від рядка призводить до розподілу пам'яті та копії рядка. Зокрема, розподіл пам’яті та копіювання пам’яті можуть дорого коштувати, якщо вони повторюються у вузькому циклі.

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

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

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


5

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

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

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


Тепер, щоб вирішити вашу основну проблему: продуктивність.

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

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


Тепер кілька цікавих читань про те, як мовні функції та оптимізація можуть реально працювати на вашу користь:

std :: unique_ptr має нульові накладні витрати

constexp дозволяє проводити обчислення за часом компіляції

переміщення семантики запобігає непотрібним тимчасовим об'єктам


std::unique_ptr has zero overheadЦе, можливо, не може бути правдою (технічно кажучи), оскільки він повинен викликати конструктор, якщо стек розкручується через виняток. Невисокий покажчик не має цього накладного виду і все ще буде правильним, якщо ваш код, ймовірно, не викине. Компілятор не зможе довести це в загальному випадку.
Томас Едінг

2
@ThomasEding Я мав на увазі розмір та тривалість виконання по відношенню до коду, який не включає виняток. Виправте мене, якщо я помиляюся, але існують моделі виконання, які мають нульові накладні витрати, коли не викидаються винятки, які все ще дозволяють поширювати винятки, коли це необхідно. Тим не менше, коли коли-небудь виняток може бути кинутий у конструктор unique_ptr? Це оголошено noexcept, і, принаймні, воно обробляє всі винятки, але я не можу уявити, який тип винятку може бути викинутий в першу чергу.
vmrob

vmrob: Вибачте мене ... Я мав на увазі написати "деструктор" замість "конструктор". Я також мав намір написати "доказувально не кину" теж. Eeek!
Томас Едінг

2
@ThomasEding Ви знаєте, я не думаю, що це навіть матиме значення, якби деструктор кинув виняток. Поки деструктор не вводить жодних нових винятків, це все ще нульове руйнування. Більше того, я вважаю, що весь деструктор схильний до єдиного видалення / безкоштовного виклику з оптимізаціями.
vmrob

4

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

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

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


2

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

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


1
це , здається, не пропонують нічого істотного за те , що було пояснено в 6 попередніх відповідей
комар

Я думаю, що ця відповідь згадує важливий момент, який ніхто інший не згадував. На перший погляд, здається, що C ++ - це супернабір C, тому якщо ви можете написати швидку реалізацію на C, то ви повинні мати змогу написати еквівалентну C ++ реалізацію. Однак C99 підтримує обмежене ключове слово, що дозволяє уникнути ненавмисного псевдонімування вказівника. C ++ не має такої підтримки. Можливість уникнути псевдонімування покажчиків є важливою особливістю Fortran, що робить його корисним для високопродуктивних додатків. Я сподіваюся, що також можна витіснити кращі показники з C99, ніж C ++ у подібних областях.
user27539
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.