CPU seviyesinde hangi opcode daha hızlı? [kapalı]


19

Her programlama dilinde, diğerlerine göre önerilen opcodes setleri vardır. Bunları burada, hız sırasına göre listelemeye çalıştım.

  1. Bitsel
  2. Tamsayı Toplama / Çıkarma
  3. Tamsayı Çarpma / Bölme
  4. karşılaştırma
  5. Kontrol akışı
  6. Şamandıra Toplama / Çıkarma
  7. Şamandıra Çarpma / Bölme

Yüksek performanslı koda ihtiyacınız olduğunda, C ++, SIMD talimatlarını veya daha verimli kontrol akışını, veri türlerini vb.Kullanmak için montajda elle optimize edilebilir. Bu yüzden veri türünün (int32 / float32 / float64) veya kullanılan işlem ( *, +, &), CPU seviyesinde performansını etkiler.

  1. CPU'da tek bir çarpma işleminden bir kat daha yavaş mı?
  2. MCU teorisinde, opkodların hızının yürütmek için gereken CPU döngüsü sayısı tarafından belirlendiğini öğrenirsiniz. Yani, çarpma 4 döngü ve toplama 2 alır?
  3. Temel matematik ve kontrol akış opcodlarının hız özellikleri tam olarak nedir?
  4. İki opkodun yürütülmesi için aynı sayıda döngü gerekiyorsa, her ikisi de herhangi bir performans kazancı / kaybı olmadan birbirinin yerine kullanılabilir mi?
  5. X86 CPU performansı ile ilgili paylaşabileceğiniz diğer teknik ayrıntılar takdir edilmektedir

17
Bu, erken optimizasyona çok benziyor ve derleyicinin yazdıklarınızı vermediğini ve gerçekten de yoksa gerçekten montaj yazmak istemediğinizi unutmayın.
Roy T.

3
Şamandıra çarpma ve bölme tamamen farklı şeylerdir, aynı kategoriye koymamalısınız. N-bit sayıları için çarpma bir O (n) işlemidir ve bölme bir O (nlogn) işlemidir. Bu, bölünmeyi modern CPU'lardaki çarpma işleminden yaklaşık 5 kat daha yavaş hale getirir.
sam hocevar

1
Tek gerçek cevap "profil" dir.
Tetrad

1
Roy'un cevabına göre, gerçekten istisnai değilseniz, el optimizasyonu montajı neredeyse her zaman net bir kayıp olacaktır. Modern CPU'lar çok karmaşık canavarlardır ve iyi optimize edici derleyiciler, tamamen açık olmayan ve elle kodlama için önemsiz olmayan kod dönüşümlerini çeker. SSE / SIMD için bile, her zaman C / C ++ 'da intrinsics kullanın ve derleyicinin kullanımlarını sizin için optimize etmesine izin verin. Ham montajı kullanmak derleyici optimizasyonlarını devre dışı bırakır ve çok büyük kaybedersiniz.
Sean Middleditch

SIMD'yi kullanmak için montajı el ile optimize etmeniz gerekmez. SIMD, duruma bağlı olarak optimize etmek için çok yararlıdır, ancak SSE2'yi kullanmak için çoğunlukla standart bir kural vardır (en azından GCC ve MSVC üzerinde çalışır). Listenizle ilgili olarak, modern bir süperserslar çok borulu işlemcide, veri bağımlılığı ve kayıt basıncı ham tamsayı ve bazen kayan nokta performansından daha fazla soruna neden olur; aynı şey veri yerelliği için de geçerlidir. Bu arada, tamsayı bölümü, modern bir
x86'daki

Yanıtlar:


26

Agner Fog'un optimizasyon kılavuzları mükemmel. Tüm yeni x86 CPU tasarımlarının (Intel Pentium'a kadar uzanan) mikro mimarisinde kılavuzları, talimat zamanlamaları ve belgeleri var. Ayrıca /programming//tags/x86/info adresinden bağlanan diğer bazı kaynaklara da bakın

