Modern donanımda kayan nokta ve tam sayı hesaplamaları


100

C ++ 'da performans açısından kritik bazı işler yapıyorum ve şu anda "daha hızlı" olduğu için doğal olarak kayan nokta olan sorunlar için tamsayı hesaplamaları kullanıyoruz. Bu, pek çok can sıkıcı soruna neden olur ve çok sayıda can sıkıcı kod ekler.

Şimdi, kayan nokta hesaplamalarının yaklaşık 386 gün boyunca ne kadar yavaş olduğunu okuduğumu hatırlıyorum, burada (IIRC) isteğe bağlı bir yardımcı işlemci olduğuna inanıyorum. Ama kesinlikle bugünlerde katlanarak daha karmaşık ve güçlü CPU'larla, kayan nokta veya tamsayı hesaplaması yapıyorsanız "hızda" hiçbir fark yaratmıyor mu? Özellikle de gerçek hesaplama süresi, bir ardışık düzen durmasına neden olmak veya ana bellekten bir şey getirmek gibi bir şeye kıyasla çok küçük olduğu için?

Doğru cevabın hedef donanımı karşılaştırmak olduğunu biliyorum, bunu test etmenin iyi bir yolu nedir? İki küçük C ++ programı yazdım ve çalıştırma sürelerini Linux'ta "zaman" ile karşılaştırdım, ancak gerçek çalışma süresi çok değişken (sanal bir sunucuda çalıştırmama yardımcı olmuyor). Bütün günümü yüzlerce kıyaslama yapmak, grafikler yapmak vb. İle harcamak yerine göreceli hızın makul bir testini yapmak için yapabileceğim bir şey var mı? Herhangi bir fikir veya düşünceniz var mı? Tamamen yanılıyor muyum?

Kullandığım programlar şu şekilde, hiçbir şekilde aynı değiller:

#include <iostream>
#include <cmath>
#include <cstdlib>
#include <time.h>

int main( int argc, char** argv )
{
    int accum = 0;

    srand( time( NULL ) );

    for( unsigned int i = 0; i < 100000000; ++i )
    {
        accum += rand( ) % 365;
    }
    std::cout << accum << std::endl;

    return 0;
}

Program 2:

#include <iostream>
#include <cmath>
#include <cstdlib>
#include <time.h>

int main( int argc, char** argv )
{

    float accum = 0;
    srand( time( NULL ) );

    for( unsigned int i = 0; i < 100000000; ++i )
    {
        accum += (float)( rand( ) % 365 );
    }
    std::cout << accum << std::endl;

    return 0;
}

Şimdiden teşekkürler!

Düzenleme: İlgilendiğim platform, masaüstü Linux ve Windows makinelerinde çalışan normal x86 veya x86-64.

Düzenleme 2 (aşağıdaki yorumdan yapıştırılmıştır): Şu anda kapsamlı bir kod tabanımız var. Gerçekten, "tamsayı hesaplaması daha hızlı olduğu için şamandırayı kullanmamamız gerektiği" genellemesine karşı çıktım - ve bu genelleştirilmiş varsayımı çürütmek için (eğer bu doğruysa) bir yol arıyorum. Tüm işi yapıp daha sonra profilini çıkarırsak, bizim için kesin sonucu tahmin etmenin imkansız olacağının farkındayım.

Her neyse, mükemmel yanıtlarınız ve yardımlarınız için teşekkürler. Başka bir şey eklemekten çekinmeyin :).


8
Testiniz olarak sahip olduğunuz şey artık önemsiz. Montajda da muhtemelen çok az fark vardır ( örneğin addlile değiştirilir fadd). Gerçekten iyi bir ölçüm elde etmenin tek yolu, gerçek programınızın temel bir parçasını almak ve bunun farklı sürümlerinin profilini çıkarmaktır. Ne yazık ki, tonlarca çaba sarf etmeden bu oldukça zor olabilir. Belki de bize hedef donanımı ve derleyicinizi söylerseniz insanlara en azından size önceden var olan deneyimi vermede yardımcı olur. Tamsayı kullanımınız hakkında, fixed_pointböyle bir işi büyük ölçüde kolaylaştıracak bir tür şablon sınıfı oluşturabileceğinizden şüpheleniyorum .
GManNickG

1
Hala kayan nokta donanımına sahip olmayan pek çok mimari var - önemsediğiniz sistemleri açıklayan bazı etiketler daha iyi yanıtlar almanıza yardımcı olacaktır.
Carl Norum

3
HTC Hero'mdaki (android) donanımın FPU'su olmadığına inanıyorum, ancak Google NexusOne'daki (android) donanımda var. hedefin nedir masaüstü / sunucu bilgisayarları? netbook'lar (olası arm + linux)? telefonlar?
SteelBytes

