Seberapa jauh sebenarnya penyusun Fortran?

74

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:50untuk 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_timedalam Fortran mengukur waktu CPU dan bukan waktu jam dinding, jadi saya membungkus panggilan timeuntuk membandingkannya dengan 2 CPU. Tidak ada perbedaan nyata antara hasil, kecuali bahwa versi C melakukan sedikit lebih baik pada dua core.

Sekarang untuk matmulperintah, 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 dgemmpanggilan 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 matmulintrinsiknya 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-rangedisetel, 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-rangegfortran 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.

Pedro
sumber
Satu masalah waktu adalah bahwa jika prosesor Anda melakukan mode stepping / turbo frekuensi, hasil ini bisa di seluruh peta.
Bill Barth
1
Daxpy_c.c Anda saat ini memperbarui x dengan banyak x dan sama sekali tidak menyentuh y. Anda mungkin ingin memperbaikinya agar adil ...
Jack Poulson
1
@JackPoulson: Hasil tangkapan yang bagus, diperbaiki dan diperbarui hasilnya.
Pedro
2
Juga, saya cukup yakin bahwa perbedaannya sepenuhnya karena manual membuka gulungan dalam versi Fortran membingungkan kompiler. Ketika saya menggantinya dengan loop sederhana yang sama yang Anda masukkan ke versi C Anda, kinerja antara keduanya hampir identik. Tanpa perubahan, versi Fortran lebih lambat dengan kompiler Intel.
Jack Poulson
1
@permeakra: Sebenarnya, standar C99 menentukan restrictkata kunci yang memberitahu kompiler persis bahwa: untuk menganggap bahwa array tidak tumpang tindih dengan struktur data lainnya.
Pedro

Jawaban:

37

Perbedaan dalam timing Anda tampaknya karena manual membuka gulungan Fortran daxpy unit-stride . Pengaturan waktu berikut ini pada 2,67 GHz Xeon X5650, menggunakan perintah

./test 1000000 10000

Kompiler Intel 11.1

Fortran dengan membuka gulungan manual: 8,7 detik
Fortran tanpa membuka gulungan manual: 5,8 detik
C tanpa membuka gulungan manual: 5,8 detik

Kompiler GNU 4.1.2

Fortran dengan membuka gulungan manual: 8,3 detik
Fortran tanpa membuka gulungan manual: 13,5 detik
C tanpa membuka gulungan manual: 13,6 detik
C dengan atribut vektor: 5,8 detik

Kompiler GNU 4.4.5

Fortran dengan membuka gulungan manual: 8,1 detik
Fortran tanpa membuka gulungan manual: 7,4 dtk
w / o membuka gulungan manual: 8,5 sec
C dengan atribusi vektor: 5,8 dtk

Kesimpulan

  • Membuka gulungan manual membantu GNU 4.1.2 Fortran kompiler pada arsitektur ini, tetapi merusak versi yang lebih baru (4.4.5) dan kompiler Intel Fortran.
  • Kompiler GNU 4.4.5 C jauh lebih kompetitif dengan Fortran daripada versi 4.2.1.
  • Vektor intrinsik memungkinkan kinerja GCC cocok dengan kompiler Intel.

Waktu untuk menguji rutinitas yang lebih rumit seperti dgemv dan dgemm?

Jack Poulson
sumber
Terima kasih untuk hasilnya! Versi gcc apa yang Anda gunakan dan dapatkah Anda lebih spesifik tentang CPU?
Pedro
2
Kompiler Anda lebih tua dari CPU Anda ... Bisakah Anda mencoba dengan gcc-4.5?
Pedro
1
Saya baru saja mencobanya. Versi vektor dengan GCC 4.4.5 sama persis dengan hasil Intel 11.1.
Jack Poulson
1
Saya baru saja menginstal gcc / gfortran versi 4.4.5 dan saya tidak dapat mereproduksi perbedaan tanpa membuka gulungan. Bahkan, dalam assembler yang dihasilkan untuk kedua kasus, loop paling dalam identik kecuali untuk nama register yang digunakan, yang dapat dipertukarkan. Bisakah Anda menjalankan kembali tes hanya untuk memastikan?
Pedro
4
Bisakah kita mengatakan ini menyelesaikan debat lama "kita terus menggunakan fortran karena lebih berkinerja", sehingga kita akhirnya bisa membuangnya di tempat sampah?
Stefano Borini
16

