İmzasız ve imzalı tamsayılar arasındaki performans farkı nedir? [kapalı]


42

İmzalı girişleri şamandıralarla karıştırırken ortaya çıkan performansın farkındayım.

İmzasız girişleri şamandıralarla karıştırmak daha mı kötü?

Yüzdürme olmadan işaretli / imzasız karıştırma işlemi sırasında herhangi bir vuruş var mı?

Farklı boyutların (u32, u16, u8, i32, i16, i8) performans üzerinde herhangi bir etkisi var mı? Hangi platformlarda?


2
PS3'e özgü metni / etiketi kaldırdım, çünkü bu herhangi bir mimari hakkında iyi bir soru ve cevap, hemen hemen hepsi olan tam sayı ve kayan nokta kayıtlarını ayıran tüm mimariler için geçerli.

Yanıtlar:


36

İçerdiği miktara (her türlü) ve yüzdürme işlemine verilen büyük ceza, bunların farklı kayıt kümelerinden kaynaklanmasıdır. Bir kayıt setinden diğerine geçmek için, değeri belleğe yazmalı ve onu bir yük-hit-mağaza duraklaması içeren geri okumanız gerekir .

Farklı boyutlar arasında veya işaretlerin sayısı arasında gidilmesi, her şeyi aynı kayıt kümesinde tutar, böylece büyük cezadan kaçınırsınız. İşaret uzatmaları, vb. Nedeniyle daha küçük cezalar olabilir, ancak bunlar bir yük hit dükkanından çok daha küçüktür.


Bağladığınız makale, PS3 Hücre İşlemcisinin buna istisna olduğunu belirtir çünkü görünüşe göre her şey aynı kayıt kümesinde saklanır (yaklaşık olarak makalenin ortasında bulunabilir veya "Hücre" yi arayabilirsiniz).
serseri

4
@bummzack: Bu sadece KKD'ler için geçerlidir, KKD'ye değil; SPE'ler çok özel bir kayan nokta ortamına sahiptir ve oyuncular hala nispeten pahalıdır. Ayrıca, imzalı ve imzasız tamsayılar için maliyetler aynıdır.

Bu iyi bir makale ve LHS hakkında bilmek önemlidir (ve bunun için oy kullanıyorum) ama sorum işaretlerle ilgili olan bu cezalar hakkında. Bunların küçük ve muhtemelen önemsiz olduğunu biliyorum, ancak yine de onlar hakkında bazı gerçek rakamlar veya referanslar görmek istiyorum.
Luis

1
@ Louis - Bununla ilgili bazı kamu belgelerini bulmaya çalışıyordum ama şu anda bulamıyorum. Xbox360 belgelerine erişiminiz varsa, bunların bir kısmını kapsayan (ve genel olarak çok iyi) Bruce Dawson tarafından iyi bir beyaz kağıt var.
celion

@ Louis: Aşağıda bir analiz yayınladım, ancak sizi tatmin ederse, lütfen celion'a cevabı verin - söylediği her şey doğru, tek yaptığım GCC'yi birkaç kez çalıştırmak.

12

Xbox 360 ve PS3 ile ilgili bilgilerin, özellikle düşük seviyeli ayrıntıların olduğu gibi yalnızca lisanslı geliştiricilerin duvarlarının arkasında olacağından şüpheleniyorum. Bununla birlikte, eşdeğer bir x86 programı oluşturabilir ve genel bir fikir edinmek için onu sökebiliriz.

İlk olarak, imzasız genişletme maliyetlerinin ne olduğunu görelim:

unsigned char x = 1;
unsigned int y = 1;
unsigned int z;
z = x;
z = y;

İlgili kısım, (GCC 4.4.5 kullanarak) içinde parçalanır:

    z = x;
  27:   0f b6 45 ff             movzbl -0x1(%ebp),%eax
  2b:   89 45 f4                mov    %eax,-0xc(%ebp)
    z = y;
  2e:   8b 45 f8                mov    -0x8(%ebp),%eax
  31:   89 45 f4                mov    %eax,-0xc(%ebp)

