Apakah menggunakan xor reg, reg memberikan keuntungan dibandingkan mov reg, 0? [duplikat]

Ada dua cara terkenal untuk menyetel register bilangan bulat ke nilai nol di x86.

Salah satu

mov reg, 0

or

xor reg, reg

Ada pendapat bahwa varian kedua lebih baik karena nilai 0 tidak disimpan dalam kode dan menghemat beberapa byte kode mesin yang dihasilkan. Ini jelas bagus - lebih sedikit cache instruksi yang digunakan dan ini terkadang memungkinkan eksekusi kode lebih cepat. Banyak kompiler menghasilkan kode seperti itu.

Namun secara formal ada ketergantungan antar-instruksi antara instruksi xor dan instruksi sebelumnya apa pun yang mengubah register yang sama. Karena ada ketergantungan, instruksi terakhir harus menunggu hingga instruksi pertama selesai dan ini dapat mengurangi beban unit prosesor dan mengganggu kinerja.

add reg, 17
;do something else with reg here
xor reg, reg

Jelas bahwa hasil xor akan sama persis terlepas dari nilai register awalnya. Tapi apakah prosesor mampu mengenali ini?

Saya mencoba tes berikut di VC++7:

const int Count = 10 * 1000 * 1000 * 1000;
int _tmain(int argc, _TCHAR* argv[])
{
    int i;
    DWORD start = GetTickCount();
    for( i = 0; i < Count ; i++ ) {
        __asm {
            mov eax, 10
            xor eax, eax
        };
    }
    DWORD diff = GetTickCount() - start;
    start = GetTickCount();
    for( i = 0; i < Count ; i++ ) {
        __asm {
            mov eax, 10
            mov eax, 0
        };
    }
    diff = GetTickCount() - start;
    return 0;
}

Dengan pengoptimalan yang dinonaktifkan, kedua loop membutuhkan waktu yang persis sama. Apakah ini cukup membuktikan bahwa prosesor mengakui bahwa tidak ada ketergantungan instruksi xor reg, reg pada instruksi mov eax, 0 sebelumnya? Tes apa yang lebih baik untuk memeriksa ini?


person sharptooth    schedule 16.07.2009    source sumber
comment
Saya rasa inilah alasan kami menggunakan bahasa tingkat tinggi. Jika Anda benar-benar ingin mengetahuinya, ubah saja tahapan codegen untuk melakukan yang satu atau yang lain. Tolok ukur. Pilih yang terbaik.   -  person jrockway    schedule 16.07.2009
comment
ah, trik xor reg, reg lama - masa lalu yang menyenangkan :)   -  person Nick Dandoulakis    schedule 16.07.2009
comment
Saya pikir arsitektur x86 secara eksplisit mendefinisikan XOR reg,reg sebagai memutus ketergantungan pada reg. Lihat manual arsitektur Intel. Saya berharap MOV reg,... melakukan hal yang sama hanya karena ini adalah MOV. Jadi pilihan Anda sebenarnya adalah, mana yang memakan lebih sedikit ruang (saya kira waktu eksekusinya sama), jika Anda tidak peduli dengan bit status (XOR merusak semuanya).   -  person Ira Baxter    schedule 21.07.2009
comment
Variabel Count Anda meluap, sehingga loop akan berjalan dengan siklus yang jauh lebih sedikit dari yang Anda harapkan   -  person phuclv    schedule 06.12.2013
comment
Pada arsitektur mikro yang lebih baru, xor reg,reg tidak memerlukan unit eksekusi (ditangani dalam decode?). Ini merusak ketergantungan pada reg, dan pembaruan sebagian bendera terhenti. Dan itu memiliki pengkodean yang lebih kecil. Tidak ada alasan bagus untuk pendekatan mov pada x86-64 terbaru, kecuali Anda harus mempertahankan tanda [e].   -  person Brett Hale    schedule 10.02.2014
comment
Ada beberapa keuntungan halus selain ukuran kode hingga menggunakan idiom zeroing yang dikenal seperti xor, dibandingkan dengan mov. Saya menulis jawaban pada pertanyaan yang lebih baru sebelum saya melihat yang ini: stackoverflow.com/questions/33666617/ . Saya pikir ini adalah jawaban yang lebih baik dan lebih lengkap daripada jawaban lainnya. Idealnya mereka harus ditandai sebagai duplikat satu sama lain.   -  person Peter Cordes    schedule 19.01.2016