5
X86'da hızlı FP istiyorsanız, optimizasyon ve SSE kodu oluşturma ile derlemeyi deneyin. SSE (sürüm ne olursa olsun) tek bir döngüde en azından float toplama, çıkarma ve çarpma yapabilir. Bölme, mod ve daha yüksek işlevler her zaman yavaş olacaktır . Ayrıca float, hız artışı sağladığını, ancak genellikle doubleolmadığını unutmayın.
Mike D.

1
Sabit noktalı tamsayı, sonuçların taşmasını önlemek için çoklu tamsayı işlemleri kullanarak FP'yi yaklaştırır. Bu, modern masaüstü CPU'larında bulunan son derece yetenekli FPU'ları kullanmaktan neredeyse her zaman daha yavaştır. Örneğin, sabit noktalı mp3 kod çözücü MAD, libmpg123'ten daha yavaştır ve sabit nokta kod çözücü için iyi kalitede olmasına rağmen, libmpg123'ün hala daha az yuvarlama hatası vardır. wezm.net/technical/2008/04/mp3-decoder-libraries-bir PPC G5 üzerinde kıyaslamalar için karşılaştırılmıştır.
Peter Cordes

Yanıtlar:


35

Ne yazık ki, sana sadece "duruma göre değişir" cevabı verebilirim ...

Deneyimlerime göre, performans için birçok değişken var ... özellikle tam sayı ve kayan nokta matematiği arasında. İşlemciden işlemciye büyük ölçüde değişir (x86 gibi aynı aile içinde bile) çünkü farklı işlemcilerin farklı "boru hattı" uzunlukları vardır. Ayrıca, bazı işlemler genellikle çok basittir (toplama gibi) ve işlemcide hızlandırılmış bir rotaya sahiptir ve diğerleri (bölme gibi) çok çok daha uzun sürer.

Diğer büyük değişken, verilerin bulunduğu yerdir. Eklemek için yalnızca birkaç değeriniz varsa, tüm veriler önbellekte saklanabilir ve burada hızla CPU'ya gönderilebilir. Zaten önbellekte veri bulunan çok, çok yavaş bir kayan nokta işlemi, sistem belleğinden bir tamsayının kopyalanması gereken bir tamsayı işleminden çok daha hızlı olacaktır.

Bu soruyu sorduğunuzu varsayıyorum çünkü performans açısından kritik bir uygulama üzerinde çalışıyorsunuz. X86 mimarisi için geliştirme yapıyorsanız ve ekstra performansa ihtiyacınız varsa, SSE uzantılarını kullanmayı düşünebilirsiniz. Bu, tek duyarlıklı kayan nokta aritmetiğini büyük ölçüde hızlandırabilir, çünkü aynı işlem aynı anda birden fazla veri üzerinde gerçekleştirilebilir, ayrıca SSE işlemleri için ayrı * bir kayıt bankası vardır. (İkinci örneğinizde "double" yerine "float" kullandığınızı fark ettim, bu da tek duyarlıklı matematik kullandığınızı düşündürüyor).

* Not: Eski MMX komutlarını kullanmak aslında programları yavaşlatır, çünkü bu eski komutlar aslında FPU ile aynı kayıtları kullanır ve hem FPU hem de MMX'i aynı anda kullanmayı imkansız hale getirir.


8
Ve bazı işlemcilerde FP matematiği tamsayı matematikten daha hızlı olabilir. Alpha işlemcinin bir FP bölme talimatı vardı, ancak bir tamsayı yoktu, bu yüzden yazılımda tamsayı bölme yapılması gerekiyordu.
Gabe

SSEx ayrıca çift duyarlıklı aritmetiği hızlandıracak mı? Üzgünüm,
SSE'ye

1
@ JohannesSchaub-litb: SSE2 (x86-64 için temel) paketlenmiş double-precision FP. doubleKayıt başına yalnızca iki 64-bit s ile, potansiyel hızlanma, floatiyi vektörleştiren koddan daha küçüktür . X86-64'te skaler floatve doubleXMM kayıtlarını kullanın; yalnızca eski x87 için kullanılır long double. (Yani @ Dan: hayır, MMX kayıtları normal FPU kayıtları ile çakışmaz çünkü x86-64 üzerindeki normal FPU SSE birimidir. MMX anlamsız olacaktır çünkü tamsayı SIMD yapabiliyorsanız xmm0..15, 8 yerine 16 bayt istersiniz . -bayt mm0..7, ve modern CPU'lar SSE veriminden daha kötü MMX'e sahiptir.)
Peter Cordes

1
Ancak MMX ve SSE * / AVX2 tamsayı talimatları aynı yürütme birimleri için rekabet eder, bu nedenle ikisini birden kullanmak neredeyse hiçbir zaman yararlı değildir. Daha fazla iş yapmak için daha geniş XMM / YMM sürümlerini kullanın. SIMD tamsayısını ve FP'yi aynı anda kullanmak aynı yazmaçlar için rekabet eder, ancak x86-64'te bunlardan 16 tane vardır. Ancak toplam verim sınırları, tamsayı ve FP yürütme birimlerini paralel olarak kullanarak iki kat daha fazla iş yapamayacağınız anlamına gelir.
Peter Cordes

