Genel Optimizasyonlar
En sevdiğim optimizasyonlardan bazıları burada. Bunları kullanarak yürütme sürelerini artırdım ve program boyutlarını azalttım.
Küçük işlevleri inline
makro olarak bildirin
Bir işleve (veya yönteme) yapılan her çağrı, değişkenleri yığına göndermek gibi ek yüklere neden olur. Bazı işlevler de dönüşte ek yüke neden olabilir. Verimsiz bir işlev veya yöntem, içeriğinde birleşik ek yükten daha az ifadeye sahiptir. Bunlar inlineing için iyi adaylardır.#define
Makrolar veya inline
fonksiyonlar . (Evet, inline
sadece bir öneri olduğunu biliyorum , ancak bu durumda bunu derleyiciye bir hatırlatma olarak kabul ediyorum .)
Ölü ve gereksiz kodu kaldırın
Kod kullanılmazsa veya programın sonucuna katkıda bulunmazsa, ondan kurtulun.
Algoritma tasarımını basitleştirin
Bir keresinde, hesapladığı cebirsel denklemi yazarak bir programdan çok sayıda montaj kodunu ve yürütme süresini kaldırdım ve ardından cebirsel ifadeyi basitleştirdim. Basitleştirilmiş cebirsel ifadenin uygulanması, orijinal fonksiyondan daha az yer ve zaman aldı.
Döngü Açılıyor
Her döngü, bir artırma ve sonlandırma denetimi ek yüküne sahiptir. Performans faktörünün bir tahminini elde etmek için, ek yükteki talimatların sayısını sayın (minimum 3: artış, kontrol, döngünün başlangıcına git) ve döngü içindeki ifade sayısına bölün. Sayı ne kadar düşükse o kadar iyidir.
Düzenleme: Önce:
unsigned int sum = 0;
for (size_t i; i < BYTES_TO_CHECKSUM; ++i)
{
sum += *buffer++;
}
Kaydırdıktan sonra:
unsigned int sum = 0;
size_t i = 0;
**const size_t STATEMENTS_PER_LOOP = 8;**
for (i = 0; i < BYTES_TO_CHECKSUM; **i = i / STATEMENTS_PER_LOOP**)
{
sum += *buffer++; // 1
sum += *buffer++; // 2
sum += *buffer++; // 3
sum += *buffer++; // 4
sum += *buffer++; // 5
sum += *buffer++; // 6
sum += *buffer++; // 7
sum += *buffer++; // 8
}
// Handle the remainder:
for (; i < BYTES_TO_CHECKSUM; ++i)
{
sum += *buffer++;
}
Bu avantajda, ikincil bir fayda elde edilir: İşlemcinin talimat önbelleğini yeniden yüklemesi gerekmeden önce daha fazla ifade yürütülür.
Bir döngüyü 32 ifadeye açtığımda harika sonuçlar elde ettim. Bu, programın 2GB'lık bir dosyada bir sağlama toplamı hesaplaması gerektiğinden, darboğazlardan biriydi. Bu optimizasyon, blok okuma ile birleştiğinde performansı 1 saatten 5 dakikaya yükseltti. Döngü açma, assembly dilinde de mükemmel performans sağladı, benim memcpy
derleyicininkinden çok daha hızlıydı memcpy
. - TM
if
İfadelerin azaltılması
İşlemciler, işlemciyi talimat sırasını yeniden yüklemeye zorladığı için dallardan veya atlamalardan nefret eder.
Boole Aritmetiği ( Düzenlendi: kod parçasına uygulanan kod formatı, örnek eklendi)
if
İfadeleri boole atamalarına dönüştürün . Bazı işlemciler, dallanma olmaksızın talimatları koşullu olarak yürütebilir:
bool status = true;
status = status && /* first test */;
status = status && /* second test */;
Kısa devre arasında mantıksal ve (operatör &&
eğer varsa) testlerin uygulanmasını engellemektedir status
olanfalse
.
Misal:
struct Reader_Interface
{
virtual bool write(unsigned int value) = 0;
};
struct Rectangle
{
unsigned int origin_x;
unsigned int origin_y;
unsigned int height;
unsigned int width;
bool write(Reader_Interface * p_reader)
{
bool status = false;
if (p_reader)
{
status = p_reader->write(origin_x);
status = status && p_reader->write(origin_y);
status = status && p_reader->write(height);
status = status && p_reader->write(width);
}
return status;
};
Döngülerin dışında Faktör Değişken Tahsisi
Döngü içinde anında bir değişken oluşturulursa, oluşturma / tahsis işlemini döngünün öncesine taşıyın. Çoğu durumda, değişkenin her yineleme sırasında tahsis edilmesi gerekmez.
Döngülerin dışındaki faktör sabiti ifadeleri
Bir hesaplama veya değişken değeri döngü indeksine bağlı değilse, onu döngünün dışına (öncesinde) taşıyın.
Bloklar halinde I / O
Verileri büyük parçalar halinde (bloklar) okuyun ve yazın. Daha büyük daha iyi. Örneğin, bir seferde bir sekizli okumak, tek okumayla 1024 sekizli okumaktan daha az verimlidir.
Misal:
static const char Menu_Text[] = "\n"
"1) Print\n"
"2) Insert new customer\n"
"3) Destroy\n"
"4) Launch Nasal Demons\n"
"Enter selection: ";
static const size_t Menu_Text_Length = sizeof(Menu_Text) - sizeof('\0');
//...
std::cout.write(Menu_Text, Menu_Text_Length);
Bu tekniğin etkinliği görsel olarak gösterilebilir. :-)
Sürekli veri için printf
aileyi kullanmayın
Sabit veri, blok yazma kullanılarak çıkarılabilir. Biçimlendirilmiş yazma, karakterleri biçimlendirmek veya biçimlendirme komutlarını işlemek için metni tararken zaman kaybına neden olur. Yukarıdaki kod örneğine bakın.
Belleğe formatlayın, sonra yazın
char
Birden çok kullanarak bir dizi biçimlendirin sprintf
, sonra kullanın fwrite
. Bu aynı zamanda veri düzeninin "sabit bölümlere" ve değişken bölümlere ayrılmasına izin verir. Adres mektup birleştirmeyi düşünün .
Sabit metni (dize değişmezleri) olarak bildir static const
Değişkenler olmadan bildirildiğinde static
, bazı derleyiciler yığın üzerinde alan ayırabilir ve verileri ROM'dan kopyalayabilir. Bunlar iki gereksiz işlemdir. Bu, static
önek kullanılarak düzeltilebilir .
Son olarak, derleyicinin yapacağı gibi kod
Bazen derleyici birkaç küçük ifadeyi bir karmaşık sürümden daha iyi optimize edebilir. Ayrıca, derleyicinin optimize etmesine yardımcı olacak kod yazmak da yardımcı olur. Derleyicinin özel blok transfer talimatlarını kullanmasını istersem, özel talimatları kullanması gerektiği gibi görünen bir kod yazacağım.