Kesimpulannya di sini:
Seberapa jauh sebenarnya penyusun Fortran?
adalah gfortran dan gcc yang cepat untuk kode sederhana. Jadi saya ingin mencoba sesuatu yang lebih rumit. Saya mengambil contoh baku tembak norma spektral. Saya pertama-tama menghitung ulang matriks 2D A (:, :), dan kemudian menghitung norma. (Solusi ini tidak diperbolehkan pada baku tembak saya pikir.) Saya telah mengimplementasikan versi Fortran dan C. Ini kodenya:
https://github.com/certik/spectral_norm
Versi gfortran tercepat adalah spectral_norm2.f90 dan spectral_norm6.f90 (satu menggunakan matranul dan dot_prodp bawaan Fortran, yang lain mengimplementasikan kedua fungsi ini dalam kode - tanpa perbedaan kecepatan). Kode C / C ++ tercepat yang bisa saya tulis adalah spectral_norm7.cpp. Pengaturan waktu pada versi git 457d9d9 pada laptop saya adalah:
$ time ./spectral_norm6 5500
1.274224153
real 0m2.675s
user 0m2.520s
sys 0m0.132s
$ time ./spectral_norm7 5500
1.274224153
real 0m2.871s
user 0m2.724s
sys 0m0.124s
Jadi versi gfortran sedikit lebih cepat. Mengapa demikian? Jika Anda mengirim permintaan tarik dengan implementasi C yang lebih cepat (atau hanya menempelkan kode), saya akan memperbarui repositori.
Di Fortran saya melewatkan array 2D, sedangkan di CI menggunakan array 1D. Jangan ragu untuk menggunakan array 2D atau cara lain yang Anda inginkan.
Mengenai kompiler, mari kita bandingkan gcc vs gfortran, icc vs ifort, dan sebagainya. (Berbeda dengan halaman shootout, yang membandingkan ifort vs gcc.)
Pembaruan : menggunakan versi 179dae2, yang meningkatkan matmul3 () dalam versi C saya, sekarang lebih cepat:
$ time ./spectral_norm6 5500
1.274224153
real 0m2.669s
user 0m2.500s
sys 0m0.144s
$ time ./spectral_norm7 5500
1.274224153
real 0m2.665s
user 0m2.472s
sys 0m0.168s
Versi vektor Pedro di bawah ini lebih cepat:
$ time ./spectral_norm8 5500
1.274224153
real 0m2.523s
user 0m2.336s
sys 0m0.156s
Akhirnya, seperti yang dilaporkan laxxy di bawah ini untuk kompiler Intel, sepertinya tidak ada perbedaan besar di sana dan bahkan kode Fortran yang paling sederhana (spectral_norm1) adalah yang tercepat.
Jawaban:
Pertama-tama, terima kasih telah mengirimkan pertanyaan / tantangan ini! Sebagai penafian, saya seorang programmer C asli dengan beberapa pengalaman Fortran, dan merasa paling nyaman di C, jadi dengan demikian, saya hanya akan fokus pada peningkatan versi C. Saya mengundang semua peretas Fortran untuk ikut serta!
Hanya untuk mengingatkan pendatang baru tentang apa ini: Premis dasar di utas ini adalah bahwa gcc / fortran dan icc / ifort harus, karena mereka masing-masing memiliki back-end yang sama, menghasilkan kode yang setara untuk program yang sama (identik secara semantik), terlepas dari itu berada di C atau Fortran. Kualitas hasil hanya tergantung pada kualitas implementasi masing-masing.
Saya bermain-main dengan kode sedikit, dan di komputer saya (ThinkPad 201x, Intel Core i5 M560, 2,67 GHz), menggunakan
gcc
4.6.1 dan flag kompiler berikut:Saya juga pergi ke depan dan menulis versi C-bahasa SIMD-Vectorized dari C ++ code,
spectral_norm_vec.c
:Ketiga versi dikompilasi dengan bendera yang sama dan
gcc
versi yang sama . Perhatikan bahwa saya membungkus panggilan fungsi utama dalam satu lingkaran dari 0..9 untuk mendapatkan pengaturan waktu yang lebih akurat.Jadi dengan flag kompiler "lebih baik", versi C ++ mengalahkan versi Fortran dan loop vektorisasi kode tangan hanya memberikan peningkatan marjinal. Pandangan cepat pada assembler untuk versi C ++ menunjukkan bahwa loop utama juga telah di-vektor-kan, meskipun tidak dikontrol lebih agresif.
Saya juga melihat assembler yang dihasilkan oleh
gfortran
dan inilah kejutan besar: tidak ada vektorisasi. Saya mengaitkan fakta bahwa hanya sedikit lebih lambat karena masalah terbatasnya bandwidth, setidaknya pada arsitektur saya. Untuk setiap perkalian matriks, data 230MB dilalui, yang cukup banyak menukar semua level cache. Jika Anda menggunakan nilai input yang lebih kecil, misalnya100
, perbedaan kinerja tumbuh secara signifikan.Sebagai catatan tambahan, alih-alih terobsesi dengan vektorisasi, penjajaran, dan flag kompiler, optimasi yang paling jelas adalah menghitung beberapa iterasi pertama dalam aritmatika presisi tunggal, sampai kita memiliki ~ 8 digit hasilnya. Instruksi presisi tunggal tidak hanya lebih cepat, tetapi jumlah memori yang harus dipindahkan juga berkurang setengahnya.
sumber
gcc
/gfortran
yang Anda gunakan? Pada utas sebelumnya, versi yang berbeda memberikan hasil yang sangat berbeda.matmul2
dalam versi Fortran secara semantik setara denganmatmul3
dalam versi C saya. Kedua versi benar-benar sekarang sama dan karenanyagcc
/gfortran
harus menghasilkan hasil yang sama untuk keduanya, misalnya tidak ada satu front-end / bahasa yang lebih baik daripada yang lain dalam hal ini.gcc
hanya memiliki keuntungan bahwa kita dapat mengeksploitasi instruksi vektor jika kita memilih.vector_size
atribut untuk membuat platform kode-independen, yaitu menggunakan sintaks ini,gcc
harus dapat menghasilkan kode vektor untuk platform lain misalnya menggunakan AltiVec pada arsitektur IBM Power.jawaban user389 telah dihapus tetapi izinkan saya menyatakan bahwa saya dengan kuat di kampnya: Saya gagal melihat apa yang kita pelajari dengan membandingkan tolok ukur mikro dalam berbagai bahasa. Tidak terlalu mengejutkan bagi saya bahwa C dan Fortran mendapatkan kinerja yang hampir sama pada benchmark ini mengingat betapa pendeknya itu. Tetapi tolok ukur ini juga membosankan karena dapat dengan mudah ditulis dalam kedua bahasa dalam beberapa lusin baris. Dari sudut pandang perangkat lunak, itu bukan kasus representatif: kita harus peduli tentang perangkat lunak yang memiliki 10.000 atau 100.000 baris kode dan bagaimana kompiler melakukannya. Tentu saja, pada skala itu, seseorang akan dengan cepat menemukan hal-hal lain: bahwa bahasa A membutuhkan 10.000 baris sedangkan bahasa B membutuhkan 50.000. Atau sebaliknya, tergantung pada apa yang ingin Anda lakukan. Dan tiba-tiba '
Dengan kata lain, tidak masalah bagi saya bahwa mungkin aplikasi saya bisa 50% lebih cepat jika saya mengembangkannya di Fortran 77 jika sebaliknya hanya akan memakan waktu 1 bulan untuk menjalankannya dengan benar sementara itu akan memakan waktu 3 bulan dalam F77. Masalah dengan pertanyaan di sini adalah bahwa ia berfokus pada aspek (kernel individu) yang tidak relevan dalam praktik dalam pandangan saya.
sumber
Ternyata saya bisa menulis kode Python (menggunakan numpy untuk melakukan operasi BLAS) lebih cepat daripada kode Fortran yang dikompilasi dengan kompiler gfortran sistem saya.
foo1.py:
dan sn6a.f90, spectral_norm6.f90 yang dimodifikasi dengan sangat ringan:
sumber
Memeriksa ini dengan kompiler Intel. Dengan 11.1 (-fast, menyiratkan -O3), dan dengan 12.0 (-O2) yang tercepat adalah 1,2,6,7, dan 8 (yaitu kode Fortran dan C "paling sederhana", dan C vektor tangan) - ini tidak bisa dibedakan satu sama lain di ~ 1.5s. Tes 3 dan 5 (dengan larik sebagai fungsi) lebih lambat; # 4 Saya tidak bisa mengkompilasi.
Cukup istimewa, jika mengkompilasi dengan 12.0 dan -O3, daripada -O2, 2 ("paling sederhana") pertama kode Fortran memperlambat BANYAK (1,5 -> 10,2 detik) - ini bukan pertama kalinya saya melihat sesuatu seperti ini, tapi ini mungkin contoh paling dramatis. Jika ini masih terjadi dalam rilis saat ini, saya pikir itu akan menjadi ide yang baik untuk melaporkannya ke Intel, karena jelas ada sesuatu yang salah dengan optimasi mereka dalam kasus yang agak sederhana ini.
Kalau tidak, saya setuju dengan Jonathan bahwa ini bukan latihan yang sangat informatif :)
sumber