49

Örneğin (daha az sayı daha hızlıdır),

64-bit Intel Xeon X5550 @ 2.67GHz, gcc 4.1.2 -O3

short add/sub: 1.005460 [0]
short mul/div: 3.926543 [0]
long add/sub: 0.000000 [0]
long mul/div: 7.378581 [0]
long long add/sub: 0.000000 [0]
long long mul/div: 7.378593 [0]
float add/sub: 0.993583 [0]
float mul/div: 1.821565 [0]
double add/sub: 0.993884 [0]
double mul/div: 1.988664 [0]

32-bit Çift Çekirdekli AMD Opteron (tm) İşlemci 265 @ 1.81GHz, gcc 3.4.6 -O3

short add/sub: 0.553863 [0]
short mul/div: 12.509163 [0]
long add/sub: 0.556912 [0]
long mul/div: 12.748019 [0]
long long add/sub: 5.298999 [0]
long long mul/div: 20.461186 [0]
float add/sub: 2.688253 [0]
float mul/div: 4.683886 [0]
double add/sub: 2.700834 [0]
double mul/div: 4.646755 [0]

As Dan işaret (ki boruhatlı tasarımlarda kendi içinde yanıltıcı olabilir) hatta sizin kez normale saat frekansı için, sonuçlar çılgınca işlemci mimarisine göre değişir (bireysel ALU / FPU performansı , hem de fiili ALU / kayan nokta birimleri sayısının mevcut per paralel olarak kaç bağımsız işlemin yürütülebileceğini etkileyen süper skaler tasarımlarda çekirdek - sonraki faktör aşağıdaki kod tarafından uygulanmaz, çünkü aşağıdaki işlemler sıralı olarak bağımlıdır.)

Zavallı adamın FPU / ALU operasyon kıyaslaması:

#include <stdio.h>
#ifdef _WIN32
#include <sys/timeb.h>
#else
#include <sys/time.h>
#endif
#include <time.h>
#include <cstdlib>

double
mygettime(void) {
# ifdef _WIN32
  struct _timeb tb;
  _ftime(&tb);
  return (double)tb.time + (0.001 * (double)tb.millitm);
# else
  struct timeval tv;
  if(gettimeofday(&tv, 0) < 0) {
    perror("oops");
  }
  return (double)tv.tv_sec + (0.000001 * (double)tv.tv_usec);
# endif
}

template< typename Type >
void my_test(const char* name) {
  Type v  = 0;
  // Do not use constants or repeating values
  //  to avoid loop unroll optimizations.
  // All values >0 to avoid division by 0
  // Perform ten ops/iteration to reduce
  //  impact of ++i below on measurements
  Type v0 = (Type)(rand() % 256)/16 + 1;
  Type v1 = (Type)(rand() % 256)/16 + 1;
  Type v2 = (Type)(rand() % 256)/16 + 1;
  Type v3 = (Type)(rand() % 256)/16 + 1;
  Type v4 = (Type)(rand() % 256)/16 + 1;
  Type v5 = (Type)(rand() % 256)/16 + 1;
  Type v6 = (Type)(rand() % 256)/16 + 1;
  Type v7 = (Type)(rand() % 256)/16 + 1;
  Type v8 = (Type)(rand() % 256)/16 + 1;
  Type v9 = (Type)(rand() % 256)/16 + 1;

  double t1 = mygettime();
  for (size_t i = 0; i < 100000000; ++i) {
    v += v0;
    v -= v1;
    v += v2;
    v -= v3;
    v += v4;
    v -= v5;
    v += v6;
    v -= v7;
    v += v8;
    v -= v9;
  }
  // Pretend we make use of v so compiler doesn't optimize out
  //  the loop completely
  printf("%s add/sub: %f [%d]\n", name, mygettime() - t1, (int)v&1);
  t1 = mygettime();
  for (size_t i = 0; i < 100000000; ++i) {
    v /= v0;
    v *= v1;
    v /= v2;
    v *= v3;
    v /= v4;
    v *= v5;
    v /= v6;
    v *= v7;
    v /= v8;
    v *= v9;
  }
  // Pretend we make use of v so compiler doesn't optimize out
  //  the loop completely
  printf("%s mul/div: %f [%d]\n", name, mygettime() - t1, (int)v&1);
}

int main() {
  my_test< short >("short");
  my_test< long >("long");
  my_test< long long >("long long");
  my_test< float >("float");
  my_test< double >("double");

  return 0;
}

8
neden mult ve div'i karıştırdınız? Mult, div'den çok daha hızlıysa (veya beklendiği gibi) ilginç olmamalı mı?
Kyss Tao

