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


17

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

Ключові міркування пішли так: компілятор Fortran випускає в середньому 1,1 інструкцію процесора на рядок коду, тоді як компілятор C випускає в середньому 1,6 інструкцію процесора на рядок коду - я не пам'ятаю точних цифр, але Ідея полягала в тому, що компілятори C випромінювали помітно більше машинного коду і тому виробляли повільніші програми.

Наскільки справедливе таке порівняння? Чи можна сказати, що компілятори Fortran виробляють швидші програми, ніж компілятори C, або навпаки, і чому така різниця існує?


19
Це може просто означати, що програми Fortran є більш багатослівними, ніж C ... Змістовне порівняння можна було виконати лише шляхом впровадження однакових функцій на обох мовах та порівняння отриманого машинного коду (розміру та швидкості).
Péter Török

Також, чи створений код підтримує паралельне виконання?

@ Péter Török, це просто означає, що, скажімо, BLAS і LAPACK у Fortran працювали набагато краще, ніж будь-який їх C / C ++ порт. Тепер розрив скорочується швидко.
SK-логіка

6
Ви можете стверджувати, що один компілятор виробляє швидший код, якщо у вас є 100% еквівалентна програма на обох мовах, написана експертами, які знають їх компіляторів і які можуть враховувати ефективність.
Сокіл

Колишній Fortran не підтримував рекурсію, і тому не обов'язково було натискати аргументи виклику функцій на стек, оскільки там було б статично виділений простір для аргументів кожної функції. Це одна з причин, чому це могло бути швидше. Напевно, ви можете знайти більш повну відповідь тут: amazon.com/Programming-Language-Pragmatics-Third-Edition/dp/…
Педро Роло

Відповіді:


36

IIRC Однією з головних причин, чому, як кажуть, Фортран швидше, є відсутність відчуження вказівників , тому вони можуть використовувати оптимізацію, яку компілятори C не можуть використовувати:

У FORTRAN аргументи функції можуть не псевдонімувати один одного, і компілятор припускає, що їх немає. Це дає чудову оптимізацію і є однією з головних причин репутації FORTRAN як швидкої мови. (Зверніть увагу, що згладжування все ще може відбуватися в межах функції FORTRAN. Наприклад, якщо A - масив, а i i j - індекси, які, мабуть, мають однакове значення, то A [i] і A [j] - це два різні імена для те ж місце пам’яті. На щастя, оскільки базовий масив повинен мати одне ім’я, аналіз індексу можна зробити для визначення випадків, коли A [i] та A [j] не можуть мати псевдоніми.)

Але я згоден з іншими тут: Порівнювати середню кількість інструкцій асемблера, сформованих для рядка коду, є повною нісенітницею. Наприклад, сучасне ядро ​​x86 може виконувати дві інструкції паралельно, якщо вони не мають доступу до одних і тих же регістрів. Таким чином, ви можете (теоретично) отримати підвищення продуктивності на 100% для того ж набору з інструкцій, просто перепорядкувавши їх . Хороші компілятори також часто генерують більше інструкцій по збірці, щоб отримати швидший код (думка розгортання, вбудовання циклу). Загальна кількість інструкцій асемблера говорить дуже мало про виконання фрагмента коду.


Ще однією причиною кращих оптимізацій є нативна підтримка складних чисел.
SK-логіка

Безумовно, правильно для Fortran IV або близько того. Не впевнений, чи у сучасних FORTRAN все ще немає покажчиків, динамічної меорії тощо
Ingo

2
Це та сама причина, яку ми часто опускаємо до трохи вбудованої збірки, коли розробляємо C та C ++ в ігровій індустрії. Люди можуть стверджувати так часто, як їм подобається, що "компілятори можуть оптимізувати краще, ніж люди, що пишуть збірку", насправді, введення вказівника означає, що вони часто не можуть . Код, який ми можемо написати вручну, технічно є незаконним для видачі компілятора, знаючи, що він не робить нічого про псевдонім вказівника.
Carson63000

5
restrictКлючове слово C дозволяє автору функції вказати, що вказівник не має псевдонімів. Чи достатньо цього для усунення різниці, чи є в ньому більше?
бк.

@bk.: "обмежувати" атаки "половини проблеми"; це дозволяє сказати, що конкретний покажчик не буде псевдонімом нічого іншого протягом свого життя, але немає способу сказати компілятору, що об’єкт, адреса якого передано функції, не буде псевдонімом нічим, як тільки ця функція повернеться.
суперкат