Yani temelde aynı - bir durumda bir baytı, diğerinde bir kelimeyi taşırız. Sonraki:

signed char x = 1;
signed int y = 1;
signed int z;
z = x;
z = y;

Dönüşür:

   z = x;
  11:   0f be 45 ff             movsbl -0x1(%ebp),%eax
  15:   89 45 f4                mov    %eax,-0xc(%ebp)
    z = y;
  18:   8b 45 f8                mov    -0x8(%ebp),%eax
  1b:   89 45 f4                mov    %eax,-0xc(%ebp)

Bu yüzden, işaret uzatma maliyeti, maliyetten movsblziyade maliyetidir movzbl- alt komut seviyesi. Modern işlemcilerin çalışma şekli nedeniyle modern işlemcileri ölçmek imkansız. Hafıza hızından önbelleğe almaya kadar önceden boru hattında bulunanlara kadar her şey çalışma zamanına hükmedecek.

~ 10 dakikada bu testleri yazmam çok kolay oldu, kolayca gerçek bir performans hatası buldum ve herhangi bir derleyici optimizasyon seviyesini açtığımda, kod bu kadar basit işler için tanınmayacak hale geldi.

Bu Yığın Taşması değildir, bu yüzden burada kimse mikrooptimizasyonun önemli olmadığını iddia edecektir. Oyunlar genellikle çok büyük ve çok sayısal olan veriler üzerinde çalışır, bu nedenle dallara, atmalara, zamanlamaya, yapıya uyum göstermeye vb. Dikkat ederek çok kritik geliştirmeler yapabilir. PPC kodunu optimize etmek için çok zaman harcayan herkesin büyük olasılıkla load-hit-mağazaları hakkında en az bir korku hikayesi vardır. Ancak bu durumda, gerçekten önemli değil. Tamsayı türünüzün depolama boyutu, hizalanıp kayıt defterine sığdığı sürece performansı etkilemez.


2
(CW çünkü bu gerçekten celion'un cevabı üzerine bir yorum ve insanların hangi kod değişikliklerini daha açıklayıcı hale getirmek zorunda kalabileceklerini merak ediyorum.)

PS3 CPU'yla ilgili bilgiler kolayca ve yasal olarak edinilebilir olduğundan, PS3 ile ilgili CPU konularının tartışılması bir sorun değildir. Sony, OtherOS desteğini kaldırana kadar Linux'u bir PS3'e sokup programlayabilir. GPU limit dışıydı ancak CPU (SPE'ler dahil) iyi. OtherOS desteği olmasa bile, uygun GCC'yi kolayca alabilir ve kod geninin nasıl olduğunu görebilirsiniz.
JasonD

@Jason: Yazımı CW olarak işaretledim, bu yüzden birisi bunu yaparsa bilgileri sağlayabilir. Bununla birlikte, Sony'nin resmi GameOS derleyicisine erişimi olan - ki gerçekten önemli olan tek kişi - büyük olasılıkla bunu yapmaktan men edilmiştir.

Aslında, işaretli tam sayı PPC IIRC'de daha pahalıdır. Küçük bir performans vuruşuna sahip, ancak işte orada ... ayrıca PS3 PPU / SPU ayrıntılarının çoğu burada: jheriko-rtw.blogspot.co.uk/2011/07/ps3-ppuspu-docs.html ve burada: jheriko-rtw.blogspot.co.uk/2011/03/ppc-instruction-set.html . Bu GameOS derleyicisinin ne olduğunu merak ediyor musunuz? Bu GCC karşılaştırıcısı mı yoksa SNC mi? Yukarıda belirtilenler dışındaki iirc, en içteki döngüleri optimize etmek hakkında konuşurken imzalanan karşılaştırmaların bir ek yüküne sahiptir. Bunu açıklayan dokümanlara erişimim yok - ve yapsam bile ...
jheriko

