Saya menyadari kinerja hit ketika mencampur int ditandatangani dengan pelampung.
Apakah lebih buruk untuk mencampur int unsigned dengan float?
Apakah ada hit ketika pencampuran ditandatangani / tidak ditandatangani tanpa mengapung?
Apakah ukuran yang berbeda (u32, u16, u8, i32, i16, i8) berpengaruh pada kinerja? Di platform mana?
c++
performance
Luis
sumber
sumber
Jawaban:
Denda besar dari pencampuran int (dalam bentuk apa pun) dan mengapung adalah karena ini ada dalam set register yang berbeda. Untuk berpindah dari satu set register ke set register yang lain, Anda harus menulis nilainya ke memori dan membacanya kembali, yang menimbulkan kios penarik beban .
Terjadi di antara berbagai ukuran atau keikutsertaan int membuat semuanya dalam set register yang sama, sehingga Anda terhindar dari penalti besar. Mungkin ada penalti yang lebih kecil karena perpanjangan tanda tangan, dll. Tetapi ini jauh lebih kecil dari pada load-hit-store.
sumber
Saya menduga bahwa informasi tentang Xbox 360 dan PS3 secara khusus akan berada di belakang tembok yang berlisensi-pengembang, seperti kebanyakan detail tingkat rendah. Namun, kita dapat membuat program x86 yang setara dan membongkarnya untuk mendapatkan ide umum.
Pertama, mari kita lihat berapa biaya pelebaran yang tidak ditandatangani:
Bagian yang relevan dibongkar menjadi (menggunakan GCC 4.4.5):
Jadi pada dasarnya sama - dalam satu kasus kita memindahkan byte, yang lain kita memindahkan kata. Berikutnya:
Berubah menjadi:
Jadi biaya perpanjangan tanda adalah berapa pun biaya
movsbl
daripadamovzbl
tingkat sub-instruksi. Itu pada dasarnya mustahil untuk diukur pada prosesor modern karena cara kerja prosesor modern. Segala sesuatu yang lain, mulai dari kecepatan memori untuk caching ke apa yang ada di dalam pipa sebelumnya, akan mendominasi runtime.Dalam ~ 10 menit saya menulis tes ini, saya bisa dengan mudah menemukan bug kinerja nyata, dan segera setelah saya mengaktifkan tingkat optimasi kompiler, kode menjadi tidak dapat dikenali untuk tugas-tugas mudah seperti itu.
Ini bukan Stack Overflow, jadi saya harap tidak ada orang di sini yang akan mengklaim optimasi mikro tidak masalah. Game sering kali bekerja pada data yang sangat besar dan sangat numerik, sehingga perhatian yang cermat terhadap percabangan, gips, penjadwalan, perataan struktur, dan sebagainya dapat memberikan peningkatan yang sangat kritis. Siapa pun yang telah menghabiskan banyak waktu untuk mengoptimalkan kode PPC mungkin memiliki setidaknya satu cerita horor tentang load-hit-store. Tetapi dalam kasus ini, itu benar-benar tidak masalah. Ukuran penyimpanan tipe integer Anda tidak memengaruhi kinerja, asalkan itu sejajar dan pas dalam register.
sumber
Operasi integer yang ditandatangani bisa lebih mahal di hampir semua arsitektur. Misalnya, pembagian dengan konstanta lebih cepat ketika tidak ditandatangani, misalnya:
akan dioptimalkan untuk:
Tapi...
akan mengoptimalkan untuk:
atau pada sistem di mana percabangan murah,
Sama berlaku untuk modulo. Ini juga berlaku untuk non-kekuatan-of-2 (tetapi contohnya lebih kompleks). Jika arsitektur Anda tidak memiliki pembagian perangkat keras (misalnya sebagian besar ARM), pembagian non-konstanta yang tidak ditandatangani juga lebih cepat.
Secara umum, memberi tahu kompiler bahwa angka negatif tidak dapat dihasilkan akan membantu optimalisasi ekspresi, terutama yang digunakan untuk terminasi loop dan kondisi lainnya.
Adapun int ukuran yang berbeda, ya ada sedikit dampak tetapi Anda harus menimbang vs memindahkan sedikit memori. Saat ini Anda mungkin mendapatkan lebih banyak dengan mengakses lebih sedikit memori daripada kehilangan dari ekspansi ukuran. Anda sangat jauh ke optimasi mikro pada saat itu.
sumber
Operasi dengan int yang ditandatangani atau tidak ditandatangani memiliki biaya yang sama pada prosesor saat ini (x86_64, x86, powerpc, arm). Pada prosesor 32 bit, u32, u16, u8 s32, s16, s8 harus sama. Anda dapat memiliki penalti dengan penyelarasan yang buruk.
Tetapi mengkonversi int ke float atau float ke int adalah operasi yang mahal. Anda dapat dengan mudah menemukan implementasi yang dioptimalkan (SSE2, Neon ...).
Poin paling penting mungkin adalah akses memori. Jika data Anda tidak sesuai dengan cache L1 / L2, Anda akan kehilangan lebih banyak siklus daripada konversi.
sumber
Jon Purdy mengatakan di atas (saya tidak bisa berkomentar) bahwa unsigned mungkin lebih lambat karena tidak bisa meluap. Saya tidak setuju, aritmatika unsigned adalah modulo 2 moular sederhana untuk jumlah bit dalam kata. Operasi yang ditandatangani pada prinsipnya dapat mengalami luapan, tetapi biasanya dimatikan.
Terkadang Anda dapat melakukan hal-hal yang pintar (tetapi tidak terlalu mudah dibaca) seperti mengemas dua atau lebih item data ke dalam sebuah int, dan mendapatkan beberapa operasi per instruksi (aritmatika saku). Tetapi Anda harus mengerti apa yang Anda lakukan. Tentu saja MMX memungkinkan Anda untuk melakukan ini secara alami. Tetapi kadang-kadang menggunakan ukuran kata yang didukung HW terbesar dan pengemasan data secara manual memberi Anda implementasi tercepat.
Hati-hati dengan penyelarasan data. Pada sebagian besar implementasi HW, beban yang tidak selaras dan toko lebih lambat. Natural alignment, artinya untuk mengucapkan kata 4byte, alamatnya adalah kelipatan empat, dan alamat kata delapan byte harus kelipatan delapan byte. Ini dibawa ke SSE (128bit mendukung penyelarasan 16byte). AVX akan segera memperpanjang ukuran register "vektor" ini menjadi 256bit kemudian 512bit. Dan load / store yang selaras akan lebih cepat daripada yang tidak selaras. Untuk Geek HW, operasi memori yang tidak selaras mungkin merentang hal-hal seperti cacheline dan bahkan batas halaman, yang harus diperhatikan oleh HW.
sumber
Adalah sedikit lebih baik untuk menggunakan bilangan bulat yang ditandatangani untuk indeks loop, karena limpahan yang ditandatangani tidak didefinisikan dalam C, sehingga kompiler akan menganggap bahwa loop tersebut memiliki kasus sudut yang lebih sedikit. Ini dikendalikan oleh "-fstrict-overflow" gcc (diaktifkan secara default) dan efeknya mungkin sulit untuk diketahui tanpa membaca output rakitan.
Selain itu, x86 berfungsi lebih baik jika Anda tidak mencampur tipe, karena dapat menggunakan operan memori. Jika harus mengonversi jenis (tanda atau nol ekstensi) itu berarti memuat eksplisit dan penggunaan register.
Tetap dengan int untuk variabel lokal dan sebagian besar ini akan terjadi secara default.
sumber
Seperti yang ditunjukkan oleh celion, overhead dari konversi antara int dan float sebagian besar berkaitan dengan penyalinan dan konversi nilai antara register. Satu-satunya overhead int unsigned di dalam dan dari diri mereka berasal dari perilaku sampul dijamin mereka, yang mengharuskan sejumlah pengecekan overflow dalam kode yang dikompilasi.
Pada dasarnya tidak ada overhead dalam mengkonversi antara bilangan bulat yang ditandatangani dan tidak ditandatangani. Ukuran integer yang berbeda mungkin (sangat kecil) lebih cepat atau lebih lambat untuk diakses tergantung pada platform. Secara umum, ukuran bilangan bulat yang paling dekat dengan ukuran kata platform akan menjadi yang tercepat untuk diakses, tetapi perbedaan kinerja keseluruhan tergantung pada banyak faktor lain, terutama ukuran cache: jika Anda menggunakan
uint64_t
semua yang Anda butuhkanuint32_t
, itu mungkin pastikan bahwa kurang dari data Anda akan muat dalam cache sekaligus, dan Anda mungkin dikenakan beberapa beban overhead.Agak terlalu berlebihan untuk memikirkan hal ini. Jika Anda menggunakan tipe yang sesuai untuk data Anda, hal-hal yang seharusnya bekerja dengan baik, dan jumlah daya yang akan diperoleh dengan memilih tipe berdasarkan arsitektur dapat diabaikan.
sumber