Saya datang terlambat ke pesta ini, jadi sulit bagi saya untuk mengikuti bolak-balik dari semua di atas. Pertanyaannya besar, dan saya pikir jika Anda tertarik itu bisa dipecah menjadi potongan-potongan kecil. Satu hal yang membuat saya tertarik hanyalah kinerja daxpyvarian Anda , dan apakah Fortran lebih lambat dari C pada kode yang sangat sederhana ini.

Menjalankan keduanya di laptop saya (Macbook Pro, Intel Core i7, 2.66 GHz), kinerja relatif dari versi C tangan-vektor Anda dan versi Fortran yang tidak di-vektorisasi tangan bergantung pada kompiler yang digunakan (dengan pilihan Anda sendiri):

Compiler     Fortran time     C time
GCC 4.6.1    5408.5 ms        5424.0 ms
GCC 4.5.3    7889.2 ms        5532.3 ms
GCC 4.4.6    7735.2 ms        5468.7 ms

Jadi, sepertinya GCC menjadi lebih baik dalam vektorisasi loop di cabang 4,6 daripada sebelumnya.


Pada debat keseluruhan, saya pikir seseorang dapat menulis kode yang cepat dan dioptimalkan baik dalam C dan Fortran, hampir seperti dalam bahasa assembly. Saya akan menunjukkan, bagaimanapun, satu hal: seperti assembler lebih membosankan untuk menulis daripada C tetapi memberi Anda kontrol yang lebih baik atas apa yang dieksekusi oleh CPU, C lebih rendah daripada Fortran. Dengan demikian, ini memberi Anda lebih banyak kontrol atas detail, yang dapat membantu mengoptimalkan, di mana sintaks standar Fortran (atau ekstensi vendornya) mungkin kurang fungsional. Satu kasus adalah penggunaan eksplisit dari jenis vektor, yang lain adalah kemungkinan menentukan keselarasan variabel dengan tangan, sesuatu yang Fortran tidak mampu.

F'x
sumber
selamat datang di scicomp! Saya setuju bahwa versi kompiler sama pentingnya dengan bahasa dalam kasus ini. Apakah yang Anda maksudkan 'bukannya' mati dalam kalimat terakhir Anda?
Aron Ahmadia
9

Cara saya menulis AXPY dalam Fortran sedikit berbeda. Ini adalah terjemahan matematika yang tepat.

m_blas.f90

 module blas

   interface axpy
     module procedure saxpy,daxpy
   end interface

 contains

   subroutine daxpy(x,y,a)
     implicit none
     real(8) :: x(:),y(:),a
     y=a*x+y
   end subroutine daxpy

   subroutine saxpy(x,y,a)
     implicit none
     real(4) :: x(:),y(:),a
     y=a*x+y
   end subroutine saxpy

 end module blas

Sekarang mari kita panggil rutin di atas dalam sebuah program.

test.f90

 program main

   use blas
   implicit none

   real(4), allocatable :: x(:),y(:)
   real(4) :: a
   integer :: n

   n=1000000000
   allocate(x(n),y(n))
   x=1.0
   y=2.0
   a=5.0
   call axpy(x,y,a)
   deallocate(x,y)

 end program main

Sekarang mari kita kompilasi dan jalankan ...

login1$ ifort -fast -parallel m_blas.f90 test.f90
ipo: remark #11000: performing multi-file optimizations
ipo: remark #11005: generating object file /tmp/ipo_iforttdqZSA.o