4

İmzalı tamsayı işlemleri neredeyse tüm mimarilerde daha pahalı olabilir. Örneğin, bir sabit tarafından bölünme, imzalanmadığında daha hızlıdır, örneğin:

unsigned foo(unsigned a) { return a / 1024U; }

için optimize edilecek:

unsigned foo(unsigned a) { return a >> 10; }

Fakat...

int foo(int a) { return a / 1024; }

şuna göre optimize eder:

int foo(int a) {
  return (a + 1023 * (a < 0)) >> 10;
}

veya dallanmanın ucuz olduğu sistemlerde,

int foo(int a) {
  if (a >= 0) return a >> 10;
  else return (a + 1023) >> 10;
}

Aynısı modulo için de geçerli. Bu aynı zamanda 2 güçleri için de geçerlidir (ancak örnek daha karmaşıktır). Mimarinizde bir donanım bölünmesi yoksa (örneğin, çoğu ARM), imzasız konstrüksiyon dışı ayraçlar da daha hızlıdır.

Genel olarak, derleyiciye negatif sayıların elde edilemeyeceğini söylemek, özellikle döngü sonlandırma ve diğer koşullamalar için kullanılan ifadelerin optimizasyonuna yardımcı olacaktır.

Farklı boyuttaki girişlere gelince, evet, küçük bir etki var, ancak etrafta daha az bellek bulunduğunu düşünmeniz gerekir. Bugünlerde muhtemelen boyut genişlemesinden kaybettiğinizden daha az belleğe erişmekten daha fazlasını alıyorsunuz. Bu noktada mikro optimizasyon konusunda çok uzağınız var.


Optimize edilmiş kodunuzu, GCC’nin ürettiği şeyleri daha da yansıtması için değiştirdim. Bir test + lea, bunu dalsız yapmanıza izin verdiğinde dalın yanıltıcı olması.

2
Belki x86'da. ARMv7'de sadece şartlı olarak yürütülür.
John Ripley

3

İmzalı veya imzasız int işlemler, geçerli işlemcilerde (x86_64, x86, powerpc, arm) aynı maliyete sahiptir. 32 bit işlemcide, u32, u16, u8 s32, s16, s8 aynı olmalıdır. Kötü hizalamada ceza alabilirsin.

Fakat int'yi float'a veya flo'a int'ye dönüştürmek pahalı bir işlemdir. Optimize edilmiş uygulamayı kolayca bulabilirsiniz (SSE2, Neon ...).

En önemli nokta muhtemelen hafıza erişimidir. Verileriniz L1 / L2 önbelleğine sığmıyorsa, dönüşümden daha fazla döngü kaybedersiniz.


2

Jon Purdy yukarıda (imzam edemediğim) imzasız olanın daha yavaş olabileceğini çünkü taşmadığını söylüyor. Katılmıyorum, işaretsiz aritmetik basit moular aritmetik modulo 2 kelimesindeki bit sayısı kadar. İlke olarak imzalanan işlemler taşmalara maruz kalabilir, ancak bunlar genellikle kapatılır.

Bazen bir int içine iki veya daha fazla veri öğesi paketi gibi zeki (ancak okunaklı şeyler değil) yapabilir ve komut başına birden çok işlem (cep aritmetik) alabilirsiniz. Ama ne yaptığını anlamalısın. Tabii ki MMX bunu doğal olarak yapmanızı sağlar. Ancak bazen en büyük HW destekli kelime boyutunu kullanmak ve verileri elle paketlemek en hızlı uygulamayı sağlar.

