Apakah kompiler Fortran benar-benar menghasilkan kode lebih cepat daripada kompiler C?

17

Ketika saya belajar di universitas saya sering mendengar gagasan bahwa kompiler Fortran menghasilkan kode lebih cepat daripada kompiler C untuk program yang setara.

Alasan utamanya seperti ini: kompiler Fortran memancarkan rata-rata 1,1 instruksi prosesor per baris kode, sedangkan kompiler C mengeluarkan rata-rata 1,6 instruksi prosesor per baris kode - Saya tidak ingat angka pastinya tetapi Gagasannya adalah bahwa kompiler C memancarkan lebih banyak kode mesin dan karenanya menghasilkan program yang lebih lambat.

Seberapa valid perbandingan itu? Bisakah kita mengatakan bahwa kompiler Fortran menghasilkan program yang lebih cepat daripada kompiler C atau sebaliknya dan mengapa perbedaan ini ada?

sharptooth
sumber
19
Itu mungkin berarti bahwa program-program Fortran lebih bertele-tele daripada C ... Suatu perbandingan yang bermakna hanya dapat dilakukan dengan mengimplementasikan fungsi yang sama dalam kedua bahasa dan membandingkan kode mesin yang dihasilkan (ukuran dan kecepatan).
Péter Török
Juga, apakah kode yang dihasilkan mendukung eksekusi paralel?
@ Péter Török, itu hanya berarti bahwa, katakanlah, BLAS dan LAPACK di Fortran digunakan untuk melakukan jauh lebih baik daripada port C / C ++ mereka. Sekarang celahnya menyusut dengan cepat.
SK-logic
6
Anda hanya bisa berpendapat bahwa satu kompiler menghasilkan kode yang lebih cepat jika Anda memiliki program setara 100% dalam kedua bahasa, yang ditulis oleh para ahli yang mengetahui kompiler mereka dan siapa yang dapat menjelaskan kinerja.
Falcon
Mantan Fortran tidak mendukung rekursi dan karenanya tidak perlu harus mendorong argumen pemanggilan fungsi ke stack karena akan ada ruang yang dialokasikan secara statis untuk argumen masing-masing fungsi. Ini adalah salah satu alasan mengapa mungkin lebih cepat. Saya kira Anda dapat menemukan jawaban yang lebih lengkap di sini: amazon.com/Programming-Language-Pragmatics-Third-Edition/dp/…
Pedro Rolo

Jawaban:

36

IIRC salah satu alasan utama mengapa Fortran dikatakan lebih cepat adalah tidak adanya pointer aliasing , sehingga mereka dapat menggunakan optimasi yang tidak dapat digunakan oleh kompiler C:

Dalam FORTRAN, argumen fungsi mungkin tidak saling alias, dan kompilator menganggap mereka tidak. Ini memungkinkan pengoptimalan yang sangat baik, dan merupakan salah satu alasan utama reputasi FORTRAN sebagai bahasa cepat. (Perhatikan bahwa aliasing mungkin masih terjadi dalam fungsi FORTRAN. Misalnya, jika A adalah array dan i dan j adalah indeks yang memiliki nilai yang sama, maka A [i] dan A [j] adalah dua nama berbeda untuk lokasi memori yang sama. Untungnya, karena array basis harus memiliki nama yang sama, analisis indeks dapat dilakukan untuk menentukan kasus di mana A [i] dan A [j] tidak dapat alias.)

Tapi saya setuju dengan yang lain di sini: Membandingkan jumlah rata-rata instruksi assembler yang dihasilkan untuk satu baris kode adalah omong kosong. Misalnya core x86 modern dapat menjalankan dua instruksi secara paralel jika mereka tidak mengakses register yang sama. Jadi Anda bisa (secara teori) mendapatkan peningkatan kinerja 100% untuk set instruksi yang sama hanya dengan memesan ulang mereka . Kompiler yang baik juga akan sering menghasilkan instruksi perakitan lebih banyak untuk mendapatkan kode lebih cepat (pikirkan loop membuka gulungan, inlining). Jumlah instruksi assembler mengatakan sangat sedikit tentang kinerja sepotong kode.

