Inilah pertanyaan konyol yang menyenangkan:
Katakanlah kita harus melakukan operasi sederhana di mana kita membutuhkan setengah dari nilai variabel. Ada biasanya dua cara untuk melakukan hal ini:
y = x / 2.0;
// or...
y = x * 0.5;
Dengan asumsi kami menggunakan operator standar yang disediakan dengan bahasa, mana yang memiliki kinerja lebih baik?
Saya menebak perkalian biasanya lebih baik jadi saya mencoba untuk tetap berpegang pada itu ketika saya membuat kode, tapi saya ingin mengkonfirmasi ini.
Meskipun secara pribadi saya tertarik dengan jawaban untuk Python 2.4-2.5, jangan ragu untuk mengirimkan jawaban untuk bahasa lain! Dan jika Anda mau, jangan ragu untuk memposting cara lain yang lebih bagus (seperti menggunakan operator shift bitwise) juga.
performance
programming-languages
Edmundito
sumber
sumber
Jawaban:
Python:
perkalian 33% lebih cepat
Lua:
=> tidak ada perbedaan nyata
LuaJIT:
=> hanya 5% lebih cepat
kesimpulan: dengan Python, lebih cepat menggandakan daripada membagi, tetapi saat Anda mendekati CPU menggunakan VM atau JIT yang lebih canggih, keuntungannya menghilang. Sangat mungkin bahwa VM Python di masa depan akan membuatnya tidak relevan
sumber
Selalu gunakan apa pun yang paling jelas. Hal lain yang Anda lakukan adalah mencoba mengakali kompilator. Jika kompilernya cerdas, ia akan melakukan yang terbaik untuk mengoptimalkan hasil, tetapi tidak ada yang bisa membuat orang berikutnya tidak membenci Anda atas solusi pengalihan bit Anda yang buruk (omong-omong, saya suka manipulasi bit, itu menyenangkan. Tapi menyenangkan! = Dapat dibaca )
Optimasi prematur adalah akar dari segala kejahatan. Ingatlah selalu tiga aturan pengoptimalan!
Jika Anda adalah seorang ahli dan dapat membenarkan kebutuhan tersebut, gunakan prosedur berikut:
Selain itu, melakukan hal-hal seperti menghapus loop dalam saat tidak diperlukan atau memilih daftar tertaut di atas array untuk jenis penyisipan bukanlah pengoptimalan, hanya pemrograman.
sumber
Saya pikir ini menjadi sangat rewel sehingga Anda akan lebih baik melakukan apa pun yang membuat kode lebih mudah dibaca. Kecuali jika Anda melakukan operasi ribuan, jika tidak jutaan, kali, saya ragu ada orang yang akan melihat perbedaannya.
Jika Anda benar-benar harus membuat pilihan, pembandingan adalah satu-satunya cara. Temukan fungsi apa yang memberi Anda masalah, kemudian cari tahu di fungsi mana masalah tersebut terjadi, dan perbaiki bagian tersebut. Namun, saya masih ragu bahwa satu operasi matematika (bahkan yang diulang berkali-kali) akan menjadi penyebab kemacetan.
sumber
Perkalian lebih cepat, pembagian lebih akurat. Anda akan kehilangan beberapa presisi jika angka Anda bukan pangkat 2:
Bahkan jika Anda membiarkan penyusun menghitung konstanta terbalik hingga presisi sempurna, jawabannya masih bisa berbeda.
Masalah kecepatan hanya mungkin menjadi masalah dalam bahasa C / C ++ atau JIT, dan bahkan hanya jika operasi berada dalam loop yang mengalami hambatan.
sumber
y = x * (1.0/3.0);
dan kompiler biasanya akan menghitung 1/3 pada waktu kompilasi. Ya, 1/3 tidak dapat direpresentasikan secara sempurna di IEEE-754, tetapi ketika Anda melakukan aritmatika floating-point, Anda tetap kehilangan presisi , baik saat Anda melakukan perkalian atau pembagian, karena bit orde rendah dibulatkan. Jika Anda tahu bahwa komputasi Anda begitu sensitif terhadap kesalahan pembulatan, Anda juga harus tahu cara terbaik menangani masalah ini.(1.0/3.0)
dengan membagi dengan3.0
. Saya mendapatkan hingga 1,0000036666774155, dan dalam ruang itu 7,3% hasilnya berbeda. Saya menganggap mereka hanya berbeda 1 bit, tetapi karena aritmatika IEEE dijamin membulatkan ke hasil terdekat yang benar, saya mendukung pernyataan saya bahwa pembagian lebih akurat. Apakah perbedaannya signifikan terserah Anda.Jika Anda ingin mengoptimalkan kode Anda tetapi masih jelas, coba ini:
Kompilator harus dapat melakukan pembagian pada waktu kompilasi, sehingga Anda mendapatkan kelipatan pada saat run-time. Saya berharap ketepatannya sama seperti dalam
y = x / 2.0
kasus ini.Di mana hal ini mungkin menjadi masalah, LOT ada di prosesor tertanam di mana emulasi floating-point diperlukan untuk menghitung aritmatika floating-point.
sumber
y = x / 2.0;
tetapi di dunia nyata, Anda mungkin harus membujuk kompiler untuk melakukan perkalian yang lebih murah. Mungkin kurang jelas mengapay = x * (1.0 / 2.0);
lebih baik, dany = x * 0.5;
sebaliknya akan lebih jelas untuk dinyatakan . Tapi ubah2.0
menjadi a7.0
dan saya lebih suka melihaty = x * (1.0 / 7.0);
daripaday = x * 0.142857142857;
.Hanya akan menambahkan sesuatu untuk opsi "bahasa lain".
C: Karena ini hanya latihan akademis yang benar - benar tidak ada bedanya, saya pikir saya akan memberikan kontribusi yang berbeda.
Saya menyusun untuk berkumpul tanpa optimasi dan melihat hasilnya.
Kode:
dikompilasi dengan
gcc tdiv.c -O1 -o tdiv.s -S
pembagian dengan 2:
dan perkalian dengan 0,5:
Namun, ketika saya mengubahnya
int
menjadidouble
s (yang mungkin akan dilakukan python), saya mendapatkan ini:divisi:
perkalian:
Saya belum membandingkan kode ini, tetapi hanya dengan memeriksa kode Anda dapat melihat bahwa menggunakan bilangan bulat, pembagian dengan 2 lebih pendek daripada perkalian dengan 2. Menggunakan penggandaan, perkalian lebih pendek karena kompilator menggunakan opcode titik mengambang prosesor, yang mana mungkin berjalan lebih cepat (tapi sebenarnya saya tidak tahu) daripada tidak menggunakannya untuk operasi yang sama. Jadi pada akhirnya jawaban ini telah menunjukkan bahwa kinerja perkalian dengan 0,5 vs. pembagian dengan 2 bergantung pada implementasi bahasa dan platform tempat menjalankannya. Pada akhirnya perbedaan tersebut dapat diabaikan dan merupakan sesuatu yang sebenarnya tidak pernah Anda khawatirkan, kecuali dalam hal keterbacaan.
Sebagai catatan tambahan, Anda dapat melihat bahwa di program saya
main()
kembalia + b
. Ketika saya mengambil kata kunci yang mudah menguap, Anda tidak akan pernah menebak seperti apa rakitan itu (tidak termasuk pengaturan program):itu melakukan baik pembagian, perkalian, DAN penambahan dalam satu instruksi! Jelas Anda tidak perlu khawatir tentang ini jika pengoptimal adalah jenis yang terhormat.
Maaf atas jawaban yang terlalu panjang.
sumber
movl $5, %eax
Nama pengoptimalan tidak penting atau bahkan relevan. Anda hanya ingin merendahkan jawaban berusia empat tahun.Pertama, kecuali Anda bekerja di C atau ASSEMBLY, Anda mungkin menggunakan bahasa tingkat yang lebih tinggi di mana memori macet dan overhead panggilan umum akan benar-benar mengecilkan perbedaan antara mengalikan dan membagi ke titik yang tidak relevan. Jadi, pilih saja yang terbaca lebih baik dalam kasus itu.
Jika Anda berbicara dari tingkat yang sangat tinggi, itu tidak akan lebih lambat untuk apa pun yang kemungkinan besar Anda gunakan. Anda akan melihat di jawaban lain, orang perlu melakukan satu juta perkalian / bagi hanya untuk mengukur beberapa perbedaan sub-milidetik di antara keduanya.
Jika Anda masih penasaran, dari sudut pandang pengoptimalan tingkat rendah:
Divide cenderung memiliki pipa yang jauh lebih panjang daripada multiply. Ini berarti membutuhkan waktu lebih lama untuk mendapatkan hasilnya, tetapi jika Anda dapat membuat prosesor sibuk dengan tugas-tugas yang tidak bergantung, maka itu tidak akan menghabiskan biaya lebih dari kelipatan.
Berapa lama perbedaan pipeline sepenuhnya bergantung pada perangkat keras. Perangkat keras terakhir yang saya gunakan adalah sekitar 9 siklus untuk perkalian FPU dan 50 siklus untuk pembagian FPU. Kedengarannya banyak, tetapi kemudian Anda akan kehilangan 1000 siklus karena kehilangan memori, sehingga dapat menempatkan segala sesuatunya dalam perspektif.
Analoginya adalah memasukkan pai ke dalam microwave saat Anda menonton acara TV. Total waktu yang Anda perlukan dari acara TV adalah berapa lama Anda harus memasukkannya ke dalam microwave, dan mengeluarkannya dari microwave. Sisa waktu Anda masih menonton acara TV. Jadi jika pai membutuhkan waktu 10 menit untuk dimasak, bukan 1 menit, itu tidak benar-benar menghabiskan waktu menonton TV Anda lagi.
Dalam praktiknya, jika Anda ingin mencapai tingkat kepedulian tentang perbedaan antara Multiply dan Divide, Anda perlu memahami pipeline, cache, branch stall, prediksi out-of-order, dan dependensi pipeline. Jika ini tidak terdengar seperti tujuan Anda dengan pertanyaan ini, maka jawaban yang benar adalah mengabaikan perbedaan di antara keduanya.
Bertahun-tahun yang lalu sangatlah penting untuk menghindari pembagian dan selalu menggunakan perkalian, tetapi saat itu hit ingatan kurang relevan, dan pembagian jauh lebih buruk. Hari-hari ini saya menilai keterbacaan lebih tinggi, tetapi jika tidak ada perbedaan keterbacaan, saya pikir itu adalah kebiasaan yang baik untuk memilih penggandaan.
sumber
Tulis mana saja yang lebih jelas menyatakan maksud Anda.
Setelah program Anda berfungsi, cari tahu apa yang lambat, dan buat lebih cepat.
Jangan lakukan sebaliknya.
sumber
Lakukan apapun yang Anda butuhkan. Pikirkan pembaca Anda terlebih dahulu, jangan khawatir tentang kinerja sampai Anda yakin Anda memiliki masalah kinerja.
Biarkan kompilator melakukan kinerja untuk Anda.
sumber
Jika Anda bekerja dengan integer atau tipe non floating point jangan lupa operator bitshifting Anda: << >>
sumber
Sebenarnya ada alasan bagus bahwa sebagai aturan umum perkalian akan lebih cepat daripada pembagian. Pembagian floating point dalam perangkat keras dilakukan dengan algoritma shift dan pengurangan bersyarat ("pembagian panjang" dengan bilangan biner) atau - lebih mungkin hari ini - dengan iterasi seperti algoritma Goldschmidt . Pergeseran dan pengurangan membutuhkan setidaknya satu siklus per bit presisi (iterasi hampir tidak mungkin untuk diparalelkan seperti halnya pergeseran-dan-tambah perkalian), dan algoritma iteratif melakukan setidaknya satu perkalian per iterasi. Dalam kedua kasus tersebut, kemungkinan besar pembagian akan mengambil lebih banyak siklus. Tentu saja ini tidak memperhitungkan kebiasaan dalam kompiler, pergerakan data, atau presisi. Namun, pada umumnya, jika Anda membuat kode loop dalam di bagian program yang sensitif terhadap waktu,
0.5 * x
atau1.0/2.0 * x
bukanx / 2.0
merupakan hal yang wajar untuk dilakukan. Pedantry dari "kode apa yang paling jelas" benar-benar benar, tetapi ketiganya sangat dekat dalam keterbacaan sehingga penyebutan dalam kasus ini hanya sekedar omong kosong.sumber
Saya selalu belajar bahwa perkalian lebih efisien.
sumber
Perkalian biasanya lebih cepat - pasti tidak pernah lebih lambat. Namun, jika bukan kecepatan kritis, tulis mana saja yang paling jelas.
sumber
Pembagian floating-point (umumnya) sangat lambat, jadi meskipun perkalian floating-point juga relatif lambat, mungkin lebih cepat daripada pembagian floating-point.
Tapi saya lebih cenderung menjawab "itu tidak terlalu penting", kecuali pembuatan profil telah menunjukkan bahwa pembagian sedikit hambatan vs. perkalian. Saya menduga, bahwa pilihan perkalian vs. pembagian tidak akan berdampak besar pada kinerja aplikasi Anda.
sumber
Ini menjadi lebih dari sebuah pertanyaan ketika Anda memprogram dalam perakitan atau mungkin C. Saya pikir dengan sebagian besar bahasa modern pengoptimalan seperti ini dilakukan untuk saya.
sumber
Berhati-hatilah dengan "perkalian menebak biasanya lebih baik jadi saya mencoba untuk tetap menggunakannya saat saya membuat kode,"
Dalam konteks pertanyaan khusus ini, lebih baik di sini berarti "lebih cepat". Yang tidak terlalu berguna.
Memikirkan kecepatan bisa menjadi kesalahan serius. Ada implikasi kesalahan yang sangat besar dalam bentuk perhitungan aljabar tertentu.
Lihat aritmatika Floating Point dengan analisis kesalahan . Lihat Masalah Dasar dalam Aritmatika Titik Mengambang dan Analisis Kesalahan .
Meskipun beberapa nilai floating-point tepat, sebagian besar nilai floating-point merupakan perkiraan; mereka adalah beberapa nilai ideal ditambah beberapa kesalahan. Setiap operasi berlaku untuk nilai ideal dan nilai kesalahan.
Masalah terbesar datang dari mencoba memanipulasi dua angka yang hampir sama. Bit paling kanan (bit error) mendominasi hasil.
Dalam contoh ini, Anda dapat melihat bahwa saat nilai semakin kecil, perbedaan antara angka yang hampir sama menghasilkan hasil bukan nol dengan jawaban yang benar adalah nol.
sumber
Saya pernah membaca bahwa perkalian lebih efisien dalam C / C ++; Tidak tahu tentang bahasa yang ditafsirkan - perbedaannya mungkin dapat diabaikan karena semua overhead lainnya.
Kecuali jika itu menjadi masalah, tetap dengan apa yang lebih mudah dipelihara / dibaca - Saya benci ketika orang memberi tahu saya ini tetapi itu sangat benar.
sumber
Saya menyarankan perkalian secara umum, karena Anda tidak perlu menghabiskan siklus untuk memastikan pembagi Anda bukan 0. Ini tidak berlaku, tentu saja, jika pembagi Anda adalah konstanta.
sumber
Android Java, diprofilkan di Samsung GT-S5830
Hasil?
Pembagian sekitar 20% lebih cepat dari perkalian (!)
sumber
a = i*0.5
, bukana *= 0.5
. Begitulah cara kebanyakan programmer menggunakan operasi.Seperti postingan # 24 (perkalian lebih cepat) dan # 30 - tetapi terkadang keduanya sama mudahnya untuk dipahami:
~ Saya menemukan keduanya sama mudahnya untuk dibaca, dan harus mengulanginya miliaran kali. Jadi sangat berguna untuk mengetahui bahwa perkalian biasanya lebih cepat.
sumber
Ada perbedaan, tetapi bergantung pada kompiler. Awalnya pada vs2003 (c ++) saya tidak mendapatkan perbedaan yang signifikan untuk tipe ganda (64 bit floating point). Namun menjalankan tes lagi pada vs2010, saya mendeteksi perbedaan besar, hingga faktor 4 lebih cepat untuk perkalian. Melacak ini, tampaknya vs2003 dan vs2010 menghasilkan kode fpu yang berbeda.
Pada Pentium 4, 2,8 GHz, vs 2003:
Pada Xeon W3530, vs2003:
Pada Xeon W3530, vs2010:
Tampaknya pada vs2003, pembagian dalam satu lingkaran (sehingga pembagi digunakan beberapa kali) diterjemahkan menjadi perkalian dengan invers. Pada vs2010, pengoptimalan ini tidak diterapkan lagi (saya kira karena ada hasil yang sedikit berbeda antara kedua metode). Perhatikan juga bahwa cpu melakukan pembagian lebih cepat segera setelah pembilang Anda 0,0. Saya tidak tahu persis algoritma yang tertanam dalam chip, tapi mungkin itu tergantung pada angka.
Edit 18-03-2013: observasi untuk vs2010
sumber
n/10.0
dengan ekspresi formulir(n * c1 + n * c2)
? Saya berharap bahwa pada kebanyakan prosesor, pembagian akan memakan waktu lebih lama dari dua perkalian dan pembagian, dan saya percaya bahwa pembagian dengan konstanta apa pun dapat menghasilkan hasil yang benar dalam semua kasus menggunakan rumus yang ditunjukkan.Inilah jawaban konyol yang menyenangkan:
x / 2.0 adalah tidak setara dengan x * 0,5
Katakanlah Anda menulis metode ini pada 22 Okt 2008.
Sekarang, 10 tahun kemudian Anda mengetahui bahwa Anda dapat mengoptimalkan potongan kode ini. Metode ini direferensikan dalam ratusan rumus di seluruh aplikasi Anda. Jadi Anda mengubahnya, dan mengalami peningkatan kinerja 5% yang luar biasa.
Apakah keputusan yang tepat untuk mengubah kode? Dalam matematika, kedua ekspresi tersebut memang setara. Dalam ilmu komputer, hal itu tidak selalu benar. Silakan baca Meminimalkan efek masalah akurasi untuk lebih jelasnya. Jika nilai yang Anda hitung - di beberapa titik - dibandingkan dengan nilai lain, Anda akan mengubah hasil kasus tepi. Misalnya:
Intinya adalah; setelah Anda puas dengan salah satu dari keduanya, maka patuhi itu!
sumber
2
dan keduanya0.5
dapat direpresentasikan dalam IEEE 754 dengan tepat, tanpa kehilangan presisi (tidak seperti misalnya0.4
atau0.1
, mereka tidak bisa).Nah, jika kita berasumsi bahwa biaya operasi tambah / sublacak 1, kalikan biaya 5, dan bagi biaya sekitar 20.
sumber
Setelah diskusi yang panjang dan menarik, inilah pendapat saya tentang ini: Tidak ada jawaban akhir untuk pertanyaan ini. Seperti yang ditunjukkan beberapa orang, itu tergantung pada keduanya, perangkat keras (cf piotrk dan gast128 ) dan kompiler (cf tes @Javier ). Jika kecepatan tidak penting, jika aplikasi Anda tidak perlu memproses data dalam jumlah besar secara real-time, Anda dapat memilih kejelasan menggunakan pembagian sedangkan jika kecepatan pemrosesan atau beban prosesor menjadi masalah, perkalian mungkin yang paling aman. Terakhir, kecuali Anda tahu persis pada platform apa aplikasi Anda akan diterapkan, tolok ukur tidak ada artinya. Dan untuk kejelasan kode, satu komentar sudah cukup!
sumber
Secara teknis tidak ada yang namanya pembagian, yang ada hanyalah perkalian dengan elemen invers. Misalnya Anda tidak pernah membagi dengan 2, Anda justru mengalikannya dengan 0,5.
'Pembagian' - mari kita menipu diri sendiri bahwa itu ada sebentar - selalu lebih sulit dari perkalian itu karena untuk 'membagi'
x
dengany
satu terlebih dahulu perlu menghitung nilainyay^{-1}
sedemikian rupay*y^{-1} = 1
dan kemudian melakukan perkalianx*y^{-1}
. Jika sudah tahuy^{-1}
maka tidak menghitungnya dariy
pasti ada optimasi.sumber