Veri hizalamasına dikkat edin. Çoğu HW uygulamasında, hizalanmamış yükler ve depolar daha yavaştır. Doğal hizalama, 4 baytlık bir sözcük için, adresin dördün katı olduğu anlamına gelir ve sekiz baytlık kelime adreslerinin sekiz baytın katı olması gerektiği anlamına gelir. Bu, SSE'ye geçer (128bit, 16 baytlık hizalamayı tercih eder). AVX yakında bu "vektör" yazmaç boyutlarını 256 bit, sonra 512 bit olacak şekilde genişletecek. Ve hizalı yükler / depolar, hizalanmamış olanlardan daha hızlı olacaktır. HW meraklıları için bir hizalanmamış hafıza işlemi, HW'nin dikkatli olması gereken önbellek ve hatta sayfa sınırları gibi şeylere yayılabilir.


1

Döngü dizinleri için işaretli tamsayıları kullanmak biraz daha iyidir, çünkü imzalı taşma C'de tanımsızdır, bu nedenle derleyici bu tür ilmeklerin daha az köşe durumu olduğunu varsayar. Bu, gcc'nin "-fstrict-overflow" (varsayılan olarak etkindir) tarafından kontrol edilir ve bunun montaj çıktısını okumadan farkedilmesi zor olabilir.

Bunun ötesinde, x86 türleri karıştırmazsanız daha iyi çalışır, çünkü bellek işlenenleri kullanabilir. Türleri dönüştürmesi gerekiyorsa (işaret veya sıfır uzantı) bu, açık bir yük ve bir yazıcının kullanılması anlamına gelir.

Yerel değişkenler için int ile sopa ve bu çoğu varsayılan olarak olacaktır.


0

Celion'un işaret ettiği gibi, ints ve floatlar arasında dönüşüm yükü büyük ölçüde, kayıtlar arasındaki değerlerin kopyalanması ve dönüştürülmesi ile ilgilidir. Kendi içinde ve kendilerinde imzasız girişlerin tek eki, derlenmiş kodda belirli bir miktar taşma kontrolü gerektiren garantili sarma davranışlarından kaynaklanmaktadır.

Temelde işaretli ve işaretsiz tamsayılar arasında dönüşüm yok. Tamsayı Farklı boyutlarda olabilir olmak (infinitesimally) daha hızlı veya platforma bağlı olarak erişmek için daha yavaş. Genel olarak konuşursak, platformun kelime boyutuna en yakın olan tam sayı boyutu, erişilmesi en hızlı olacaktır , ancak genel performans farkı, diğer birçok faktöre, en önemlisi önbellek boyutuna bağlıdır: uint64_ttüm ihtiyacınız olduğunda kullanırsanız uint32_t, verilerinizin daha azının bir kerede önbelleğe sığabileceğinden ve bir miktar ek yüke maruz kalabileceğinizden emin olun.

Bununla birlikte, bunun hakkında düşünmek bile biraz aşırı. Verilerinize uygun türleri kullanırsanız, işler mükemmel şekilde sonuçlanmalıdır ve mimariye dayalı türler seçerek kazanılacak güç miktarı yine de ihmal edilebilir.


Hangi taşma kontrolünden bahsediyorsunuz? Birleştiriciden daha düşük bir seviye demek istemediğiniz sürece, iki inç eklemek için kullanılan kod çoğu sistemde aynıdır ve örneğin işaret büyüklüğü kullanan birkaç kişi için uzun sürmez. Sadece farklı.

@JoeWreschnig: Kahretsin. Onu bulamıyorum ama en azından belirli platformlarda tanımlanmış sarmalama davranışı için farklı assembler çıktı hesaplamaları örnekleri gördüğümü biliyorum. Bulabildiğim tek ilgili yazı: stackoverflow.com/questions/4712315/…
Jon Purdy

Farklı sarma davranışı için farklı montajcı çıktısı, derleyicinin imzalı durumda, örneğin b> 0 ise a + b> a olması durumunda, imzalı taşma tanımının tanımlanmadığı (ve bu nedenle güvenilir olamayacağı) optimizasyonlar yapmasıdır. Bu gerçekten tamamen farklı bir durum.
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.