8

Повністю невірне порівняння.

По-перше, як зазначає @ Péter Török, спочатку потрібно порівняти кількість рядків в еквівалентних програмах від Fortran і C, щоб це було навіть достовірним порівнянням за кількістю створених рядків.

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

Крім того, довгий запуск коду може бути швидшим, оскільки це призводить до меншої кількості рядків виконання (тобто, кількість рядків! = Кількість виконаних рядків ).


5

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

Я не експерт по Fortran, я знаю небагато. Порівнюючи їх, я думаю, що добре написаний C зробив би набагато кращі показники роботи зі складнішими структурами даних та функціональністю, ніж Fortran. Хтось (будь ласка) виправить мене, якщо я тут помиляюся, але я думаю, що Фортран дещо на «нижчому рівні», ніж C. Якщо так, я впевнений, що деякі проблеми вийдуть швидше на Фортран.

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


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

4

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


4

Це твердження, можливо, було правдою в старі часи (приблизно в кінці 70-х років), коли C був у зародковому стані, а Fortran був підтриманий усіма основними виробниками і був дуже оптимізований. Early Fortrans базувався на архітектурі IBM настільки прості речі, як арифметика, якби, безумовно, було б одне твердження за інструкцією зі зборки. Це стосується старих машин, таких як Data General і Prime, які мали 3-х стрибкові переходи. Це не працює на сучасних наборах інструкцій, які не мають тристороннього стрибка.

Рядки коду не відповідають твердженням коду. Раніші версії Fortran дозволяли лише одне твердження на рядок. Пізніші версії Fortran можуть приймати кілька заяв на рядок. C може мати кілька заяв на рядок. У більш швидких компіляторах виробництва, таких як IVF Intel (раніше CVF, MS Powerstation) і Intel C, різниця між ними дійсно немає. Ці компілятори дуже оптимізовані.


4

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

Розглянемо функцію:

void diff(float dest[], float src1[], float src2[], int n)
{
  for (int i=0; i<n; i++)
    dest[i] = src1[i] - src2[i];
}

якщо компілятор знав, що кожен з покажчиків ідентифікує початок масиву, він може генерувати код, який буде діяти на елементи масиву паралельно або в будь-якому порядку, оскільки для будь-яких x! = y операцій над dest [x ] не вплине на src1 [y] і src2 [y]. Наприклад, у деяких системах компілятор може отримати користь від генерування коду, еквівалентного:

void dif(float dest[], float src1[], float src2[], int n)
{
  int i=0;
  float t1a,t1b,t2a,t2b,tsa,tsb;
  if (n > 2)
  {
    n-=4;
    t1a = src1[n+3]; t1b = src2[n+3]; t1b=src2[n+2]; t2b = src2[n+2];
    do
    {
      tsa = t1a-t2a;
      t1a = src1[n+1]; t2a = src2[n+1]; 
      tsb = t2b-t2b;
      dest[n+3] = tsa;
      t1b = src1[n]; t2b = src2[n]; 
      n-=2;
      dest[n+4] = tsb;
    } while(n >= 0);
    ... add some extra code to handle cleanup
  }
  else
    ... add some extra code to handle small values of n
}

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

Хоча програміст C міг би спробувати досягти порівнянної продуктивності, явно виписавши код, який розкрутив цикл і перекрив операції суміжних проходів, такий код міг легко погіршити продуктивність, якщо він використовував би стільки автоматичних змінних, що компілятору довелося "розлити" їх на пам'ять. Оптимізатор компілятора FORTRAN, ймовірно, знає більше, ніж програміст про те, які форми переплетення дають оптимальну ефективність у визначеному сценарії, і такі рішення часто краще залишити таким компіляторам. У той час як C99 намагалися поліпшити ситуацію Кассіопеяна кілька, додавши restrictспецифікатор, який може бути використаний тільки тут , якщо dest[]був окремий масив з обох src1[]і src2[], або , якщо програміст додав окремі версії циклу для обробки випадків , коли всеdest було НЕ перетинається зsrc1і src2, деsrc1[]і destбули рівними, і src2були непересічними, де src2[]і dest[]були рівними, і src1були непересічними, і де всі три масиви були рівними. FORTRAN, навпаки, може без проблем обробляти всі чотири випадки, використовуючи той самий вихідний код і той же машинний код.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.