Optimasi Dasar
Terkait pengoptimalan, banyak orang yang meremehkan, "Kecepatan komputer sekarang sangat cepat, apa gunanya menjadi beberapa persen lebih cepat?" Hal ini masuk akal. Hasil yang dikompilasi oleh kompiler saat ini telah sepenuhnya dioptimalkan. Kecuali untuk pengembangan perangkat lunak tertentu seperti grafik, gambar, dan multimedia, pengoptimalan yang disengaja tidak diperlukan dalam banyak kasus, tetapi jika pengembang sedang menulis kode. waktu, Anda sudah memiliki kesadaran akan pengoptimalan. Saat menyelesaikan pengoptimalan, Anda dapat memastikan atau bahkan meningkatkan efisiensi pengembangan.
Tentu saja, desain algoritme adalah inti dari pengoptimalan. Dalam banyak kasus, efisiensi eksekusi program terutama ditentukan oleh pemahaman keseluruhan pengembang terhadap program, desain algoritme, dll.! Namun terkadang pengoptimalan detail juga masuk akal!
Terlebih lagi, dalam banyak kasus optimasi semacam ini tidak memerlukan penulisan kode secara langsung melalui assembly, namun dalam hal ini juga dapat mencerminkan keunggulan penguasaan pengetahuan assembly!
Seperti dua fungsi berikut:
fungsi GetBit(i: Kardinal; n: Kardinal): Boolean;
mulai
Hasil := Boolean((i shr n) dan 1);
akhir;
fungsi GetBit(i: Kardinal; n: Kardinal): Boolean;
mulai
Hasil := Boolean((1 shl n) dan i);
akhir;
Kode perakitan yang sesuai:
MOV ECX, EDX
SHR EAX, CL
DAN EAX, $01
MOV ECX, EDX
MOV EDX, $01
SHL EDX, CL
DAN EAX, EDX
Fungsinya sama, semuanya mengambil nilai bit i tertentu, mengembalikan True jika 1, dan False jika 0!
Di permukaan, Anda mungkin berpikir bahwa efisiensi eksekusi kedua fungsi itu sama, namun sebenarnya ada perbedaan. Operasi shift dari program pertama dilakukan pada i. Menurut konvensi pemanggilan default di Delphi, register, saat ini, i Nilai disimpan dalam register EAX, dan operasi shift dapat diselesaikan secara langsung; tetapi program kedua berbeda untuk menyelesaikan operasi shift pada nilai langsung 1, maka harus ditransfer ke register terlebih dahulu. jadi harus ada satu instruksi lagi! Tentu saja, tidak dalam semua kasus, instruksi yang lebih sedikit akan lebih cepat daripada instruksi yang lebih banyak. Selama eksekusi tertentu, kita juga harus mempertimbangkan masalah-masalah seperti siklus jam eksekusi instruksi dan pemasangan instruksi (lebih lanjut tentang ini nanti). masalah secara mandiri. Hanya jika Perbandingan hanya dapat dilakukan di lingkungan kode tertentu.
Dalam keadaan normal, perbedaan efisiensi ini terlalu kecil, tetapi menjaga kesadaran akan pengoptimalan selama pemrograman bukanlah hal yang buruk! Jika kode tersebut terletak di lapisan terdalam dari sebuah loop, dan N siklus jam terakumulasi melalui sejumlah besar loop, perbedaan dalam efisiensi eksekusi bisa menjadi sangat besar!
Di atas hanyalah contoh kecil. Dapat dilihat bahwa jika Anda dapat memikirkan beberapa masalah dari perspektif perakitan selama pengembangan, Anda dapat menulis kode detail yang lebih efisien dalam bahasa tingkat tinggi sambil memastikan efisiensi pengembangan! Namun masih sering kali pengoptimalan mendetail harus diselesaikan menggunakan kode rakitan yang disematkan, dan terkadang karena penerapan kode rakitan yang disematkan, penulisan kode juga menjadi lebih efisien.
Jika Anda perlu membalik urutan byte dari angka 32 digit, bagaimana Anda bisa melakukannya sepenuhnya dalam bahasa tingkat tinggi di Delphi? Anda dapat menggunakan shifting, Anda juga dapat memanggil fungsi Swap bawaan beberapa kali, tetapi jika Anda memikirkan instruksi BSWAP, semuanya menjadi sangat sederhana.
fungsi SwapLong(Nilai: Kardinal): Kardinal;
asm
BSWAP EAX
akhir;
Catatan: Sama seperti di atas, nilai Value disimpan di register EAX, dan nilai 32 digit juga dikembalikan melalui EAX, jadi hanya diperlukan satu kalimat.
Tentu saja, sebagian besar optimasi perakitan tertanam tidak sesederhana itu, tetapi sulit untuk mencapai optimasi yang lebih mendalam dengan sedikit pengetahuan perakitan yang dipelajari di perguruan tinggi. Pengalaman hanya dapat diperoleh melalui akumulasi terus-menerus dan perbandingan kode perakitan yang dikompilasi! Untungnya, dalam banyak kasus, optimasi mendetail bukanlah bagian utama dari desain program.
Namun jika program yang dikembangkan melibatkan grafik, gambar, multimedia, dll, masih perlu dilakukan optimasi yang lebih mendalam! Untungnya, Delphi6 dapat memberikan dukungan yang baik baik itu optimasi instruksi floating point atau penerapan MMX, SSE, 3DNow, dll. Bahkan jika Anda ingin Delphi versi sebelumnya mendukung set instruksi yang diperluas CPU ini atau ingin mendukung set instruksi CPU baru di masa depan, Anda dapat menggunakan empat instruksi perakitan DB, DW, DD, dan DQ yang didukung oleh Delphi dalam perakitan tertanam ( di Delphi6 resmi Borland Panduan bahasa hanya mengatakan bahwa itu mendukung DB, DW, dan DD) dan representasi numerik dari instruksi yang relevan juga dapat diimplementasikan secara fleksibel.
menyukai:
DW $A20F //CPUID
DW $770F //EMMS
DB $0F, $6F, $C1 //MOVQ MM0, MM1
Memahami instruksi hanyalah dasar saja. Setelah merancang algoritma seputar FPU, MMX, dan SSE, jika ingin mengoptimalkannya lebih lanjut, Anda juga harus memahami beberapa karakteristik teknis dari CPU itu sendiri.
Mari kita lihat dua buah kode berikut:
asm
TAMBAHKAN [a], ECX
TAMBAHKAN [b], EDX
akhir
asm
MOV EAX, [a]
MOV EBX, [b]
TAMBAHKAN EAX, ECX
TAMBAHKAN EBX, EDX
MOV [a], EAX
MOV [b], EBX
akhir
Apakah cara kedua lebih efisien? Salah, seperti disebutkan di atas, instruksi yang lebih sedikit tidak berarti efisiensi eksekusi yang tinggi. Menurut informasi yang relevan, siklus jam untuk eksekusi dua instruksi di bagian pertama kode adalah 3 (setiap instruksi perlu menyelesaikan tiga langkah pembacaan, memodifikasi dan menulis langkah), siklus jam yang dieksekusi oleh enam instruksi di bagian kedua kode semuanya 1. Jadi kedua bagian kode tersebut sama efisiennya? Salah lagi, potongan kode kedua sebenarnya dieksekusi lebih efisien daripada kode pertama! Mengapa? Karena CPU setelah kelas Pentium memiliki dua jalur pipa untuk mengeksekusi instruksi, ketika dua instruksi yang berdekatan dapat dipasangkan, keduanya dapat dieksekusi pada waktu yang sama! Khusus untuk dua potongan kode di atas, apa alasan spesifiknya?
Meskipun dua instruksi pada kode pertama dapat dipasangkan, total siklus jam eksekusi yang diperlukan adalah 5, bukan 3, sedangkan enam instruksi pada kode kedua dapat dieksekusi secara paralel, yang menghasilkan hasil ini.
Omong-omong, ini semua adalah contoh yang sangat sederhana, yang dengan sendirinya tidak dapat banyak membantu Anda. Jika Anda benar-benar ingin mengoptimalkan program tertentu, Anda harus mencari beberapa artikel khusus tentang optimasi FPU dan MMX, atau menemukan manual teknis untuk mempelajari dan mempelajari teknologi seperti "eksekusi di luar urutan" dan "prediksi cabang". Saya hanya berharap agar semua teman-teman yang masih kuliah tidak hanya fokus pada alat pengembangan yang "menghasilkan uang" dan teknologi baru yang modis, tetapi dapat meluangkan lebih banyak waktu untuk meletakkan fondasinya. Dengan landasan yang kokoh, Anda dapat dengan cepat menguasai ilmu-ilmu baru , Hanya dengan cara ini kita dapat menguasai alat dan keterampilan pengembangan baru dalam waktu lebih cepat... (hilangkan seribu kata).
Namun sekali lagi, pengetahuan masih perlu digunakan untuk memecahkan masalah praktis setiap hari, jika Anda hanya fokus pada detail teknis setiap hari, Anda mungkin menjadi peretas yang hebat, tetapi Anda tidak akan pernah mengembangkan perangkat lunak kelas satu. Oleh karena itu, tujuan mendasarnya tetap harus menciptakan nilai. Jadi... Saya tidak akan membicarakannya lagi, tidak akan terlihat seperti artikel teknis jika saya membicarakannya lagi. ^_^
Lampiran: Selain mempertimbangkan efisiensi eksekusi, optimalisasi program juga harus mempertimbangkan masalah ukuran (ukuran kecil dapat memuat memori lebih cepat dan menyelesaikan decoding instruksi dan tugas lainnya lebih cepat). , EAX bukan MOV EAX, $0. Meskipun siklus jam eksekusi keduanya 1, panjang instruksi yang pertama (2 byte) jelas lebih pendek daripada yang terakhir (5 byte). Namun karena yang disebutkan di atas hanyalah rincian, maka masalah volume tidak disebutkan. Lebih banyak masalah pengurangan ukuran harus diserahkan kepada kompiler untuk diselesaikan.