Jawaban (6)


jawaban sebenarnya untuk Anda:

Manual Referensi Pengoptimalan Arsitektur Intel 64 dan IA-32

Bagian 3.5.1.8 adalah tempat yang ingin Anda lihat.

Singkatnya, ada situasi di mana xor atau mov mungkin lebih disukai. Permasalahannya berpusat pada rantai ketergantungan dan pelestarian kode kondisi.

person Mark    schedule 16.07.2009
comment
Sepertinya teks yang dikutip tidak merekomendasikan penggunaan MOV dalam situasi apa pun. - person mwfearnley; 07.05.2016
comment
@mwfearnley Sayangnya Addison memutuskan untuk mengedit jawaban saya dan memilih sebagian konten, tidak jelas mengapa hal ini dilakukan. Anda harus membaca dokumen lengkap yang mencakup situasi di mana mov lebih disukai. - person Mark; 09.05.2016
comment
Terima kasih telah menjelaskan. Saya kira itu adalah upaya untuk menghindari masalah dengan pemindahan/perubahan dokumen, tetapi sayangnya kutipan tersebut tidak memuat semua poin yang diperlukan.. Saya dapat melihat sekarang dari bagian itu, dikatakan untuk menggunakan MOV ketika Anda ingin menghindari mengatur kode kondisi. - person mwfearnley; 09.05.2016
comment
@mwfearnley: Jarang sekali Anda tidak bisa hanya melakukan xor-zero sebelum menyetel tanda. Lihat jawaban saya pada xor pertanyaan terbaru untuk beberapa saran tentang cara menghindari mov reg, 0 dalam persiapan untuk setcc. (Dan untuk detail lebih lanjut tentang semua kelebihan xor-zeroing). mov reg,0 / setcc sangat buruk pada CPU Intel lama, di mana membaca reg lengkap menyebabkan terhentinya sebagian register yang xor akan hindari. - person Peter Cordes; 09.05.2016

Pada CPU modern, pola XOR lebih disukai. Ini lebih kecil dan lebih cepat.

Lebih kecil sebenarnya penting karena pada banyak beban kerja nyata, salah satu faktor utama yang membatasi kinerja adalah kesalahan i-cache. Hal ini tidak akan ditangkap dalam tolok ukur mikro yang membandingkan kedua opsi tersebut, namun di dunia nyata hal ini akan membuat kode berjalan sedikit lebih cepat.

Dan, dengan mengabaikan berkurangnya i-cache yang hilang, XOR pada CPU mana pun dalam beberapa tahun terakhir memiliki kecepatan yang sama atau lebih cepat dari MOV. Apa yang lebih cepat daripada menjalankan instruksi MOV? Tidak menjalankan instruksi apa pun sama sekali! Pada prosesor Intel terbaru, logika pengiriman/penggantian nama mengenali pola XOR, 'menyadari' bahwa hasilnya akan menjadi nol, dan hanya mengarahkan register pada register nol fisik. Ia kemudian membuang instruksi tersebut karena tidak perlu menjalankannya.

Hasil akhirnya adalah pola XOR tidak menggunakan sumber daya eksekusi dan, pada CPU Intel terbaru, dapat 'mengeksekusi' empat instruksi per siklus. MOV unggul dalam tiga instruksi per siklus.

Untuk detailnya lihat postingan blog yang saya tulis ini:

https://randomascii.wordpress.com/2012/12/29/the-surprising-subtleties-of-zeroing-a-register/

Kebanyakan pemrogram tidak perlu mengkhawatirkan hal ini, namun penulis kompiler harus khawatir, dan memahami kode yang dihasilkan adalah hal yang baik, dan itu sangat keren!

person Bruce Dawson    schedule 19.03.2015
comment
Tulisan yang bagus! Saya ingin tahu apakah pola yang sama ada di Thumb. - person Asti; 16.01.2021
comment
Kemungkinan besar optimasi yang sama juga terjadi di Thumb. Pengoptimalan ini berlaku untuk semua prosesor yang rusak dan akan menghemat daya dan terkadang meningkatkan kinerja. Tapi, saya tidak tahu. - person Bruce Dawson; 17.01.2021

x86 memiliki instruksi dengan panjang variabel. MOV EAX, 0 memerlukan satu atau dua byte lebih banyak dalam ruang kode daripada XOR EAX, EAX.

person ajs410    schedule 15.10.2009
comment
mov eax, 0 adalah 5 byte: satu untuk opcode mov eax, imm32, dan 4 untuk 4B data langsung. xor eax, eax adalah 2 byte: satu opcode xor r32, r/m32, satu untuk operan. - person Peter Cordes; 12.12.2015

Saya tidak lagi bisa memperbaiki mobil saya sendiri setelah saya menjual station wagon HR tahun 1966 saya. Saya mengalami perbaikan serupa dengan CPU modern :-)

Ini sangat bergantung pada mikrokode atau sirkuit yang mendasarinya. Sangat mungkin bahwa CPU dapat mengenali "XOR Rn,Rn" dan hanya menghilangkan semua bit tanpa mengkhawatirkan isinya. Namun tentu saja, ia mungkin melakukan hal yang sama dengan "MOV Rn, 0". Kompiler yang baik akan memilih varian terbaik untuk platform target jadi ini biasanya hanya menjadi masalah jika Anda membuat kode dalam assembler.

Jika CPU cukup pintar, ketergantungan XOR Anda akan hilang karena ia mengetahui nilainya tidak relevan dan akan tetap menyetelnya ke nol (sekali lagi ini tergantung pada CPU aktual yang digunakan).

Namun, saya sudah lama tidak memedulikan beberapa byte atau beberapa siklus clock dalam kode saya - ini sepertinya optimasi mikro menjadi gila.

person paxdiablo    schedule 16.07.2009
comment
Terlepas dari apakah optimasi tersebut berlebihan untuk penggunaan praktis, mungkin ada baiknya memahami bahwa tidak semua instruksi serupa diciptakan sama. ;) - person jerryjvl; 16.07.2009
comment
@jerryjvl - Penting juga untuk menyadari bahwa CPU desktop x86 modern tidak menjalankan kode mesin x86 - mereka mendekode x86 menjadi RISC seperti instruksi internal yang harus dijalankan. Dengan demikian, mereka dapat mengenali urutan kode umum (seperti xor eax, eax) dan menerjemahkannya ke dalam instruksi yang lebih sederhana, seperti mungkin instruksi reg yang jelas. Xor sebenarnya mungkin tidak dilakukan dalam kasus ini. - person Michael; 16.07.2009
comment
optimasi mikro mungkin perlu menjadi gila ketika Anda sedang menulis MBR =). - person brianmearns; 25.03.2013
comment
@ sh1ftst0rm : hanya orang-orang tidak pintar yang melakukan hal seperti itu saat ini. - person Daniel Kamil Kozar; 06.05.2014

Saya pikir pada arsitektur sebelumnya, instruksi mov eax, 0 biasanya memakan waktu sedikit lebih lama daripada xor eax, eax juga... tidak dapat mengingat secara pasti alasannya. Kecuali Anda memiliki lebih banyak movs namun saya membayangkan Anda tidak akan menyebabkan cache hilang karena satu literal disimpan dalam kode.