login1$ export OMP_NUM_THREADS=1
login1$ time ./a.out 
real    0 m 4.697 s
user    0 m 1.972 s
sys     0 m 2.548 s

login1$ export OMP_NUM_THREADS=2
login1$ time ./a.out 
real    0 m 2.657 s
user    0 m 2.060 s
sys     0 m 2.744 s

Perhatikan bahwa saya tidak menggunakan loop apa pun atau arahan OpenMP yang eksplisit . Apakah ini mungkin di C (yaitu, tidak ada penggunaan loop dan auto-parallelization)? Saya tidak menggunakan C jadi saya tidak tahu.

stali
sumber
Parallelisation otomatis adalah fitur dari kompiler Intel (baik Fortran dan C), dan bukan dari bahasa. Karenanya ekuivalen dalam C juga harus paralel. Hanya ingin tahu, bagaimana kinerjanya untuk yang lebih moderat n = 10.000?
Pedro
3
Itulah intinya. Autopar lebih mudah di Fortran karena fakta bahwa Fortran (tidak seperti C) mendukung operasi seluruh array seperti matmult, transpos dll. Jadi optimasi kode lebih mudah untuk kompiler Fortran. GFortran (yang telah Anda gunakan) tidak memiliki sumber daya pengembang untuk mengoptimalkan kompiler Fortran karena fokus mereka saat ini adalah menerapkan standar Fortran 2003 daripada optimasi.
stali
Uhmm ... Kompiler Intel C / C ++ iccjuga melakukan paralelisasi otomatis. Saya telah menambahkan file icctest.cke sumber lain. Bisakah Anda mengompilasinya dengan opsi yang sama seperti yang Anda gunakan di atas, menjalankannya, dan melaporkan waktunya? Saya harus menambahkan printf-statement ke kode saya untuk menghindari gcc mengoptimalkan semuanya. Ini hanya hack cepat dan saya harap ini bebas bug!
Pedro
Saya telah mengunduh kompiler icc / ifort terbaru dan melakukan tes sendiri. Pertanyaannya telah diperbarui untuk memasukkan hasil-hasil baru ini, yaitu bahwa otovectorization Intel berfungsi baik di Fortran maupun C.
Pedro
1
Terima kasih. Ya saya perhatikan bahwa ada sedikit perbedaan mungkin karena loop sederhana dan operasi adalah Level 1 BLAS. Tetapi seperti yang saya katakan sebelumnya karena kemampuan Fortran untuk melakukan seluruh operasi array dan penggunaan kata kunci seperti PURE / ELEMENTAL ada lebih banyak ruang untuk optimasi kompiler. Bagaimana kompiler menggunakan informasi ini dan apa yang sebenarnya dilakukannya adalah hal yang berbeda. Anda juga dapat mencoba matmul jika Anda ingin bpaste.net/show/23035
stali
6

Saya pikir, tidak hanya menarik bagaimana kompiler mengoptimalkan kode untuk perangkat keras modern. Terutama antara GNU C dan GNU Fortran pembuatan kode bisa sangat berbeda.

Jadi mari kita pertimbangkan contoh lain untuk menunjukkan perbedaan di antara mereka.

Menggunakan bilangan kompleks, kompiler GNU C menghasilkan overhead yang besar untuk operasi aritmatika yang hampir sangat mendasar pada bilangan kompleks. Kompiler Fortran memberikan kode yang jauh lebih baik. Mari kita lihat contoh kecil berikut ini di Fortran:

COMPLEX*16 A,B,C
C=A*B

memberikan (gfortran -g -o complex.fo -c complex.f95; objdump -d -S complex.fo):