nikie
sumber
Alasan lain untuk optimisasi yang lebih baik adalah dukungan asli untuk bilangan kompleks.
SK-logic
Benar untuk Fortran IV atau lebih. Tidak yakin apakah FORTRAN modern masih tidak memiliki pointer, dinamis, dll.
Ingo
2
Itulah alasan yang sama kita sering jatuh ke sedikit perakitan inline ketika berkembang di C dan C ++ di industri game. Orang dapat mengklaim sesering mereka suka bahwa "kompiler dapat mengoptimalkan lebih baik daripada manusia menulis perakitan", faktanya, pointer aliasing berarti bahwa mereka sering tidak bisa . Kode yang dapat kita tulis dengan tangan secara teknis ilegal bagi kompiler untuk memancarkannya, karena ia tidak tahu apa-apa tentang pointer alias.
Carson63000
5
restrictKata kunci C memungkinkan pembuat fungsi menentukan bahwa pointer tidak memiliki alias. Apakah ini cukup untuk mengatasi perbedaannya, atau ada lebih dari itu?
bk.
@ bk .: C "batasi" serangan "setengah masalah"; itu memungkinkan untuk mengatakan bahwa pointer tertentu tidak akan alias apa pun dalam masa pakainya, tetapi tidak ada cara untuk memberitahu kompiler bahwa objek yang alamatnya dilewatkan ke suatu fungsi tidak akan disebut dengan apa pun setelah fungsi itu kembali.
supercat
8

Perbandingan yang sama sekali tidak valid.

Pertama, seperti yang ditunjukkan oleh @ Péter Török, Anda harus terlebih dahulu membandingkan jumlah baris dalam program setara dari Fortran dan C agar ini menjadi perbandingan yang valid pada jumlah baris yang dihasilkan.

Kedua, lebih sedikit baris kode tidak selalu sama dengan program yang lebih cepat . Tidak semua instruksi mesin menggunakan jumlah siklus yang sama untuk dieksekusi , tetapi Anda juga memiliki masalah lain seperti akses memori , caching , dll.

Di atas itu semua, kode panjang berjalan bisa lebih cepat karena menghasilkan jumlah baris eksekusi yang lebih rendah (mis., Hitungan Baris! = Hitungan Baris yang Dieksekusi ).

Dan McGrath
sumber
5

Dan benar, program yang lebih lama bukan berarti program lebih lambat. Ini sangat tergantung pada apa yang mereka lakukan.

Saya bukan ahli Fortran, saya tahu sedikit. Membandingkan mereka, saya akan berpikir C ditulis dengan baik akan melakukan jauh lebih baik dalam kinerja dengan struktur data yang lebih kompleks dan fungsionalitas daripada Fortran. Seseorang (tolong) koreksi saya jika saya salah di sini, tapi saya pikir Fortran agak pada 'tingkat yang lebih rendah' ​​daripada C. Jika demikian, saya yakin untuk beberapa masalah akan keluar lebih cepat pada Fortran.

Hal lain, pada pandangan pertama saya pikir Anda bertanya apakah kompiler lebih cepat. Saya benar-benar berpikir bahwa Fortran umumnya akan mengkompilasi lebih cepat untuk jumlah kode yang serupa, tetapi program yang dihasilkan dan cara kerjanya akan menjadi cerita yang berbeda. Hanya lebih mudah untuk diuraikan.

Garet Claborn
sumber
2
Jika Anda menggunakan struktur data yang rumit FORTRAN mungkin merupakan pilihan yang salah. FORTRAN dioptimalkan untuk menghasilkan angka sederhana dengan sangat cepat.
Zachary K
4

Saya pikir bagian dari itu adalah bahwa kompiler FORTRAN dirancang untuk melakukan beberapa jenis matematika dengan sangat cepat. Itulah sebabnya mengapa orang menggunakan FORTRAN, untuk melakukan perhitungan secepat mungkin

Zachary K
sumber
4

Pernyataan itu mungkin benar di masa lalu (sekitar akhir 70-an) ketika C masih dalam masa pertumbuhan, dan Fortran didukung oleh semua produsen besar dan sangat dioptimalkan. Fortrans awal didasarkan pada arsitektur IBM hal-hal yang sangat sederhana seperti aritmatika jika tentu akan menjadi satu pernyataan per instruksi perakitan. Ini berlaku untuk mesin lama seperti Data General dan Prime, yang memiliki lompatan 3 arah. Ini tidak berfungsi pada set instruksi modern yang tidak memiliki lompatan 3 arah.

