Optimasi Generik
Di sini sebagai beberapa pengoptimalan favorit saya. Saya sebenarnya telah meningkatkan waktu eksekusi dan mengurangi ukuran program dengan menggunakan ini.
Deklarasikan fungsi kecil sebagai inline
atau makro
Setiap panggilan ke suatu fungsi (atau metode) menimbulkan overhead, seperti mendorong variabel ke tumpukan. Beberapa fungsi juga dapat menimbulkan biaya tambahan. Fungsi atau metode yang tidak efisien memiliki lebih sedikit pernyataan dalam isinya daripada overhead gabungan. Ini adalah kandidat yang baik untuk penyebarisan, baik sebagai #define
makro atau inline
fungsi. (Ya, saya tahu inline
ini hanya saran, tetapi dalam hal ini saya menganggapnya sebagai pengingat bagi kompiler.)
Hapus kode yang mati dan berlebihan
Jika kode tidak digunakan atau tidak berkontribusi pada hasil program, singkirkan.
Sederhanakan desain algoritme
Saya pernah menghapus banyak kode assembly dan waktu eksekusi dari program dengan menuliskan persamaan aljabar yang dihitungnya dan kemudian menyederhanakan ekspresi aljabar. Implementasi ekspresi aljabar yang disederhanakan memakan lebih sedikit ruang dan waktu daripada fungsi aslinya.
Ulangi Membuka gulungan
Setiap loop memiliki overhead pemeriksaan incrementing dan terminasi. Untuk mendapatkan perkiraan faktor kinerja, hitung jumlah instruksi di overhead (minimal 3: kenaikan, periksa, goto start of loop) dan bagi dengan jumlah pernyataan di dalam loop. Semakin rendah angkanya semakin baik.
Edit: berikan contoh loop unrolling Before:
unsigned int sum = 0;
for (size_t i; i < BYTES_TO_CHECKSUM; ++i)
{
sum += *buffer++;
}
Setelah membuka gulungan:
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++;
}
Dalam keuntungan ini, keuntungan kedua diperoleh: lebih banyak pernyataan dieksekusi sebelum prosesor harus memuat ulang cache instruksi.
Saya mendapatkan hasil yang luar biasa ketika saya membuka loop ke 32 pernyataan. Ini adalah salah satu hambatan karena program harus menghitung checksum pada file 2GB. Pengoptimalan ini dikombinasikan dengan pembacaan blok meningkatkan kinerja dari 1 jam menjadi 5 menit. Loop unrolling memberikan kinerja yang sangat baik dalam bahasa assembly juga, my memcpy
jauh lebih cepat daripada compiler memcpy
. - TM
Pengurangan if
pernyataan
Prosesor membenci cabang, atau lompatan, karena memaksa prosesor untuk memuat ulang antrian instruksinya.
Aritmatika Boolean ( Diedit: format kode yang diterapkan ke fragmen kode, contoh tambahan)
Ubah if
pernyataan menjadi tugas boolean. Beberapa prosesor dapat menjalankan instruksi secara kondisional tanpa bercabang:
bool status = true;
status = status && /* first test */;
status = status && /* second test */;
The arus pendek dari Logical AND operator ( &&
) mencegah pelaksanaan tes jika status
ini false
.
Contoh:
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;
};
Faktor Alokasi Variabel di luar loop
Jika variabel dibuat dengan cepat di dalam loop, pindahkan pembuatan / alokasi ke before loop. Dalam kebanyakan kasus, variabel tidak perlu dialokasikan selama setiap iterasi.
Faktorkan ekspresi konstan di luar loop
Jika nilai kalkulasi atau variabel tidak bergantung pada indeks loop, pindahkan ke luar (sebelum) loop.
I / O dalam blok
Membaca dan menulis data dalam potongan besar (blok). Lebih besar lebih baik. Misalnya, membaca satu oktek dalam satu waktu kurang efisien dibandingkan membaca 1024 oktet dengan sekali pembacaan.
Contoh:
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);
Efisiensi teknik ini dapat ditunjukkan secara visual. :-)
Jangan gunakan printf
keluarga untuk data konstan
Data konstan dapat dikeluarkan dengan menggunakan penulisan blok. Penulisan berformat akan membuang waktu memindai teks untuk memformat karakter atau memproses perintah pemformatan. Lihat contoh kode di atas.
Format ke memori, lalu tulis
Format ke char
array menggunakan beberapa sprintf
, lalu gunakan fwrite
. Ini juga memungkinkan tata letak data untuk dipecah menjadi "bagian konstan" dan bagian variabel. Pikirkan gabungan surat .
Deklarasikan teks konstan (string literal) sebagai static const
Ketika variabel dideklarasikan tanpa static
, beberapa kompiler mungkin mengalokasikan ruang pada stack dan menyalin data dari ROM. Ini adalah dua operasi yang tidak perlu. Ini bisa diperbaiki dengan menggunakan static
awalan.
Terakhir, Kode seperti kompilator
Terkadang, kompilator dapat mengoptimalkan beberapa pernyataan kecil dengan lebih baik daripada satu versi rumit. Selain itu, menulis kode untuk membantu pengoptimalan kompilator juga membantu. Jika saya ingin kompilator menggunakan instruksi transfer blok khusus, saya akan menulis kode yang sepertinya harus menggunakan instruksi khusus.