Perhatikan juga bahwa dari memori, status flag tidak sama di antara metode-metode ini, tetapi saya mungkin salah mengingatnya.

person jerryjvl    schedule 16.07.2009

Apakah Anda sedang menulis kompiler?

Dan pada catatan kedua, pembandingan Anda mungkin tidak akan berhasil, karena Anda memiliki cabang di sana yang mungkin memakan waktu lama. (kecuali kompiler Anda membuka gulungannya untuk Anda)

Alasan lain mengapa Anda tidak dapat melakukan benchmark pada satu instruksi dalam satu loop adalah karena semua kode Anda akan di-cache (tidak seperti kode sebenarnya). Jadi Anda telah menghilangkan banyak perbedaan ukuran antara mov eax,0 dan xor eax,eax dari gambar dengan menyimpannya di cache L1 sepanjang waktu.

Dugaan saya adalah bahwa perbedaan kinerja yang dapat diukur di dunia nyata disebabkan oleh perbedaan ukuran yang menghabiskan cache, dan bukan karena waktu eksekusi kedua opsi.

person Thomas    schedule 16.07.2009
comment
Seluruh situs web ini memiliki kualitas yang peduli terhadap seluruh dunia. Saya rasa itu bukan jawaban yang bagus. - person Roman Starkov; 21.01.2011
comment
Tampaknya Anda dan orang lain berfokus pada apa yang menurut Anda menyinggung. Saya telah menghapus bagian itu karena saya pikir Anda dan orang lain tidak pernah membaca lebih dari itu dan hanya memberi suara negatif. - person Thomas; 09.07.2019
comment
Untuk Sandybridge/Ivybridge, Anda dapat dengan mudah membuat loop yang berjalan pada 1 iterasi per jam dengan nop atau xor same,same, tetapi menghambat throughput unit eksekusi ALU dengan mov reg,0. CPU Intel yang lebih baru memiliki 4 unit eksekusi ALU, jadi contoh nyata eliminasi xor-zeroing yang membuat perbedaan terukur selain ukuran kode jauh lebih sulit untuk dibuat. (xorps memusatkan perhatian pada reg xmm/ymm masih mudah, karena port vektor ALU lebih sedikit dibandingkan lebar front-end). Dan CPU AMD tidak menghilangkan uop back-end, jadi keuntungannya hanyalah ukuran kode. - person Peter Cordes; 10.07.2019
comment
Sebagian besar kode mendapatkan cache L1i hampir sepanjang waktu. Kesalahan cache L1i bisa saja terjadi, namun sebagian besar instruksi yang dieksekusi sepanjang program berasal dari cache L1i, atau bahkan cache uop yang lebih kecil/cepat. Sebagian besar program menghabiskan banyak waktunya dalam loop berukuran kecil hingga sedang. Cache berfungsi. - person Peter Cordes; 10.07.2019
comment
Anda benar bahwa upaya OP untuk melakukan benchmark sepertinya tidak akan berhasil. Tapi mungkin di Sandybridge, jika loop overhead adalah 2 ALU uops tambahan sehingga menghasilkan total 4 uops front-end. Jika salah satunya adalah xor-zeroing yang bisa dihilangkan, backend bisa mengatasinya. - person Peter Cordes; 10.07.2019
comment
Saya setuju dengan semua yang Anda katakan. Saya tidak mengetahui dengan cepat tentang keberadaan AGI stall atau yang setara di CPU Intel modern selain kemacetan ALU, tetapi intinya tetap sama: Anda tidak dapat melakukan benchmark pada instruksi seperti yang dilakukan operasi tersebut. Itu tergantung pada semua kode di sekitarnya, dan cabang hanyalah sebagian saja. Maksud saya metodologinya dan mengapa? tetap sama, dan hanya diperkuat tetapi apa yang Anda tambahkan. - person Thomas; 11.07.2019