C=A*B
  52:   dd 45 e0                fldl   -0x20(%ebp)
  55:   dd 45 e8                fldl   -0x18(%ebp)
  58:   dd 45 d0                fldl   -0x30(%ebp)
  5b:   dd 45 d8                fldl   -0x28(%ebp)
  5e:   d9 c3                   fld    %st(3)
  60:   d8 ca                   fmul   %st(2),%st
  62:   d9 c3                   fld    %st(3)
  64:   d8 ca                   fmul   %st(2),%st
  66:   d9 ca                   fxch   %st(2)
  68:   de cd                   fmulp  %st,%st(5)
  6a:   d9 ca                   fxch   %st(2)
  6c:   de cb                   fmulp  %st,%st(3)
  6e:   de e9                   fsubrp %st,%st(1)
  70:   d9 c9                   fxch   %st(1)
  72:   de c2                   faddp  %st,%st(2)
  74:   dd 5d c0                fstpl  -0x40(%ebp)
  77:   dd 5d c8                fstpl  -0x38(%ebp)

Yang merupakan kode mesin 39 byte. Ketika kita mempertimbangkan hal yang sama dalam C

 double complex a,b,c; 
 c=a*b; 

dan lihat outputnya (dilakukan dengan cara yang sama seperti di atas), kita mendapatkan:

  41:   8d 45 b8                lea    -0x48(%ebp),%eax
  44:   dd 5c 24 1c             fstpl  0x1c(%esp)
  48:   dd 5c 24 14             fstpl  0x14(%esp)
  4c:   dd 5c 24 0c             fstpl  0xc(%esp)
  50:   dd 5c 24 04             fstpl  0x4(%esp)
  54:   89 04 24                mov    %eax,(%esp)
  57:   e8 fc ff ff ff          call   58 <main+0x58>
  5c:   83 ec 04                sub    $0x4,%esp
  5f:   dd 45 b8                fldl   -0x48(%ebp)
  62:   dd 5d c8                fstpl  -0x38(%ebp)
  65:   dd 45 c0                fldl   -0x40(%ebp)
  68:   dd 5d d0                fstpl  -0x30(%ebp)

Yang merupakan kode mesin 39 byte juga, tetapi langkah fungsi 57 merujuk ke, melakukan bagian pekerjaan yang tepat dan melakukan operasi yang diinginkan. Jadi kami memiliki kode mesin 27 byte untuk menjalankan operasi multi. Fungsi di belakang adalah muldc3 yang disediakan oleh libgcc_s.sodan memiliki jejak 1375 byte dalam kode mesin. Ini memperlambat kode secara dramatis dan memberikan output yang menarik saat menggunakan profiler.

Ketika kami menerapkan contoh BLAS di atas untuk zaxpydan melakukan pengujian yang sama, kompiler Fortran harus memberikan hasil yang lebih baik daripada kompiler C.

(Saya menggunakan GCC 4.4.3 untuk percobaan ini, tetapi saya perhatikan perilaku ini yang dirilis oleh GCC lainnya.)

Jadi menurut saya kita tidak hanya berpikir tentang paralelisasi dan vektorisasi ketika kita memikirkan mana yang merupakan kompiler yang lebih baik, kita juga harus melihat bagaimana hal-hal dasar diterjemahkan ke kode assembler. Jika terjemahan ini memberikan kode yang buruk optimasi hanya dapat menggunakan hal-hal ini sebagai input.