Sadece eğlenmek için, bazı soruları cevaplayacağım (son Intel CPU'larının numaraları). Op seçimi, kodu optimize etmede ana faktör değildir (bölünmeyi engelleyemediğiniz sürece).

CPU'da tek bir çarpma işleminden bir kat daha yavaş mı?

Evet (2 gücü olmadan). (3-4x gecikme süresi, Intel'de saat başına yalnızca bir işlem.) Bundan kaçınmak için yolunuzun dışına çıkmayın, çünkü 2 veya 3 ekler.

Temel matematik ve kontrol akış opcodlarının hız özellikleri tam olarak nedir?

Eğer bilmek istiyorsanız Agner Sis talimat tabloları ve mikro mimari kılavuzu bakınız aynen : P. Koşullu sıçramalara dikkat edin. Koşulsuz sıçramalar (işlev çağrıları gibi) küçük bir ek yüke sahiptir, ancak çok fazla değildir.

İki opkodun yürütülmesi için aynı sayıda döngü gerekiyorsa, her ikisi de herhangi bir performans kazancı / kaybı olmadan birbirinin yerine kullanılabilir mi?

Hayır, başka bir şeyle aynı yürütme bağlantı noktası için rekabet edebilirler veya olmayabilirler. İşlemcinin paralel olarak çalışabileceği diğer bağımlılık zincirlerine bağlıdır. (Uygulamada, genellikle yararlı bir karar verilmez. Bazen, Intel CPU'larda farklı bağlantı noktalarında çalışan bir vektör kayması veya bir vektör karıştırması kullanabilirsiniz. PSLLDQvb.) karıştırma ünitesinde çalışır.)

X86 CPU performansı ile ilgili paylaşabileceğiniz diğer teknik ayrıntılar takdir edilmektedir

Agner Fog'un mikroarka belgeleri, Intel ve AMD CPU'ların boru hatlarını, bir döngünün yineleme başına tam olarak kaç döngü alması gerektiğini ve darboğazın uop verimi, bağımlılık zinciri veya bir yürütme bağlantı noktası için çekişme olup olmadığını yeterince ayrıntılı olarak açıklar. Gibi StackOverflow'daki benim bazı yanıtlar, bakın bu bir ya bu bir .

Ayrıca, http://www.realworldtech.com/haswell-cpu/ (ve önceki tasarımlar için benzer) CPU tasarımını seviyorsanız eğlenceli bir okuma.

İşte en iyi konuklarıma dayanarak bir Haswell CPU için sıralanmış listeniz. Yine de bu, bir şey için bir şey düşünmek için yararlı bir yol değil. Önbellek / dal tahmini etkileri genellikle baskındır, bu nedenle iyi kalıplara sahip olmak için kodunuzu yazın. Sayılar çok dalgalıdır ve verim bir sorun olmasa bile yüksek gecikmeyi hesaba katmaya veya başka şeylerin paralel olarak gerçekleşmesi için boruyu tıkayan daha fazla uops üretmeye çalışın. Esp. önbellek / şube numaraları çok oluşur. Döngü ile taşınan bağımlılıklar için gecikme, her yineleme bağımsız olduğunda verim önemlidir.

TL: DR bu sayılar, gecikme, yürütme-bağlantı noktası darboğazları ve ön uç verimi (veya şube özlüyorları gibi şeyler için duraklar) kadar "tipik" bir kullanım örneği için hayal ettiğim şeylere dayanarak oluşturulmuştur. ). Lütfen bu sayıları herhangi bir ciddi perf analizi için kullanmayın .

  • 0,5 ila 1 Bitsel / Tamsayı Toplama / Çıkarma /
    kaydırma ve döndürme (derleme zamanı sabit sayısı) /
    bunların tüm vektör sürümleri (döngü verimi başına 1 ila 4, 1 döngü gecikmesi)
  • 1 vektör min, maks, karşılaştır-eşit, karşılaştır-daha büyük (maske oluşturmak için)
  • 1.5 vektör karıştırır. Haswell ve daha yenisinin sadece bir shuffle portu var ve bana ihtiyacınız varsa çok fazla karıştırmaya ihtiyaç duyuyor, bu yüzden daha az shuffle kullanmayı düşünmeyi teşvik etmek için biraz daha fazla ağırlık veriyorum. Özgür değiller, esp. bellekten bir pshufb kontrol maskesine ihtiyacınız varsa.
  • 1.5 yükleme / depolama (L1 önbellek isabet. Gecikme süresi daha iyi)
  • 1.75 Tamsayı Çarpma (Intel'de 1c tput başına 3c gecikme / bir, AMD'de 4c lat ve 2c tput'ta yalnızca bir adet). Küçük sabitler LEA ve / veya ADD / SUB / shift kullanarak daha ucuzdur . Ancak elbette derleme zamanı sabitleri her zaman iyidir ve genellikle başka şeylere de optimize edebilir. (Ve bir döngüde çarpma genellikle derleyici tarafından tmp += 7bunun yerine bir döngüde güç azaltılabilir tmp = i*7)
  • 1.75 bazı 256b vektör karıştırması (bir AVX vektörünün 128b şeritleri arasında veri taşıyabilen insns üzerinde ekstra gecikme). (Veya şerit geçişi karışıklıklarının daha fazla uops'a ihtiyaç duyduğu Ryzen'de 3 ila 7)
  • 2 fp add / sub (ve aynı vektör versiyonları) (döngü geçişi başına 1 veya 2, 3 ila 5 döngü gecikmesi). Gecikme üzerinde tıkanırsanız yavaş olabilir, örneğin sadece 1 sumdeğişkenli bir diziyi toplamak . (Bunu ağırlık ve kullanım durumuna bağlı olarak 1 kadar düşük veya 5 kadar yüksek olabilir).
  • 2 vektör fp mul veya FMA. (x * y + z, FMA desteği etkinken derlerseniz bir katır veya bir eklenti kadar ucuzdur).
  • 2 genel elemanların vektör elemanlarına ( _mm_insert_epi8vb.) Yerleştirilmesi / çıkarılması
  • 2.25 vektör int mul (16-bit elemanlar veya 8 x 8 -> 16-bit yapan pmaddubsw). Skylake üzerinde daha ucuz, skaler mul'dan daha iyi verim
  • Değişken sayısına göre 2.25 kaydırma / döndürme (2c gecikme süresi, Intel'de 2c verim başına bir, AMD veya BMI2 ile daha hızlı)
  • 2.5 Dallanma olmadan karşılaştırma ( y = x ? a : b, veya y = x >= 0) ( test / setccveya cmov)
  • 3 int-> float dönüşümü
  • 3 mükemmel tahmin Kontrol akışı (tahmini şube, çağrı, dönüş).
  • 4 vektör int mul (32 bit elemanlar) (2 uops, Haswell'de 10c gecikme süresi)
  • 4 tamsayı bölme veya %derleme zamanı sabiti (2'nin gücü olmayan).
  • 7 vektör yatay ops (örn. PHADDBir vektör içine değer ekleme)
  • 11 (vektör) FP Bölümü (10-13c gecikme, 7c verim başına bir veya daha kötüsü). (Nadiren kullanılırsa ucuz olabilir, ancak verim FP mul'den 6 ila 40x daha kötüdür)
  • 13? Kontrol Akışı (kötü tahmin edilen dal, belki% 75 tahmin edilebilir)
  • İnt int bölümü ( evet , aslında FP bölümünden daha yavaştır ve vektörleştirilemez). (derleyicilerin mul / shift / add ile sihirli bir sabit kullanarak bir sabite bölündüğünü unutmayın ve div / mod'un 2 güç ile çok ucuz olduğunu unutmayın.)
  • 16 (vektör) FP sqrt
  • 25? yük (L3 önbellek isabet). (önbellek özledim mağazaları yüklerden daha ucuzdur.)
  • 50? FP trig / exp / log. Çok fazla exp / log'a ihtiyacınız varsa ve tam doğruluğa ihtiyacınız yoksa, daha kısa polinom ve / veya bir tabloyla hız için doğruluk ticaretini yapabilirsiniz. SIMD vectorizasyonu da yapabilirsiniz.
  • 50-80? her zaman - öngörülen şube, 15-20 döngü maliyeti
  • 200-400? yükle / sakla (önbellek özledim)
  • 3000 ??? dosyayı dosyadan oku (işletim sistemi disk önbellek isabet) (sayıları burada oluşturarak)
  • 20000 ??? disk okuma sayfası (OS disk önbellek kaçırma, hızlı SSD) (tamamen oluşturulmuş numara)

Tamamen tahminlere dayanarak uydurdum . Bir şeyler yanlış görünüyorsa, bunun nedeni farklı bir kullanım durumu veya bir düzenleme hatası düşündüğüm içindi.

Kaydırma sayısı değişken olduğunda daha hızlı tamsayı kaydırıcıları olması dışında AMD işlemcilerdeki göreceli maliyet benzer olacaktır. AMD Bulldozer ailesi CPU'lar çeşitli nedenlerden dolayı elbette çoğu kodda daha yavaştır. (Ryzen birçok konuda oldukça iyidir).

Bir şeyleri tek boyutlu bir maliyete kaynatmanın gerçekten imkansız olduğunu unutmayın . Önbellek özledikleri ve şube yanlış tahminleri dışında, bir kod bloğundaki darboğaz gecikme, toplam üop verimi (ön uç) veya belirli bir portun (yürütme portu) verimi olabilir.

Etraftaki kod CPU'yu diğer işlerle meşgul tutarsa ​​FP bölümü gibi "yavaş" bir işlem çok ucuz olabilir . (vektör FP div veya sqrt her biri 1 uop, sadece kötü gecikme ve verim var. Sadece bölme birimini engelliyorlar, tüm yürütme portunu değil. Tamsayı div birkaç uops.) Yani sadece bir FP bölmeniz varsa her ~ 20 mul ve ekleme için ve CPU'nun yapacağı başka işler de var (örneğin, bağımsız bir döngü yinelemesi), o zaman FP div'in "maliyeti" bir FP mul ile aynı olabilir. Bu muhtemelen tüm yaptığınız zaman düşük iş hacmi olan bir şeyin en iyi örneğidir, ancak düşük toplam uops nedeniyle diğer kodlarla (gecikme bir faktör olmadığında) çok iyi karışır.

Tamsayı bölümünün çevre koduyla neredeyse aynı olmadığına dikkat edin: Haswell'de 9 uops, 8-11c verim başına bir ve 22-29c gecikme süresine sahip. (64 bit bölünmesi Skylake'de bile çok daha yavaştır.) Dolayısıyla, gecikme ve işlem sayıları FP div ile biraz benzer, ancak FP div sadece bir uop.

Verim, gecikme ve toplam uops için kısa bir insns dizisini analiz etme örnekleri için, SO cevaplarımdan bazılarına bakın:

İDK bu tür analizler de dahil olmak üzere SO cevapları yazarsa. Kendimi bulmak için çok daha kolay bir zamanım var, çünkü bu ayrıntıya sık sık girdiğimi biliyorum ve ne yazdığımı hatırlıyorum.


4'teki "tahmini şube" mantıklıdır - 20-25'deki "tahmini şube" gerçekten ne olmalıdır? (Yanlış tahmin edilen dalların (13 civarında listelenen) bundan çok daha pahalı olduğunu düşünmüştüm, ama tam da bu yüzden gerçeğe daha yakın bir şey öğrenmek için bu sayfadayım - büyük masa için teşekkürler!)
Matt

@Matt: Bence bu bir düzenleme hatasıydı ve "yanlış tahmin edilen dal" olması gerekiyordu. Bunu işaret ettiğiniz için teşekkürler. 13'ün yanlış tahmin edilen bir dal için olduğunu, her zaman yanlış tahmin edilen bir dal için olmadığını unutmayın, bu yüzden açıklığa kavuşturdum. El yıkamayı yeniden yaptım ve bazı düzenlemeler yaptım. : P
Peter Cordes

16

Söz konusu CPU'ya bağlıdır, ancak modern bir CPU için liste şu şekildedir:

  1. Bitsel, toplama, çıkarma, karşılaştırma, çarpma
  2. Bölünme
  3. Kontrol akışı (bkz. Cevap 3)

CPU'ya bağlı olarak 64 bit veri türleriyle çalışmak için önemli bir ücret olabilir.

Sorularınız:

  1. Modern bir CPU'da hiç veya kayda değer ölçüde değil. CPU'ya bağlı.
  2. Bu bilgi 20 ila 30 yıl eski (Okul berbat, şimdi kanıt var) gibi bir şey, modern CPU'lar saat başına değişken sayıda talimatı idare ediyor, kaç tanesi programlayıcının ne olduğuna bağlı.
  3. Bölme diğerlerinden biraz daha yavaştır, dal tahmini doğru ise kontrol akışı çok hızlı ve yanlışsa çok yavaştır (20 döngü gibi bir şey CPU'ya bağlıdır). Sonuç, çok sayıda kodun esas olarak kontrol akışı ile sınırlı olmasıdır. ifAritmetik ile makul bir şekilde yapabileceğiniz bir şeyle yapmayın.
  4. Herhangi bir komutun kaç döngü alacağına dair sabit bir sayı yoktur, ancak bazen iki farklı talimat eşit şekilde çalışabilir, bunları başka bir bağlama koyabilir ve belki de yapmazlar, farklı bir CPU'da çalıştırırlar ve muhtemelen 3. bir sonuç görürsünüz.
  5. Kontrol akışının üstünde diğer büyük zaman kaybı önbellek özlüyor, önbellekteki olmayan verileri okumaya çalıştığınızda CPU'nun bellekten alınmasını beklemesi gerekecektir. Genel olarak, her yerden veri toplamak yerine, veri parçalarını yan yana aynı anda tutmaya çalışmalısınız.

Ve son olarak, bir oyun yapıyorsanız, tüm bunlar hakkında çok fazla endişelenmeyin, CPU döngülerini kesmekten daha iyi bir oyun yapmaya odaklanın.


Ayrıca FPU'nun oldukça hızlı olduğunu da belirtmek isterim: özellikle Intel'de - sabit nokta sadece deterministik sonuçlar istiyorsanız gerçekten gereklidir.
Jonathan Dickinson

2
Son bölüme daha çok önem veririm - iyi bir oyun yapın. Kodun anlaşılır olmasına yardımcı olur - bu nedenle 3. bir performans sorununu gerçekten ölçtüğünüzde geçerlidir. İhtiyaç olursa bu ifs'leri daha iyi bir şeye dönüştürmek her zaman kolaydır. Öte yandan, 5. daha zordur - kesinlikle ilk önce düşünmek istediğiniz bir durum olduğuna katılıyorum, çünkü genellikle mimariyi değiştirmek anlamına gelir.
Luaan

3

X64_64 üzerinde bir milyon kez ilmekledi tamsayı operasyon cadı hakkında bir test yaptım, aşağıdaki gibi kısa bir sonuca ulaştım,

--- 116 mikrosaniye ekle

alt ---- 116 mikrosaniye

mul ---- 1036 mikrosaniye

div ---- 13037 mikrosaniye

yukarıdaki veriler döngüden kaynaklanan ek yükü zaten azalttı,


2

Intel işlemci kılavuzları web sitelerinden ücretsiz olarak indirilebilir. Oldukça büyükler ancak teknik olarak sorunuza cevap verebilirler. Özellikle optimizasyon kılavuzu peşinde olduğunuz şeydir, ancak kullanım kılavuzunda ayrıca çipten çipe değiştiği için simd talimatları için büyük CPU hatlarının çoğu için zamanlamalar ve gecikmeler vardır.

Genel olarak, tam dalların yanı sıra işaretçi kovalamayı (bağlantı listesi traveralleri, sanal işlevleri çağıran) mükemmel katiller için en üstte düşünürdüm, ancak x86 / x64 cpus, diğer mimarilere kıyasla her ikisinde de çok iyi. Başka bir platforma geçerseniz, yüksek performanslı kod yazıyorsanız, ne kadar sorunun olabileceğini göreceksiniz.


+1, bağımlı yükler (işaretçi takibi) büyük bir sorun. Önbellek kaçırma gelecekteki yüklerin başlamasını bile engelleyecektir. Bir seferde uçuş sırasında ana bellekten çok fazla yük olması, bir op'a sahip olmaktan öncekinin tamamen tamamlanmasını gerektirir.
Peter Cordes
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.