Bu soru, son zamanlarda " C ++ vs Fortran for HPC " yanıtlarına gelen iki tartışmanın bir uzantısı . Ve bu bir sorudan biraz daha zor.
Fortran lehine en sık duyulan tartışmalardan biri, derleyicilerin daha iyi olduğu yönünde. Çoğu C / Fortran derleyicisi aynı arka ucu paylaştığından, her iki dilde de semantik olarak eşdeğer programlar için oluşturulan kod aynı olmalıdır. Bununla birlikte, C / Fortran'ın derleyicinin optimizasyonu için daha fazla / daha az daha kolay olduğu iddia edilebilir.
Böylece basit bir test yapmaya karar verdim: daxpy.f ve daxpy.c'nin bir kopyasını aldım ve onları gfortran / gcc ile derledim.
Şimdi daxpy.c daxpy.f'in bir f2c çevirisidir (otomatik olarak oluşturulan kod, çirkin gibi çirkin), bu yüzden bu kodu aldım ve biraz temizledik (daxpy_c ile karşılaştığım), bu temelde en içteki döngüyü yeniden yazmak anlamına geliyordu.
for ( i = 0 ; i < n ; i++ )
dy[i] += da * dx[i];
Sonunda, gcc'nin vektör sözdizimini kullanarak yeniden yazdım (daxpy_cvec yazın):
#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];
Unutmayın, uzunluk 2 vektörleri (hepsi SSE2'nin izin verdiği) kullandığımı ve aynı anda iki vektörü işlediğime dikkat edin. Bunun nedeni birçok mimaride, vektör elementlerimizden daha fazla çarpma birimine sahip olabileceğimizdir.
Tüm kodlar "-O3 -Wall -msse2 -march = native -ffast-math -fomit-frame-pointer-malign-double -fstrict-aliasing" bayraklarıyla gfortran / gcc versiyon 4.5 kullanılarak derlendi. Dizüstü bilgisayarımda (Intel Core i5 CPU, M560, 2.67GHz) Aşağıdaki çıktıyı aldım:
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.
Bu yüzden, orijinal Fortran kodu 8.1 saniyeden biraz daha fazla sürüyor, otomatik çevirisi 10.5 saniye sürüyor, saf C uygulaması 7.9'da ve açıkça vektörelleştirilmiş kod 5.6'da az da olsa yapıyor.
Bu Fortran'ın saf C uygulamasından biraz daha yavaş ve vectorized C uygulamasından% 50 daha yavaş olması.
İşte soru şu: Ben yerel bir C programcısıyım ve bu yüzden bu kod üzerinde iyi bir iş yaptığımdan eminim, ancak Fortran koduna en son 1993 yılında dokunulmuştu ve bu nedenle biraz eski olabilir. Fortran'da diğerlerinin yapabileceği kadar rahat bir kodlama hissetmediğim için, herkes daha iyi bir iş yapabilir mi, yani iki C versiyonundan birine göre daha rekabetçi mi?
Ayrıca, herhangi biri bu testi icc / ifort ile deneyebilir mi? Vektör sözdizimi muhtemelen işe yaramaz, ancak saf C sürümünün orada nasıl davrandığını görmek isterim. Aynı xlc / xlf etrafında yatan herkes için de geçerlidir.
Kaynakları ve bir Makefile'i buraya yükledim . Doğru zamanlamaları almak için, test.c'deki CPU_TPS'yi CPU'nuzdaki Hz sayısına ayarlayın. Herhangi bir sürümünde herhangi bir gelişme bulursanız, lütfen onları buraya gönderin!
Güncelleme:
Stali'nin test kodunu çevrimiçi olarak dosyalara ekledim ve C sürümüyle tamamladım. Programları, önceki testle tutarlı olacak şekilde 10'000 uzunluğunda vektörler üzerinde 1'000'000 döngü yapacak şekilde değiştirdim (ve makinem stali'nin orijinalinde olduğu gibi 1'000'000'000 uzunluğunda vektörler atayamıyordu. kodu). Rakamlar şimdi biraz daha küçük olduğundan, -par-threshold:50
derleyiciyi paralelleştirme olasılığını artırmak için bu seçeneği kullandım . Kullanılan icc / ifort versiyonu 12.1.2 20111128 ve sonuçları aşağıdaki gibidir.
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
Özet olarak, sonuçlar, tüm pratik amaçlar için, hem C hem de Fortran versiyonları için aynıdır ve her iki kod da otomatik olarak paraleldir. Önceki teste kıyasla hızlı sürelerin, tek kesinlikli kayar nokta aritmetiğinden kaynaklandığını unutmayın!
Güncelleme:
Burada ispat külfetinin nereye gittiğini pek sevmesem de, stali'nin matris çarpma örneğini C olarak yeniden kodladım ve web'deki dosyalara ekledim . İşte bir ve iki CPU için üçlü döngü sonuçları:
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
Not cpu_time
Fortranda CPU zamanı değil duvar saatlik zaman hasta veri formu, o yüzden içeri aramaları sarılı time
2 işlemciler için bunları karşılaştırmak. C sürümünün iki çekirdek üzerinde biraz daha iyi olması dışında, sonuçlar arasında gerçek bir fark yoktur.
Şimdi matmul
komut için, tabii ki sadece Fortran'da bu içsel C'de bulunmaz.
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
Vay. Bu kesinlikle korkunç. Birisi ya yanlış yaptığımı öğrenebilir ya da neden bu içselliğin hala bir şekilde iyi bir şey olduğunu açıklayabilir mi?
Ben eklemek vermedi dgemm
onlar Intel MKL aynı işleve kütüphane çağrıları gibi kıyaslama çağrıları.
Gelecekteki testler için, C’de Fortran’dan daha yavaş olduğu bilinen bir örnek önerilebilir mi?
Güncelleme
matmul
Stali'nin içsel olanın, daha küçük matrislerdeki açık matris ürününden daha hızlı bir "mıknatıs sırası" olduğunu iddia etmek için , her iki yöntemi de kullanarak her biri 10'000 kez olan 100x100 boyutundaki matrisleri çarpmak için kendi kodunu değiştirdim. Bir ve iki CPU üzerindeki sonuçlar aşağıdaki gibidir:
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
Güncelleme
Grisu, optimizasyonlar olmadan, gcc'nin karmaşık sayılardaki işlemleri kütüphane işlev çağrılarına dönüştürdüğü, gfortran'ın birkaç talimat içerdiği anlamına geldiğine işaret etmekte haklıdır.
Eğer seçenek -fcx-limited-range
ayarlanmışsa C derleyici aynı kompakt kodu üretecektir , yani derleyiciye ara değerlerdeki potansiyel aşırı / düşük akışları görmezden gelmesi talimatı verilmiştir. Bu seçenek bir şekilde varsayılan olarak gfortran'da ayarlanmıştır ve yanlış sonuçlara yol açabilir. Gfortran'a zorlamak -fno-cx-limited-range
hiçbir şeyi değiştirmedi.
Dolayısıyla bu, sayısal hesaplamalar için gfortran kullanımına karşı bir argümandır : Karmaşık değerler üzerinde yapılan işlemler, doğru sonuçlar kayan nokta aralığında olsa bile, aşırı / düşük olabilir. Bu aslında bir Fortran standardıdır. Gcc'de veya genel olarak C99'da varsayılan, aksi belirtilmediği sürece işleri kesin yapmaktır (IEEE-754 uyumlu olanları okuyun).
Hatırlatma: Lütfen asıl sorunun Fortran derleyicilerinin C derleyicilerden daha iyi kod üretip üretmediği olduğunu unutmayın. Burası, bir dilin genel yararına ilişkin tartışmaların yeri değil. Gerçekten ilgilendiğim şey, herhangi birinin, yalnızca SIMD optimizasyonu için derleyiciye güvenmek zorunda olma problemlerini örneklendirdiği gibi açık bir vektörlemeyi kullanarak C'deki kadar verimli bir dakspy üretecek bir gutkanın bir eşekarısı oluşturmasının bir yolunu bulabilmesidir. Bir Fortran derleyicisinin C karşılığını çıkardığı durumda.