MK alias Grisu
sumber
1
Saya baru saja membuat contoh di sepanjang baris kode Anda, complex.cdan menambahkannya ke kode online. Saya harus menambahkan semua input / output untuk memastikan tidak ada yang dioptimalkan. Saya hanya menerima telepon __muldc3jika saya tidak menggunakannya -ffast-math. Dengan -O2 -ffast-mathsaya mendapatkan 9 baris assembler inline. Bisakah Anda mengkonfirmasi ini?
Pedro
Saya telah menemukan penyebab yang lebih spesifik untuk perbedaan assembler yang dihasilkan dan telah menambahkan ini ke pertanyaan saya di atas.
Pedro
Menggunakan -O2 memimpin compiler untuk menghitung setiap apa yang mungkin terjadi pada saat runtime, itu sebabnya konstruksi seperti itu kadang-kadang hilang. Opsi -Fast-matematika tidak boleh digunakan dalam komputasi ilmiah ketika Anda ingin mengandalkan output.
MK alias Grisu
1
Nah, dengan argumen itu (tidak -ffast-math) Anda seharusnya tidak menggunakan Fortran untuk perhitungan Anda yang bernilai kompleks. Seperti yang saya jelaskan dalam pembaruan pertanyaan saya, -ffast-mathatau, lebih umum -fcx-limited-range, memaksa gcc untuk menggunakan non-IEEE yang sama, perhitungan rentang terbatas seperti standar di Fortran. Jadi jika Anda ingin berbagai nilai kompleks dan mengoreksi Inf dan NaN, Anda tidak boleh menggunakan Fortran ...
Pedro
2
@Pedro: Jika Anda ingin GCC berperilaku seperti GFortran wrt. perkalian dan pembagian yang kompleks, Anda harus menggunakan aturan -fcx-fortran.
janneb
4

Orang-orang,

Saya menemukan diskusi ini sangat menarik, tetapi saya terkejut melihat bahwa memesan kembali loop dalam contoh Matmul mengubah gambar. Saya tidak memiliki kompiler intel yang tersedia di mesin saya saat ini, jadi saya menggunakan gfortran, tetapi menulis ulang loop di mm_test.f90 untuk

call cpu_time(start)  
do r=1,runs  
  mat_c=0.0d0  
     do j=1,n  
        do k=1,n  
  do i=1,n  
           mat_c(i,j)=mat_c(i,j)+mat_a(i,k)*mat_b(k,j)  
        end do  
     end do  
  end do  
end do  
call cpu_time(finish)  

mengubah seluruh hasil untuk mesin saya.

Hasil waktu versi sebelumnya adalah:

#time ./mm_test_f 10000 100
 matmul    time   6.3620000000000001     
 triple do time   21.420999999999999     

sedangkan dengan triple loop diatur ulang seperti di atas yeilded:

#time ./mm_test_f 10000 100
 matmul    time   6.3929999999999998     
 triple do time   3.9190000000000005    

Ini adalah gcc / gfortran 4.7.2 20121109 pada Intel (R) Core (TM) i7-2600K CPU @ 3.40GHz

Bendera kompiler yang digunakan adalah bendera dari Makefile yang saya dapatkan di sini ...

Schatzi
sumber
3
Itu tidak mengherankan, karena penyimpanan matriks dalam memori mendukung satu urutan, yaitu, jika baris disimpan secara berdampingan, lebih baik untuk mengulangi baris paling dalam, karena Anda dapat memuat setiap baris sekali ke memori lokal cepat dibandingkan dengan pemuatan berulang kali (sepotong ) untuk mengakses elemen tunggal. Lihat stackoverflow.com/questions/7395556 .
Christian Clason
Saya kira saya terkejut bahwa "intrinsik matmul" tidak akan dikodekan untuk melakukan hal-hal seperti ini. Ini jauh lebih cepat dengan triple lakukan dipesan dengan cara kedua. Tampaknya berada di set kompiler ini, karena versi gfortran sebelumnya saya bisa lebih "flat" dalam pengaturan waktu - tidak masalah ke arah mana Anda melakukan mult - butuh waktu yang hampir bersamaan.
Schatzi
-2

Bukan bahasa yang membuat kode berjalan lebih cepat, meskipun mereka membantu. Ini adalah kompiler, CPU dan sistem operasi yang membuat kode berjalan lebih cepat. Membandingkan bahasa hanya keliru, tidak berguna dan tidak berarti. Sama sekali tidak masuk akal karena Anda membandingkan dua variabel: bahasa dan kompiler. Jika satu kode berjalan lebih cepat, Anda tidak tahu berapa banyak bahasa atau berapa kompilernya. Saya tidak mengerti mengapa komunitas ilmu komputer tidak mengerti ini :-(

Wadud Miah
sumber