Baris kode tidak sama dengan pernyataan kode. Versi Fortran sebelumnya hanya membolehkan satu pernyataan per baris. Versi Fortran yang lebih baru dapat mengambil banyak pernyataan per baris. C dapat memiliki beberapa pernyataan per baris. Pada kompiler produksi yang lebih cepat seperti Intel IVF (sebelumnya CVF, MS Powerstation) dan Intel C, benar-benar tidak ada perbedaan antara keduanya. Kompiler ini sangat dioptimalkan.

cangkir
sumber
4

FORTRAN gaya lama mensyaratkan bahwa seorang programmer yang ingin membuat bagian dari array tersedia untuk fungsi yang diperlukan untuk meneruskan referensi ke seluruh array, bersama dengan satu atau lebih nilai integer yang menentukan subscript awal dan juga subscript akhir atau jumlah item . C memungkinkan untuk menyederhanakan ini dengan melewatkan pointer ke awal bagian bunga bersama dengan jumlah elemen. Dalam istilah langsung, ini akan membuat segalanya lebih cepat (melewati dua hal daripada tiga). Namun, secara tidak langsung, ini mungkin memperlambat segalanya dengan membatasi jenis pengoptimalan yang dapat dilakukan oleh kompiler.

Pertimbangkan fungsinya:

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

jika kompilator tahu bahwa masing-masing pointer akan mengidentifikasi awal array, itu dapat menghasilkan kode yang akan bertindak atas elemen-elemen array secara paralel, atau dalam urutan apa pun, karena untuk setiap x! = y, operasi pada dest [x ] tidak akan memengaruhi src1 [y] atau src2 [y]. Sebagai contoh, pada beberapa sistem kompiler dapat mengambil manfaat dari menghasilkan kode yang setara dengan:

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
}

Perhatikan bahwa setiap operasi yang memuat atau menghitung nilai memiliki setidaknya satu operasi lagi di antara itu dan operasi berikutnya yang menggunakan nilai itu. Beberapa prosesor dapat tumpang tindih pemrosesan operasi yang berbeda ketika kondisi tersebut terpenuhi, sehingga meningkatkan kinerja. Perhatikan, bagaimanapun, bahwa karena kompiler C tidak memiliki cara untuk mengetahui bahwa kode tidak akan melewati pointer ke sebagian- daerah tumpang tindih array umum, kompiler C tidak dapat melakukan transformasi di atas. Kompiler FORTRAN yang diberikan kode yang setara, bagaimanapun, dapat dan memang melakukan transformasi seperti itu.

Sementara seorang programmer C dapat mencoba untuk mencapai kinerja yang sebanding dengan secara eksplisit menulis kode yang membuka gulungan dan tumpang tindih operasi pass yang berdekatan, kode tersebut dapat dengan mudah menurunkan kinerja jika menggunakan begitu banyak variabel otomatis sehingga kompiler harus "menumpahkan" mereka untuk Penyimpanan. Pengoptimal kompiler FORTRAN kemungkinan akan tahu lebih banyak daripada seorang programmer tentang bentuk interleaving apa yang akan menghasilkan kinerja optimal dalam skenario tertentu, dan keputusan seperti itu sering kali lebih baik diserahkan kepada kompiler seperti itu. Sementara C99 berusaha memperbaiki situasi C agak dengan menambahkan restrictkualifikasi, yang hanya bisa digunakan di sini jika dest[]adalah array yang terpisah dari keduanya src1[]dan src2[], atau jika programmer menambahkan versi loop yang terpisah untuk menangani kasus-kasus di mana semua destterlepas darisrc1dan src2, di mana src1[]dan destsama dan src2terpisah, di mana src2[]dan dest[]sama dan src1terpisah, dan di mana ketiga array sama. FORTRAN, sebaliknya, dapat menangani keempat kasus tanpa kesulitan menggunakan kode sumber yang sama dan kode mesin yang sama.

supercat
sumber