13
Çarpma, hem tam sayı hem de kayan nokta durumlarında bölmeden çok daha hızlıdır. Bölme performansı aynı zamanda sayıların boyutuna da bağlıdır. Genelde bölmenin ~ 15 kat daha yavaş olduğunu varsayıyorum.
Sogartar

4
pastebin.com/Kx8WGUfg Karşılaştırmanızı yaptım ve her işlemi kendi döngüsüne ayırdım ve volatileemin olmak için ekledim . Win64 günü, FPU kullanılmamış olduğunu ve kullanan derler böylece MSVC, bunun için kod oluşturmaz mulssve divssdaha hızlı Win32 FPU daha 25x orada XMM talimatları. Test makinesi Core i5 M 520 @ 2.40GHz'dir
James Dunne

4
@JamesDunne dikkatli olun, çünkü fp operasyonları vçok hızlı bir şekilde 0 veya +/- inf değerine ulaşacaktır, bu da (teorik olarak) belirli fpu uygulamaları tarafından özel bir durum / hızlı hız olarak değerlendirilebilir veya görülmeyebilir.
vladr

3
Her işlem aynı akümülatör ( v) ile yapıldığından, bu "kıyaslama" sıra dışı yürütme için veri paralelliği içermez . En son Intel tasarımlarında, bölme hiçbir şekilde ardışık düzende değildir ( divss/ divps10-14 döngü gecikmesine ve aynı karşılıklı iş hacmine sahiptir). mulssancak 5 döngü gecikmesidir, ancak her döngüde bir tane yayınlayabilir. (Veya Haswell'de döngü başına iki, çünkü hem bağlantı noktası 0 hem de bağlantı noktası 1 FMA için bir çarpana sahiptir).
Peter Cordes

23

Sabit noktalı ve kayan noktalı matematik arasında gerçek dünya hızında önemli bir fark olması muhtemeldir, ancak ALU ile FPU'nun teorik olarak en iyi verimi tamamen önemsizdir. Bunun yerine, mimarinizdeki hesaplamanız tarafından kullanılmayan tamsayı ve kayan noktalı yazmaçların (gerçek yazmaçlar, kayıt adları değil) sayısı (örneğin döngü kontrolü için), her türden bir önbellek satırına uyan elemanların sayısı , tamsayı ve kayan nokta matematiği için farklı anlamlar dikkate alındığında optimizasyonlar mümkündür - bu etkiler baskın olacaktır. Algoritmanızın veri bağımlılıkları burada önemli bir rol oynar, böylece genel bir karşılaştırma probleminizdeki performans açığını tahmin edemez.

Örneğin, tamsayı toplama değişkendir, bu nedenle derleyici bir kıyaslama için kullandığınız gibi bir döngü görürse (rastgele verilerin önceden hazırlandığını ve böylece sonuçları gizlemeyeceğini varsayarak), döngüyü açabilir ve kısmi toplamları hesaplayabilir bağımlılık yok, sonra döngü sona erdiğinde onları ekleyin. Ancak kayan noktayla, derleyicinin işlemleri istediğiniz sırayla yapması gerekir (burada sıra noktaları vardır, bu nedenle derleyici aynı sonucu garanti etmelidir, bu da yeniden sıralamayı engeller), bu nedenle her eklemenin güçlü bir bağımlılığı vardır. bir öncekinin sonucu.

Önbelleğe bir seferde daha fazla tamsayı işlenen sığdırmanız da olasıdır. Dolayısıyla sabit noktalı sürüm, FPU'nun teorik olarak daha yüksek verimliliğe sahip olduğu bir makinede bile kayan sürümden bir derece daha iyi performans gösterebilir.


4
+1, sıfırlanmış sabit tamsayı işlemleri nedeniyle nasıl 0 zamanlı döngüler oluşturabileceğine işaret etmek için. Ayrıca, sonuç gerçekten kullanılmazsa derleyici döngüyü (tamsayı veya FP) tamamen atabilir.
vladr

Bunun sonucu şudur: döngü değişkeni bağımsız değişken olan bir işlevi çağırmalıdır. Hiçbir derleyicinin işlevin hiçbir şey yapmadığını ve çağrının göz ardı edilebileceğini göremeyeceğini düşündüğüm için. Bir çağrı ek yükü olduğundan, yalnızca zaman == (float süresi - tamsayı zamanı) arasındaki farklar önemli olacaktır.
GameAlchemist

@GameAlchemist: Birçok derleyici, satır içi yapmanın bir yan etkisi olarak boş işlev çağrılarını ortadan kaldırır. Bunu önlemek için çaba sarf etmelisiniz.
Ben Voigt

OP, FP'nin daha doğal bir uyum sağlayacağı şeyler için tamsayı kullanmaktan bahsediyor gibiydi, bu nedenle FP koduyla aynı sonucu elde etmek için daha fazla tamsayı kodu gerekirdi. Bu durumda, sadece FP kullanın. Örneğin, FPU'lu bir donanımda (örneğin bir masaüstü CPU), sabit noktalı tamsayı MP3 kod çözücüleri kayan noktalı kod çözücülerden daha yavaştır (ve biraz daha fazla yuvarlama hatasıdır). Sabit noktalı codec uygulamaları, esas olarak FP donanımı olmayan, yalnızca yavaş benzetilmiş FP'ye sahip, soyulmuş ARM CPU'larda çalışmak için mevcuttur.
Peter Cordes

x86-64 üzerinde AVX-512 sayıl kayan nokta matematik sadece 16 GP kayıtlarını ancak 32 ZMM kayıtlarını orada böylece ile: İlk nokta için bir örnek olabilir daha hızlı olması
phuclv

18

Toplama çok daha hızlıdır rand, bu nedenle programınız (özellikle) işe yaramaz.

Performans etkin noktalarını belirlemeniz ve programınızı aşamalı olarak değiştirmeniz gerekir. Görünüşe göre geliştirme ortamınızda önce çözülmesi gereken sorunlar var. Küçük bir problem seti için programınızı bilgisayarınızda çalıştırmak imkansız mı?

Genel olarak, tamsayı aritmetik ile FP işlerine girişmek, yavaşlamanın reçetesidir.


Evet, kayan nokta sürümünde rand tamsayısından kayan noktaya dönüşüm gibi. Bunu test etmenin daha iyi bir yolu hakkında herhangi bir fikriniz var mı?
maxpenguin

1
Hızı profillemeye çalışıyorsanız, POSIX'lere timespec_tveya benzer bir şeye bakın. Döngünün başlangıcındaki ve sonundaki zamanı kaydedin ve farkı alın. Ardından randveri oluşturmayı döngünün dışına taşıyın . Algoritmanızın tüm verilerini dizilerden aldığından ve tüm verilerini dizilere koyduğundan emin olun. Bu, gerçek algoritmanızı kendisi alır ve kurulum, malloc, sonuç baskısı, görev değiştirme hariç her şeyi alır ve profil oluşturma döngünüzden kesintiye uğrar.
Mike D.

3
@maxpenguin: soru, test ettiğiniz şeydir. Artem grafik yaptığınızı varsaydı, Carl yerleşik bir platformda olup olmadığınızı düşündü, FP değil, sanırım bir sunucu için bilim kodluyorsunuz. Kıyaslamaları genelleyemez veya "yazamazsınız". Karşılaştırmalar, programınızın yaptığı gerçek çalışmalardan örneklenir. Size söyleyebileceğim bir şey, her ne olursa olsun, programınızdaki performans-kritik öğeye dokunursanız, "esasen aynı hızda" kalmayacağıdır.
Potatoswatter

iyi nokta ve iyi cevap. Şu anda kapsamlı bir kod tabanımız var. Gerçekten, "tamsayı hesaplaması daha hızlı olduğu için şamandırayı kullanmamamız gerektiği" genellemesine karşı çıktım - ve bu genelleştirilmiş varsayımı çürütmek için (eğer bu doğruysa) bir yol arıyorum. Tüm işi yapıp daha sonra profilini çıkarmadan bizim için kesin sonucu tahmin etmenin imkansız olacağının farkındayım. Neyse, yardımlarınız için teşekkürler.
maxpenguin

18

TIL Bu değişir (çok fazla). İşte gnu derleyicisini kullanan bazı sonuçlar (btw ayrıca makinelerde derleyerek kontrol ettim, xenial'den gnu g ++ 5.4, linaro'dan 4.6.3'e göre çok daha hızlıdır)

Intel i7 4700MQ xenial

short add: 0.822491
short sub: 0.832757
short mul: 1.007533
short div: 3.459642
long add: 0.824088
long sub: 0.867495
long mul: 1.017164
long div: 5.662498
long long add: 0.873705
long long sub: 0.873177
long long mul: 1.019648
long long div: 5.657374
float add: 1.137084
float sub: 1.140690
float mul: 1.410767
float div: 2.093982
double add: 1.139156
double sub: 1.146221
double mul: 1.405541
double div: 2.093173

Intel i3 2370M benzer sonuçlara sahip

short add: 1.369983
short sub: 1.235122
short mul: 1.345993
short div: 4.198790
long add: 1.224552
long sub: 1.223314
long mul: 1.346309
long div: 7.275912
long long add: 1.235526
long long sub: 1.223865
long long mul: 1.346409
long long div: 7.271491
float add: 1.507352
float sub: 1.506573
float mul: 2.006751
float div: 2.762262
double add: 1.507561
double sub: 1.506817
double mul: 1.843164
double div: 2.877484

Intel (R) Celeron (R) 2955U (xenial çalıştıran Acer C720 Chromebook)

short add: 1.999639
short sub: 1.919501
short mul: 2.292759
short div: 7.801453
long add: 1.987842
long sub: 1.933746
long mul: 2.292715
long div: 12.797286
long long add: 1.920429
long long sub: 1.987339
long long mul: 2.292952
long long div: 12.795385
float add: 2.580141
float sub: 2.579344
float mul: 3.152459
float div: 4.716983
double add: 2.579279
double sub: 2.579290
double mul: 3.152649
double div: 4.691226

DigitalOcean 1 GB Damlacık Intel (R) Xeon (R) CPU E5-2630L v2 (güvenilir çalışıyor)

short add: 1.094323
short sub: 1.095886
short mul: 1.356369
short div: 4.256722
long add: 1.111328
long sub: 1.079420
long mul: 1.356105
long div: 7.422517
long long add: 1.057854
long long sub: 1.099414
long long mul: 1.368913
long long div: 7.424180
float add: 1.516550
float sub: 1.544005
float mul: 1.879592
float div: 2.798318
double add: 1.534624
double sub: 1.533405
double mul: 1.866442
double div: 2.777649

AMD Opteron (tm) İşlemci 4122 (hassas)

short add: 3.396932
short sub: 3.530665
short mul: 3.524118
short div: 15.226630
long add: 3.522978
long sub: 3.439746
long mul: 5.051004
long div: 15.125845
long long add: 4.008773
long long sub: 4.138124
long long mul: 5.090263
long long div: 14.769520
float add: 6.357209
float sub: 6.393084
float mul: 6.303037
float div: 17.541792
double add: 6.415921
double sub: 6.342832
double mul: 6.321899
double div: 15.362536

Bu kodu kullanan http://pastebin.com/Kx8WGUfg olarakbenchmark-pc.c

g++ -fpermissive -O3 -o benchmark-pc benchmark-pc.c

Birden çok geçiş yaptım, ancak bu genel sayıların aynı olduğu durumda görünüyor.

Dikkate değer bir istisna, ALU mul ve FPU mul gibi görünüyor. Toplama ve çıkarma çok farklı görünüyor.

İşte grafik biçiminde yukarıdakiler (tam boyut için tıklayın, daha düşük, daha hızlı ve tercih edilir):

Yukarıdaki verilerin tablosu

@Peter Cordes'e uyum sağlamak için güncelleme

https://gist.github.com/Lewiscowles1986/90191c59c9aedf3d08bf0b129065cccc

i7 4700MQ Linux Ubuntu Xenial 64 bit (tüm yamalar 2018-03-13 uygulandı)
    short add: 0.773049
    short sub: 0.789793
    short mul: 0.960152
    short div: 3.273668
      int add: 0.837695
      int sub: 0.804066
      int mul: 0.960840
      int div: 3.281113
     long add: 0.829946
     long sub: 0.829168
     long mul: 0.960717
     long div: 5.363420
long long add: 0.828654
long long sub: 0.805897
long long mul: 0.964164
long long div: 5.359342
    float add: 1.081649
    float sub: 1.080351
    float mul: 1.323401
    float div: 1.984582
   double add: 1.081079
   double sub: 1.082572
   double mul: 1.323857
   double div: 1.968488
AMD Opteron (tm) İşlemci 4122 (hassas, DreamHost paylaşımlı barındırma)
    short add: 1.235603
    short sub: 1.235017
    short mul: 1.280661
    short div: 5.535520
      int add: 1.233110
      int sub: 1.232561
      int mul: 1.280593
      int div: 5.350998
     long add: 1.281022
     long sub: 1.251045
     long mul: 1.834241
     long div: 5.350325
long long add: 1.279738
long long sub: 1.249189
long long mul: 1.841852
long long div: 5.351960
    float add: 2.307852
    float sub: 2.305122
    float mul: 2.298346
    float div: 4.833562
   double add: 2.305454
   double sub: 2.307195
   double mul: 2.302797
   double div: 5.485736
Intel Xeon E5-2630L v2 @ 2.4GHz (Güvenilir 64-bit, DigitalOcean VPS)
    short add: 1.040745
    short sub: 0.998255
    short mul: 1.240751
    short div: 3.900671
      int add: 1.054430
      int sub: 1.000328
      int mul: 1.250496
      int div: 3.904415
     long add: 0.995786
     long sub: 1.021743
     long mul: 1.335557
     long div: 7.693886
long long add: 1.139643
long long sub: 1.103039
long long mul: 1.409939
long long div: 7.652080
    float add: 1.572640
    float sub: 1.532714
    float mul: 1.864489
    float div: 2.825330
   double add: 1.535827
   double sub: 1.535055
   double mul: 1.881584
   double div: 2.777245

gcc5 belki gcc4.6'nın yapmadığı bir şeyi otomatik olarak vektörleştirir? Is benchmark-pcthroughput ve gecikme bazı kombinasyonlarını ölçme? Haswell'inizde (i7 4700MQ), tamsayı çarpma, saat başına 1, 3 döngü gecikmesidir, ancak tam sayı ekleme / çıkarma , saat başına 4, 1 döngü gecikmesidir ( agner.org/optimize ). Bu nedenle, muhtemelen toplama ve mul'un çok yakın çıkması için bu sayıları seyrelten çok fazla döngü yükü vardır (uzun toplama: 0.824088'e karşı uzun mul: 1.017164). (gcc, çok düşük yineleme sayılarının tam olarak açılması dışında, varsayılan olarak döngüleri açmama ayarıdır).
Peter Cordes

Ve BTW, neden intsadece test etmiyor shortve long? Linux x86-64'te, short16 bittir (ve bu nedenle bazı durumlarda kısmi kayıt yavaşlamaları vardır), longve long longher ikisi de 64 bit türlerdir. (Belki de x86-64'ün hala 32 bit kullandığı Windows için tasarlanmıştır long? Veya belki de 32 bit modu için tasarlanmıştır.) Linux'ta, x32 ABI long64 bit modunda 32 bit'e sahiptir , bu nedenle kitaplıklarınız yüklüyse gcc -mx32ILP32 için derleyicide kullanın . Ya da sadece rakamlara -m32bakın ve kullanın long.
Peter Cordes

