Pertanyaan ini merupakan perpanjangan dari dua diskusi yang muncul baru-baru ini di balasan untuk " C ++ vs Fortran untuk HPC ". Dan ini sedikit lebih menantang daripada sebuah pertanyaan ...
Salah satu argumen yang paling sering terdengar dalam mendukung Fortran adalah bahwa penyusunnya lebih baik. Karena sebagian besar kompiler C / Fortran berbagi ujung belakang yang sama, kode yang dihasilkan untuk program semantik yang setara dalam kedua bahasa harus identik. Satu dapat berpendapat, bagaimanapun, bahwa C / Fortran lebih / kurang mudah untuk kompiler untuk mengoptimalkan.
Jadi saya memutuskan untuk mencoba tes sederhana: Saya mendapat salinan daxpy.f dan daxpy.c dan mengompilasinya dengan gfortran / gcc.
Sekarang daxpy.c hanyalah terjemahan f2c dari daxpy.f (kode yang dibuat secara otomatis, jelek sekali), jadi saya mengambil kode itu dan membersihkannya sedikit (ketemu daxpy_c), yang pada dasarnya berarti menulis ulang loop paling dalam sebagai
for ( i = 0 ; i < n ; i++ )
dy[i] += da * dx[i];
Akhirnya, saya menulis ulang (masukkan daxpy_cvec) menggunakan sintaks vektor gcc:
#define vector(elcount, type) __attribute__((vector_size((elcount)*sizeof(type)))) type
vector(2,double) va = { da , da }, *vx, *vy;
vx = (void *)dx; vy = (void *)dy;
for ( i = 0 ; i < (n/2 & ~1) ; i += 2 ) {
vy[i] += va * vx[i];
vy[i+1] += va * vx[i+1];
}
for ( i = n & ~3 ; i < n ; i++ )
dy[i] += da * dx[i];
Perhatikan bahwa saya menggunakan vektor dengan panjang 2 (itu semua memungkinkan SSE2) dan saya memproses dua vektor sekaligus. Ini karena pada banyak arsitektur, kita mungkin memiliki unit multiplikasi lebih banyak daripada elemen vektor.
Semua kode dikompilasi menggunakan gfortran / gcc versi 4.5 dengan flag "-O3 -Wall -msse2 -march = asli -fast-matematika -fomit-frame-pointer -malign-double -fstrict-aliasing". Di laptop saya (Intel Core i5 CPU, M560, 2.67GHz) saya mendapat output sebagai berikut:
pedro@laika:~/work/fvsc$ ./test 1000000 10000
timing 1000000 runs with a vector of length 10000.
daxpy_f took 8156.7 ms.
daxpy_f2c took 10568.1 ms.
daxpy_c took 7912.8 ms.
daxpy_cvec took 5670.8 ms.
Jadi kode Fortran asli membutuhkan waktu lebih dari 8,1 detik, terjemahan otomatisnya membutuhkan waktu 10,5 detik, implementasi C naif melakukannya di 7,9 dan kode vektor secara eksplisit melakukannya dalam 5,6, sedikit kurang.
Itu Fortran sedikit lebih lambat dari implementasi C naif dan 50% lebih lambat dari implementasi C vektor.
Jadi inilah pertanyaannya: Saya seorang programmer C asli dan jadi saya cukup yakin bahwa saya melakukan pekerjaan dengan baik pada kode itu, tetapi kode Fortran terakhir kali disentuh pada tahun 1993 dan karenanya mungkin agak ketinggalan zaman. Karena saya tidak merasa nyaman dengan pengkodean di Fortran seperti yang dilakukan orang lain di sini, adakah yang bisa melakukan pekerjaan yang lebih baik, yaitu lebih kompetitif dibandingkan dengan salah satu dari dua versi C?
Adakah yang bisa mencoba tes ini dengan icc / ifort? Sintaks vektor mungkin tidak akan berfungsi, tetapi saya akan penasaran untuk melihat bagaimana versi C naif berperilaku di sana. Hal yang sama berlaku untuk siapa pun dengan xlc / xlf berbaring.
Saya telah mengunggah sumber dan Makefile di sini . Untuk mendapatkan ketepatan waktu, atur CPU_TPS di test.c ke jumlah Hz di CPU Anda. Jika Anda menemukan peningkatan pada salah satu versi, silakan posting di sini!
Memperbarui:
Saya telah menambahkan kode uji stali ke file online dan menambahkannya dengan versi C. Saya memodifikasi program untuk melakukan 1'000'000 loop pada vektor dengan panjang 10'000 agar konsisten dengan tes sebelumnya (dan karena mesin saya tidak dapat mengalokasikan vektor dengan panjang 1'000'000'000, seperti pada stali's original kode). Karena jumlahnya sekarang sedikit lebih kecil, saya menggunakan opsi -par-threshold:50
untuk membuat kompiler lebih mungkin untuk diparalelkan. Versi icc / ifort yang digunakan adalah 12.1.2 20111128 dan hasilnya adalah sebagai berikut
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./icctest_c
3.27user 0.00system 0:03.27elapsed 99%CPU
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./icctest_f
3.29user 0.00system 0:03.29elapsed 99%CPU
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./icctest_c
4.89user 0.00system 0:02.60elapsed 188%CPU
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./icctest_f
4.91user 0.00system 0:02.60elapsed 188%CPU
Singkatnya, hasilnya, untuk semua tujuan praktis, identik untuk versi C dan Fortran, dan kedua kode diparalelkan secara otomatis. Perhatikan bahwa waktu yang cepat dibandingkan dengan pengujian sebelumnya adalah karena penggunaan aritmatika titik apung presisi tunggal!
Memperbarui:
Meskipun saya tidak begitu suka di mana beban pembuktian akan terjadi di sini, saya telah mengkodekan ulang contoh perkalian matriks stali di C dan menambahkannya ke file di web . Berikut ini adalah hasil dari tripple loop untuk satu dan dua CPU:
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 2500
triple do time 3.46421700000000
3.63user 0.06system 0:03.70elapsed 99%CPU
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_c 2500
triple do time 3.431997791385768
3.58user 0.10system 0:03.69elapsed 99%CPU
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 2500
triple do time 5.09631900000000
5.26user 0.06system 0:02.81elapsed 189%CPU
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_c 2500
triple do time 2.298916975280899
4.78user 0.08system 0:02.62elapsed 184%CPU
Perhatikan bahwa cpu_time
dalam Fortran mengukur waktu CPU dan bukan waktu jam dinding, jadi saya membungkus panggilan time
untuk membandingkannya dengan 2 CPU. Tidak ada perbedaan nyata antara hasil, kecuali bahwa versi C melakukan sedikit lebih baik pada dua core.
Sekarang untuk matmul
perintah, tentu saja hanya di Fortran karena intrinsik ini tidak tersedia dalam C:
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 2500
matmul time 23.6494780000000
23.80user 0.08system 0:23.91elapsed 99%CPU
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 2500
matmul time 26.6176640000000
26.75user 0.10system 0:13.62elapsed 197%CPU
Wow. Benar-benar mengerikan. Adakah yang bisa menemukan kesalahan saya, atau menjelaskan mengapa intrinsik ini masih merupakan hal yang baik?
Saya tidak menambahkan dgemm
panggilan ke patokan karena mereka panggilan perpustakaan ke fungsi yang sama di Intel MKL.
Untuk tes selanjutnya, adakah yang bisa menyarankan contoh yang diketahui lebih lambat dalam C daripada di Fortran?
Memperbarui
Untuk memverifikasi klaim stali bahwa matmul
intrinsiknya adalah "urutan magnitue" lebih cepat daripada produk matriks eksplisit pada matriks yang lebih kecil, saya memodifikasi kodenya sendiri untuk melipatgandakan matriks ukuran 100x100 menggunakan kedua metode, masing-masing 10'000 kali. Hasilnya, pada satu dan dua CPU, adalah sebagai berikut:
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=1 time ./mm_test_f 10000 100
matmul time 3.61222500000000
triple do time 3.54022200000000
7.15user 0.00system 0:07.16elapsed 99%CPU
pedro@laika:~/work/fvsc$ OMP_NUM_THREADS=2 time ./mm_test_f 10000 100
matmul time 4.54428400000000
triple do time 4.31626900000000
8.86user 0.00system 0:04.60elapsed 192%CPU
Memperbarui
Grisu benar dalam menunjukkan bahwa, tanpa optimisasi, gcc mengubah operasi pada bilangan kompleks menjadi panggilan fungsi pustaka sementara gfortran menyatukannya dalam beberapa instruksi.
Kompiler C akan menghasilkan kode ringkas yang sama jika opsi -fcx-limited-range
disetel, yaitu kompiler diinstruksikan untuk mengabaikan potensi kelebihan / kekurangan aliran dalam nilai-nilai perantara. Opsi ini entah bagaimana diatur secara default di gfortran dan dapat menyebabkan hasil yang salah. Memaksa -fno-cx-limited-range
gfortran tidak mengubah apa pun.
Jadi ini sebenarnya adalah argumen yang menentang penggunaan gfortran untuk perhitungan numerik: Operasi pada nilai kompleks mungkin over / under-flow bahkan jika hasil yang benar berada dalam kisaran floating-point. Ini sebenarnya adalah standar Fortran. Di gcc, atau di C99 secara umum, defaultnya adalah melakukan hal-hal dengan ketat (baca IEEE-754 compliant) kecuali ditentukan lain.
Pengingat: Harap diingat bahwa pertanyaan utama adalah apakah kompiler Fortran menghasilkan kode yang lebih baik daripada kompiler C. Ini bukan tempat untuk diskusi tentang manfaat umum dari satu bahasa di atas yang lain. Apa yang saya akan benar-benar tertarik adalah jika ada yang bisa menemukan cara membujuk gfortran untuk menghasilkan daxpy seefisien yang ada di C menggunakan vektorisasi eksplisit karena ini mencontohkan masalah harus bergantung pada kompiler khusus untuk optimasi SIMD, atau kasus di mana kompiler Fortran keluar-melakukan rekan C-nya.