10 ile çarpmak için say (i << 3) + (i << 1) kullanmak i * 10'u doğrudan kullanmaktan daha mı hızlı?
Makinenizde olabilir veya olmayabilir - eğer ilgilenirseniz, gerçek dünya kullanımınızı ölçün.
Bir vaka çalışması - 486'dan core i7'ye
Kıyaslama anlamlı olarak yapmak çok zordur, ancak birkaç gerçeğe bakabiliriz. Gönderen http://www.penguin.cz/~literakl/intel/s.html#SAL ve http://www.penguin.cz/~literakl/intel/i.html#IMUL biz x86 saat döngüsü hakkında bir fikir edinmek aritmetik kaydırma ve çarpma için gereklidir. Diyelim ki "486" (listelenen en yenisi), 32 bit kayıt ve hemen, IMUL 13-42 döngü ve IDIV 44'e sadık kalıyoruz. Her SAL 2 alır ve 1 ekler, bu yüzden birkaç tanesi birlikte yüzeysel olarak değişir. kazanan gibi.
Bugünlerde core i7 ile:
( http://software.intel.com/en-us/forums/showthread.php?t=61481 adresinden )
Gecikme, bir tamsayı ekleme için 1 döngü ve bir tamsayı çarpma için 3 döngüdür . Gecikmeleri ve düşünceleri http://www.intel.com/products/processor/manuals/ adresinde bulunan "Intel® 64 ve IA-32 Mimarileri Optimizasyon Referans Kılavuzu" nun Ek C'sinde bulabilirsiniz .
(bazı Intel bulanıklıklarından)
SSE'yi kullanarak Core i7, eşzamanlı ekleme ve çarpma talimatları yayınlayabilir, bu da saat döngüsü başına 8 kayan nokta işleminin (FLOP) en yüksek oranıyla sonuçlanır.
Bu size işlerin ne kadar ilerlediğine dair bir fikir verir. *
90'larda bile ciddiye alınan optimizasyon trivia'sı - birazcık kaymaya karşı - artık kullanılmıyor. Bit kaydırma hala daha hızlıdır, ancak tüm vardiyalarınızı yaptığınızda ve sonuçları eklediğinizde iki mul / div'ın gücü olmayanlar için tekrar yavaşlar. Daha fazla talimat, daha fazla önbellek hatası, boru hattında daha fazla potansiyel sorun, geçici kayıtların daha fazla kullanılması, kayıt içeriğinin yığından daha fazla kaydedilmesi ve geri yüklenmesi anlamına gelebilir ... tüm etkileri kesin olarak ölçmek çok karmaşık hale gelir, ancak ağırlıklı olarak olumsuz.
kodunda uygulama ve işlevsellik karşılaştırması
Daha genel olarak, sorunuz C ve C ++ olarak etiketlenir. 3. nesil diller olarak, temeldeki CPU komut setinin ayrıntılarını gizlemek için özel olarak tasarlanmıştır. Dil Standartlarını karşılamak için , temel donanım olmasa bile çarpma ve kaydırma işlemlerini (ve diğerlerini) desteklemelidirler . Bu gibi durumlarda, diğer birçok talimatı kullanarak gerekli sonucu sentezlemelidirler. Benzer şekilde, CPU eksikse ve FPU yoksa kayan nokta işlemleri için yazılım desteği sağlamalıdırlar. Modern CPU'ların hepsi destekliyor*
ve<<
, bu yüzden saçma teorik ve tarihsel görünebilir, ancak önemli olan şey, uygulama seçme özgürlüğünün her iki yöne de gitmesidir: CPU, genel durumda kaynak kodunda istenen işlemi uygulayan bir talimata sahip olsa bile, derleyici tercih ettiği başka bir şey seçin çünkü derleyicinin karşılaştığı özel durum için daha iyidir .
Örnekler (varsayımsal bir montaj dili ile)
source literal approach optimised approach
#define N 0
int x; .word x xor registerA, registerA
x *= N; move x -> registerA
move x -> registerB
A = B * immediate(0)
store registerA -> x
...............do something more with x...............
Exclusive veya ( xor
) gibi yönergelerin kaynak koduyla hiçbir ilişkisi yoktur, ancak kendisiyle herhangi bir şeyin xoring edilmesi tüm bitleri temizler, bu nedenle 0 olarak bir şey ayarlamak için kullanılabilir. Bellek adreslerini ima eden kaynak kodu, herhangi bir kullanımı gerektirmeyebilir.
Bu tür bilgisayar korsanları bilgisayarlar olduğu sürece kullanılmaktadır. 3GL'lerin ilk günlerinde, geliştirici alımını güvence altına almak için derleyici çıktısı mevcut hardcore el optimizasyonlu montaj dili geliştiricisini tatmin etmek zorunda kaldı. üretilen kodun daha yavaş, daha ayrıntılı veya daha kötü olmadığı bir topluluk. Derleyiciler çok sayıda büyük optimizasyonu hızlı bir şekilde benimsedi - her bir montaj dili programcısının olabileceğinden daha iyi bir merkezi mağaza haline geldiler, ancak her zaman belirli bir durumda çok önemli olan belirli bir optimizasyonu kaçırmaları ihtimali var - insanlar bazen derleyin ve daha iyi bir şey için okuyorsun, derleyiciler de birileri bu deneyimleri geri besleyene kadar söylendiği gibi yaparlar.
Bu nedenle, bazı donanımlarda kaydırma ve ekleme hala daha hızlı olsa bile, derleyici yazarının hem güvenli hem de yararlı olduğunda tam olarak çalışmış olması muhtemeldir.
İdame
Donanımınız değişirse, yeniden derleyebilirsiniz ve hedef CPU'ya bakıp en iyi seçimi yaparsınız, oysa "optimizasyonlarınızı" tekrar gözden geçirmek veya hangi derleme ortamlarının çarpma ve hangilerinin değişmesi gerektiğini listelemek olası değildir. 10+ yıl önce yazılan, modern işlemcilerde çalıştıklarında içerdikleri kodu yavaşlatan, iki bit kaydırmalı "optimizasyonları" düşünün!
Neyse ki, GCC gibi iyi derleyiciler, herhangi bir optimizasyon etkinleştirildiğinde (yani ...main(...) { return (argc << 4) + (argc << 2) + argc; }
-> imull $21, 8(%ebp), %eax
) bir dizi bit kaydırma ve aritmetiği doğrudan bir çarpma ile değiştirebilir, böylece yeniden derleme kodu düzeltmeden bile yardımcı olabilir, ancak bu garanti edilmez.
Çarpma veya bölme uygulayan garip bitshifting kodu, kavramsal olarak elde etmeye çalıştığınız şeyden çok daha az ifade edicidir, bu nedenle diğer geliştiriciler bununla karıştırılacaktır ve şaşkın bir programcının, görünüşte akıl sağlığını geri kazanmak için gerekli olan şeyleri tanıtması veya önemli bir şeyi kaldırması daha olasıdır. Sadece bariz şeyler gerçekten somut olarak faydalı olduklarında yaparsanız ve sonra bunları iyi bir şekilde belgelerseniz (ancak yine de sezgisel olan diğer şeyleri belgelemiyorsanız), herkes daha mutlu olacaktır.
Kısmi çözümlere karşı genel çözümler
Aşağıdaki gibi bazı ekstra bilgi varsa senin o int
irade gerçekten sadece değerleri depolamak x
, y
ve z
, o zaman bu değerler için çalışma ve derleyici en olmadığında daha çabuk size sonuç almak olduğunu bazı talimatlar çalışmak mümkün olabilir bu anlayış ve tüm int
değerler için çalışan bir uygulamaya ihtiyaç duyar . Örneğin, sorunuzu düşünün:
Çarpma ve bölme, bit operatörleri kullanılarak gerçekleştirilebilir ...
Çarpmayı resmediyorsunuz, ama bölünmeye ne dersiniz?
int x;
x >> 1; // divide by 2?
C ++ Standart 5.8'e göre:
-3- E1 >> E2'nin değeri E1 sağa kaydırılmış E2 bit konumudur. E1 imzasız bir türe sahipse veya E1 imzalı bir türe ve negatif olmayan bir değere sahipse, sonucun değeri E1 bölümünün E2 gücüne yükseltilen 2 miktarına bölünen parçasıdır. E1 imzalı bir türe ve negatif bir değere sahipse, elde edilen değer uygulama tanımlıdır.
Bu nedenle, bit kaydırma x
negatif olduğunda uygulama tanımlı bir sonuç verir : farklı makinelerde aynı şekilde çalışmayabilir. Ancak, /
çok daha öngörülebilir bir şekilde çalışır. ( Farklı makineler farklı negatif sayılar temsiline sahip olabileceğinden ve dolayısıyla temsili oluşturan aynı sayıda bit olsa bile farklı aralıklara sahip olabileceğinden mükemmel bir şekilde tutarlı olmayabilir.)
"Umurumda değil ... bu int
çalışanın yaşını saklıyor, asla olumsuz olamaz " diyebilirsiniz . Bu tür özel bir bilginiz varsa, evet - >>
kodunuzda açıkça yapmadığınız sürece güvenli optimizasyonunuz derleyici tarafından aktarılabilir. Ancak, bu tür bir kavrayışa sahip olmayacağınız zamanlar riskli ve nadiren yararlıdır ve aynı kod üzerinde çalışan diğer programcılar, evinize verdiğiniz bazı olağandışı beklentiler üzerine bahse girdiğinizi bilemezler. "optimizasyonunuz" nedeniyle tamamen güvenli bir değişiklik geri tepebilir.
Bu şekilde çoğaltılamayan veya bölünemeyen herhangi bir girdi var mı?
Evet ... yukarıda belirtildiği gibi, negatif sayılar bit kaydırmaya "bölündüğünde" uygulama tanımlı davranışa sahiptir.