Ve derleyicinizin herhangi bir şeyi otomatik hale getirip getirmediğini gerçekten kontrol etmelisiniz. Örneğin , addpsyerine xmm yazmaçları kullanmak, addss4 FP eklemek için, skaler kadar hızlı bir komutta paralel olarak ekleme yapmak addss. ( -march=nativeYalnızca x86-64 için SSE2 temelini değil, CPU'nuzun desteklediği komut setlerini kullanmaya izin vermek için kullanın ).
Peter Cordes

@cincodenada lütfen 15'in tamamını yan tarafta gösteren çizelgeleri bırakın, çünkü o zaman performansın bir göstergesi.
MrMesees

@PeterCordes Yarın bakmaya çalışacağım, çalışkanlığınız için teşekkür ederim.
MrMesees

7

Dikkate alınması gereken iki nokta -

Modern donanım, talimatları üst üste bindirebilir, bunları paralel olarak yürütebilir ve donanımı en iyi şekilde kullanmak için yeniden sıralayabilir. Ayrıca, herhangi bir önemli kayan nokta programı, yalnızca dizilere, döngü sayacına vb. İndisleri hesaplasa bile, büyük olasılıkla önemli bir tamsayı çalışmasına sahip olacaktır, bu nedenle yavaş bir kayan nokta talimatınız olsa bile, ayrı bir donanım bitinde çalışıyor olabilir. bazı tamsayı çalışmalarıyla örtüşüyor. Demek istediğim, kayan nokta komutları tam sayı komutlarından daha yavaş olsa bile, genel programınız daha hızlı çalışabilir çünkü daha fazla donanımdan faydalanabilir.

Her zaman olduğu gibi, emin olmanın tek yolu gerçek programınızın profilini çıkarmaktır.

İkinci nokta, bugünlerde çoğu CPU'nun, aynı anda birden fazla kayan nokta değerinde çalışabilen kayan nokta için SIMD komutlarına sahip olmasıdır. Örneğin, tek bir SSE yazmacına 4 kayan nokta yükleyebilir ve hepsi üzerinde paralel olarak 4 çarpma gerçekleştirebilirsiniz. SSE talimatlarını kullanmak için kodunuzun bazı kısımlarını yeniden yazabilirseniz, muhtemelen bir tamsayı sürümünden daha hızlı olacaktır. Visual c ++, bunu yapmak için derleyici iç işlevleri sağlar, bazı bilgiler için bkz. Http://msdn.microsoft.com/en-us/library/x5c07e2a(v=VS.80).aspx .


Win64'te FPU komutlarının artık MSVC derleyicisi tarafından oluşturulmadığına dikkat edilmelidir. Kayan nokta her zaman orada SIMD talimatlarını kullanır. Bu, Win32 ve Win64 arasında floplarla ilgili olarak büyük bir hız farkına neden olur.
James Dunne

5

Kalan işlem yoksa, kayan noktalı sürüm çok daha yavaş olacaktır. Tüm eklemeler sıralı olduğundan, cpu toplamı paralel hale getiremeyecektir. Gecikme kritik olacak. FPU ekleme gecikmesi tipik olarak 3 döngüdür, tamsayı toplama ise 1 döngüdür. Bununla birlikte, modern cpu'larda tam olarak boru hattına bağlı olmadığından, kalan operatör için bölücü muhtemelen kritik kısım olacaktır. bu nedenle, bölme / kalan komutunun zamanın çoğunu tüketeceğini varsayarsak, gecikme eklemeden kaynaklanan fark küçük olacaktır.


4

Saniyede milyonlarca kez çağrılacak bir kod yazmadığınız sürece (örneğin, bir grafik uygulamasında ekrana bir çizgi çizmek gibi), tamsayı ve kayan nokta aritmetiği nadiren darboğazdır.

Verimlilik sorularının olağan ilk adımı, çalışma süresinin gerçekte nerede harcandığını görmek için kodunuzun profilini çıkarmaktır. Bunun için linux komutu gprof.

Düzenle:

Her ne kadar tamsayılar ve kayan noktalı sayılar kullanarak çizgi çizme algoritmasını her zaman uygulayabileceğinizi düşünmeme rağmen, onu çok sayıda arayın ve bir fark yaratıp yaratmadığını görün:

http://en.wikipedia.org/wiki/Bresenham's_algorithm


2
Bilimsel uygulamalar FP kullanır. FP'nin tek avantajı, hassasiyetin ölçekle değişmez olmasıdır. Bilimsel gösterim gibi. Sayıların ölçeğini zaten biliyorsanız (örneğin, çizgi uzunluğunun bir dizi piksel olduğunu), FP engellenir. Ama çizgiyi çizmeye başlamadan önce bu doğru değil.
Potatoswatter

4

Günümüzde tamsayı işlemleri genellikle kayan noktalı işlemlerden biraz daha hızlıdır. Dolayısıyla, tamsayı ve kayan noktalı aynı işlemlerle bir hesaplama yapabiliyorsanız, tamsayı kullanın. ANCAK "Bu, pek çok can sıkıcı soruna neden olur ve çok sayıda can sıkıcı kod ekler" diyorsunuz. Bu, kayan nokta yerine tamsayı aritmetiği kullandığınız için daha fazla işleme ihtiyacınız varmış gibi görünüyor. Bu durumda, kayan nokta daha hızlı çalışacaktır çünkü

  • Daha fazla tamsayı işlemine ihtiyaç duyduğunuz anda, muhtemelen çok daha fazlasına ihtiyacınız olacaktır, bu nedenle hafif hız avantajı, ek işlemler tarafından tüketilenden daha fazladır.

  • kayan noktalı kod daha basittir; bu, kodu yazmanın daha hızlı olduğu anlamına gelir; bu, eğer hız kritikse, kodu optimize etmek için daha fazla zaman harcayabileceğiniz anlamına gelir.


Burada, donanımda bulunan ve genellikle hesaplama süresine hakim olan ikincil etkilerin hiçbirini hesaba katmayan pek çok çılgın spekülasyon var. Kötü bir başlangıç ​​noktası değil, ancak her uygulamada profil oluşturma yoluyla kontrol edilmesi ve müjde olarak öğretilmemesi gerekiyor.
Ben Voigt

3

Sayıya rand () yerine 1 ekleyen bir test yaptım. Sonuçlar (x86-64'te) şunlardı:

  • kısa: 4.260s
  • int: 4.020s
  • uzun uzun: 3.350s
  • şamandıra: 7.330s
  • çift: 7.210s

1
Kaynak, derleme seçenekleri ve zamanlama yöntemi? Sonuçlara biraz şaşırdım.
GManNickG

OP ile aynı döngü, "rand ()% 365", "1" ile değiştirilir. Optimizasyon yok. "Zaman" komutundan kullanıcı zamanı.
dan04

13
Anahtar "optimizasyon yok". Optimizasyon kapalıyken profil oluşturmazsınız, daima "bırakma" modunda profil çıkarırsınız.
Dean Harding

2
Bu durumda, yine de, optimizasyon kapalı, operasyonu gerçekleşmeye zorlar ve kasıtlı olarak yapılır - döngü, zamanı makul bir ölçüm ölçeğine genişletmek için oradadır. 1 sabitinin kullanılması rand () maliyetini ortadan kaldırır. Yeterince akıllı bir optimizasyon derleyicisi, döngüden çıkmadan 100.000.000 kez 1 eklendiğini ve tek bir işlemde 100000000 eklediğini görecektir. Bu, tüm amacı aşıyor, değil mi?
Stan Rogers

7
@Stan, değişkeni uçucu yap. Akıllı bir optimizasyon derleyicisi bile o zaman birden çok işlemi onurlandırmalıdır.
vladr

0

O kadar güvenilir "duyduğum bir şey" temelinde, eski günlerde tamsayı hesaplaması kayan noktadan yaklaşık 20 ila 50 kat daha hızlıydı ve bugünlerde iki katından daha az daha hızlı.


1
Lütfen görüşten daha fazlasını sunan buna tekrar bakmayı düşünün (özellikle görüşün toplanan gerçekler karşısında uçtuğu göz önüne alındığında)
MrMesees

1
@MrMesees Bu cevap çok kullanışlı olmasa da yaptığınız testlerle tutarlı olduğunu söyleyebilirim. Ve tarihsel önemsiz şeyler de muhtemelen iyidir.
Jonatan Öström

Gün içinde 286'larla çalışan biri olarak teyit edebilirim; "Evet onlar vardı!"
David H Parry
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.