Penggandaan dan pembagian dapat dicapai menggunakan operator bit, misalnya
i*2 = i<<1
i*3 = (i<<1) + i;
i*10 = (i<<3) + (i<<1)
dan seterusnya.
Apakah benar-benar lebih cepat menggunakan say (i<<3)+(i<<1)
untuk dikalikan dengan 10 daripada menggunakan i*10
secara langsung? Apakah ada input yang tidak dapat dikalikan atau dibagi dengan cara ini?
Jawaban:
Jawaban singkat: Tidak mungkin.
Jawaban panjang: Kompiler Anda memiliki pengoptimal di dalamnya yang tahu cara melipatgandakan secepat arsitektur prosesor target Anda mampu. Taruhan terbaik Anda adalah memberi tahu kompiler niat Anda dengan jelas (yaitu i * 2 daripada i << 1) dan biarkan ia memutuskan apa urutan kode perakitan / mesin tercepat. Bahkan dimungkinkan bahwa prosesor itu sendiri telah mengimplementasikan instruksi penggandaan sebagai urutan pergeseran & menambahkan dalam kode mikro.
Intinya - jangan menghabiskan banyak waktu untuk mengkhawatirkan hal ini. Jika Anda bermaksud bergeser, bergeserlah. Jika Anda bermaksud melipatgandakan, gandakan. Lakukan apa yang paling jelas secara semantik - rekan kerja Anda akan berterima kasih nanti. Atau, lebih mungkin, mengutuk Anda nanti jika Anda melakukannya sebaliknya.
sumber
gcc -O3
pada x86 denganreturn i*10
dari dari versi shift . Sebagai seseorang yang sering melihat keluaran kompiler (lihat banyak jawaban asm / optimisasi saya), saya tidak terkejut. Ada saat-saat itu dapat membantu untuk memegang kompiler dengan satu cara dalam melakukan sesuatu , tetapi ini bukan salah satunya. gcc pandai integer matematika, karena itu penting.millis() >> 2
; Apakah terlalu banyak meminta hanya membagi?i / 32
vsi >> 5
dani / 4
vsi >> 2
pada gcc untuk cortex-a9 (yang tidak memiliki divisi perangkat keras) dengan optimasi -O3 dan perakitan yang dihasilkan persis sama. Saya tidak suka menggunakan divisi dulu tapi itu menggambarkan niat saya dan hasilnya sama.Hanya titik konkret: bertahun-tahun yang lalu, saya membandingkan dua versi algoritma hashing saya:
dan
Pada setiap mesin yang saya gunakan untuk benchmark, yang pertama setidaknya secepat yang kedua. Agak mengherankan, kadang-kadang lebih cepat (misalnya pada Sun Sparc). Ketika perangkat keras tidak mendukung perkalian cepat (dan sebagian besar tidak saat itu), kompiler akan mengubah perkalian menjadi kombinasi shift dan add / sub yang sesuai. Dan karena ia tahu tujuan akhir, kadang-kadang bisa melakukannya dengan instruksi yang lebih sedikit daripada ketika Anda secara eksplisit menulis shift dan add / subs.
Perhatikan bahwa ini kira-kira 15 tahun yang lalu. Mudah-mudahan, kompiler hanya menjadi lebih baik sejak itu, sehingga Anda dapat mengandalkan compiler untuk melakukan hal yang benar, mungkin lebih baik daripada yang Anda bisa. (Juga, alasan kodenya terlihat begitu adalah karena lebih dari 15 tahun yang lalu. Saya jelas-jelas menggunakan
std::string
dan iterator hari ini.)sumber
Selain semua jawaban baik lainnya di sini, izinkan saya menunjukkan alasan lain untuk tidak menggunakan shift ketika Anda bermaksud membagi atau mengalikan. Saya belum pernah melihat seseorang memperkenalkan bug dengan melupakan prioritas relatif multiplikasi dan penambahan. Saya telah melihat bug yang diperkenalkan ketika programmer pemeliharaan lupa bahwa "mengalikan" melalui shift adalah perkalian secara logis tetapi tidak secara sintaksis dengan prioritas yang sama dengan perkalian.
x * 2 + z
danx << 1 + z
sangat berbeda!Jika Anda bekerja pada angka maka gunakan operator aritmatika seperti
+ - * / %
. Jika Anda bekerja pada array bit, gunakan operator yang suka memutar-mutar bit& ^ | >>
. Jangan mencampurnya; ekspresi yang memiliki sedikit twiddling dan aritmatika adalah bug yang menunggu untuk terjadi.sumber
Ini tergantung pada prosesor dan kompiler. Beberapa kompiler sudah mengoptimalkan kode dengan cara ini, yang lain tidak. Jadi, Anda perlu memeriksa setiap kali kode Anda perlu dioptimalkan dengan cara ini.
Kecuali Anda sangat perlu mengoptimalkan, saya tidak akan mengacak kode sumber saya hanya untuk menyimpan instruksi perakitan atau siklus prosesor.
sumber
>>
operator lebih cepat daripada/
dan, jika nilai yang ditandatangani dapat negatif, seringkali semantik juga superior. Jika seseorang membutuhkan nilai yangx>>4
akan menghasilkan, itu jauh lebih jelas daripadax < 0 ? -((-1-x)/16)-1 : x/16;
, dan saya tidak bisa membayangkan bagaimana kompiler dapat mengoptimalkan ekspresi yang terakhir itu untuk sesuatu yang bagus.Mungkin atau mungkin tidak ada di mesin Anda - jika Anda peduli, ukurlah dalam penggunaan dunia nyata Anda.
Sebuah studi kasus - dari 486 ke inti i7
Benchmarking sangat sulit dilakukan secara bermakna, tetapi kita dapat melihat beberapa fakta. Dari http://www.penguin.cz/~literakl/intel/s.html#SAL dan http://www.penguin.cz/~literakl/intel/i.html#IMUL kita mendapatkan gagasan tentang siklus clock x86 dibutuhkan untuk perubahan aritmatika dan perkalian. Katakanlah kita berpegang pada "486" (yang terbaru terdaftar), register 32 bit dan segera, IMUL mengambil 13-42 siklus dan IDIV 44. Setiap SAL mengambil 2, dan menambahkan 1, sehingga bahkan dengan beberapa dari mereka yang bersama-sama bergeser tampak dangkal seperti seorang pemenang.
Hari-hari ini, dengan i7 inti:
(dari http://software.intel.com/en-us/forums/showthread.php?t=61481 )
(dari beberapa uraian Intel)
Itu memberi Anda gambaran tentang seberapa jauh hal-hal telah terjadi. Trivia optimisasi - seperti bit shifting versus
*
- yang telah dianggap serius bahkan sampai tahun 90an sudah usang sekarang. Bit-shifting masih lebih cepat, tetapi untuk non-power-of-two mul / div pada saat Anda melakukan semua shift Anda dan menambahkan hasilnya lebih lambat lagi. Kemudian, lebih banyak instruksi berarti lebih banyak kesalahan cache, lebih banyak potensi masalah dalam pemipaan, lebih banyak menggunakan register sementara dapat berarti lebih banyak menyimpan dan memulihkan konten register dari stack ... dengan cepat menjadi terlalu rumit untuk mengukur semua dampak secara definitif tetapi mereka sebagian besar negatif.fungsionalitas dalam kode sumber vs implementasi
Secara umum, pertanyaan Anda ditandai C dan C ++. Sebagai bahasa generasi ke-3, mereka dirancang khusus untuk menyembunyikan detail set instruksi CPU yang mendasarinya. Untuk memenuhi Standar bahasa mereka, mereka harus mendukung operasi multiplikasi dan perpindahan (dan banyak lainnya) bahkan jika perangkat keras yang mendasarinya tidak . Dalam kasus seperti itu, mereka harus mensintesis hasil yang diperlukan menggunakan banyak instruksi lain. Demikian pula, mereka harus memberikan dukungan perangkat lunak untuk operasi floating point jika CPU tidak memilikinya dan tidak ada FPU. CPU modern semua mendukung
*
dan<<
, jadi ini mungkin tampak tidak masuk akal secara teoritis dan historis, tetapi yang penting adalah bahwa kebebasan untuk memilih implementasi berjalan dua arah: bahkan jika CPU memiliki instruksi yang mengimplementasikan operasi yang diminta dalam kode sumber dalam kasus umum, kompiler bebas untuk pilih sesuatu yang lebih disukai karena lebih baik untuk kasus spesifik yang dihadapi oleh kompiler.Contoh (dengan bahasa majelis hipotetis)
Instruksi seperti eksklusif atau (
xor
) tidak memiliki hubungan dengan kode sumber, tetapi xor-ing apa pun dengan sendirinya membersihkan semua bit, sehingga dapat digunakan untuk mengatur sesuatu menjadi 0. Kode sumber yang menyiratkan alamat memori mungkin tidak memerlukan penggunaan apa pun.Jenis peretasan ini telah digunakan selama komputer ada. Pada hari-hari awal 3GL, untuk mengamankan serapan pengembang, output kompiler harus memuaskan pengembang bahasa pengoptimalisasi tangan hardcore yang ada. komunitas bahwa kode yang dihasilkan tidak lebih lambat, lebih banyak kata atau lebih buruk. Compiler dengan cepat mengadopsi banyak optimisasi hebat - mereka menjadi toko yang lebih tersentralisasi daripada yang bisa dilakukan oleh programmer bahasa assembly mana pun, meskipun selalu ada kemungkinan mereka kehilangan optimasi tertentu yang penting dalam kasus tertentu - manusia kadang-kadang dapat buang dan grope untuk sesuatu yang lebih baik sementara kompiler hanya melakukan apa yang diperintahkan sampai seseorang memberi makan pengalaman itu kembali ke mereka.
Jadi, bahkan jika pengalihan dan penambahan masih lebih cepat pada beberapa perangkat keras tertentu, maka penulis kompiler kemungkinan telah bekerja tepat ketika itu aman dan menguntungkan.
Maintabilitas
Jika perubahan perangkat keras Anda, Anda dapat mengkompilasi ulang dan itu akan melihat CPU target dan membuat pilihan terbaik lain, sedangkan Anda tidak akan pernah ingin mengunjungi kembali "optimisasi" Anda atau daftar mana lingkungan kompilasi yang harus menggunakan perkalian dan mana yang harus bergeser. Pikirkan semua "optimisasi" non-kekuatan-dua-bit-bergeser yang ditulis 10+ tahun yang lalu yang memperlambat kode mereka saat berjalan pada prosesor modern ...!
Untungnya, kompiler yang baik seperti GCC biasanya dapat mengganti serangkaian bithift dan aritmatika dengan perkalian langsung ketika optimasi apa pun diaktifkan (mis.
...main(...) { return (argc << 4) + (argc << 2) + argc; }
->imull $21, 8(%ebp), %eax
) sehingga kompilasi ulang dapat membantu bahkan tanpa memperbaiki kode, tetapi itu tidak dijamin.Kode bitshifting aneh yang menerapkan perkalian atau pembagian jauh lebih ekspresif dari apa yang Anda coba capai secara konseptual, sehingga pengembang lain akan bingung dengan hal itu, dan programmer yang bingung lebih mungkin memperkenalkan bug atau menghapus sesuatu yang penting dalam upaya mengembalikan kewarasan yang tampak. Jika Anda hanya melakukan hal-hal yang tidak jelas ketika mereka benar-benar bermanfaat, dan kemudian mendokumentasikannya dengan baik (tapi jangan mendokumentasikan hal-hal lain yang intuitif), semua orang akan lebih bahagia.
Solusi umum versus solusi parsial
Jika Anda memiliki pengetahuan tambahan, seperti bahwa Anda
int
benar-benar hanya akan menyimpan nilaix
,y
danz
, maka Anda mungkin dapat mengerjakan beberapa instruksi yang bekerja untuk nilai-nilai itu dan memberi Anda hasil Anda lebih cepat daripada ketika kompiler tidak memiliki wawasan itu dan membutuhkan implementasi yang bekerja untuk semuaint
nilai. Misalnya, pertimbangkan pertanyaan Anda:Anda menggambarkan perkalian, tetapi bagaimana dengan pembagian?
Menurut C ++ Standard 5.8:
Jadi, bit shift Anda memiliki hasil yang ditentukan ketika hasilnya
x
negatif: itu mungkin tidak bekerja dengan cara yang sama pada mesin yang berbeda. Tapi,/
kerjanya jauh lebih mudah ditebak. (Ini mungkin juga tidak sepenuhnya konsisten, karena mesin yang berbeda mungkin memiliki representasi berbeda dari bilangan negatif, dan karenanya rentang yang berbeda bahkan ketika ada jumlah bit yang sama yang membentuk representasi.)Anda mungkin berkata, "Saya tidak peduli ... yang
int
menyimpan usia karyawan, itu tidak mungkin negatif". Jika Anda memiliki wawasan khusus semacam itu, maka ya ->>
optimisasi aman Anda mungkin dilewati oleh kompiler kecuali Anda melakukannya secara eksplisit dalam kode Anda. Tapi, itu berisiko dan jarang berguna karena Anda tidak akan memiliki wawasan seperti ini, dan programmer lain yang bekerja dengan kode yang sama tidak akan tahu bahwa Anda telah bertaruh dengan harapan yang tidak biasa dari data yang Anda miliki. akan menangani ... apa yang tampaknya benar-benar perubahan yang aman bagi mereka mungkin menjadi bumerang karena "optimasi" Anda.Ya ... seperti yang disebutkan di atas, angka negatif memiliki implementasi perilaku yang ditentukan ketika "dibagi" dengan sedikit-bergeser.
sumber
intVal>>1
akan memiliki semantik yang sama yang berbeda dari merekaintVal/2
dengan cara yang kadang berguna. Jika seseorang perlu menghitung dengan cara portabel nilai yang akan dihasilkan oleh arsitektur biasaintVal >> 1
, ekspresi itu harus agak lebih rumit dan lebih sulit untuk dibaca, dan kemungkinan akan menghasilkan kode yang jauh lebih rendah daripada yang diproduksi untukintVal >> 1
.Baru saja mencoba di komputer saya mengkompilasi ini:
Ketika membongkar itu menghasilkan output:
Versi ini lebih cepat daripada kode yang dioptimalkan dengan tangan dengan perubahan dan penambahan murni.
Anda benar-benar tidak pernah tahu apa yang akan dihasilkan oleh kompiler, jadi lebih baik hanya menulis perkalian normal dan biarkan dia mengoptimalkan cara yang diinginkannya, kecuali dalam kasus yang sangat tepat di mana Anda tahu kompiler tidak dapat mengoptimalkan.
sumber
vector<T>::size()
. Kompiler saya cukup kuno! :)Pergeseran pada umumnya jauh lebih cepat daripada mengalikan pada tingkat instruksi tetapi Anda mungkin membuang-buang waktu melakukan optimasi prematur. Kompiler dapat melakukan optimasi ini pada saat kompilasi. Melakukannya sendiri akan memengaruhi keterbacaan dan mungkin tidak berpengaruh pada kinerja. Mungkin hanya layak untuk melakukan hal-hal seperti ini jika Anda telah membuat profil dan menemukan ini sebagai hambatan.
Sebenarnya trik pembagian, yang dikenal sebagai 'divisi sihir' sebenarnya dapat menghasilkan hadiah besar. Sekali lagi Anda harus profil dulu untuk melihat apakah itu diperlukan. Tetapi jika Anda menggunakannya ada program yang berguna di sekitar untuk membantu Anda mengetahui instruksi apa yang diperlukan untuk semantik divisi yang sama. Berikut ini sebuah contoh: http://www.masm32.com/board/index.php?topic=12421.0
Contoh yang saya angkat dari utas OP di MASM32:
Akan menghasilkan:
sumber
Instruksi shift dan integer multiply memiliki kinerja yang serupa pada kebanyakan CPU modern - instruksi integer integer relatif lambat pada tahun 1980-an tetapi secara umum ini tidak lagi benar. Instruksi pengganda bilangan bulat mungkin memiliki latensi yang lebih tinggi , sehingga mungkin masih ada kasus di mana pergeseran lebih disukai. Ditto untuk kasus-kasus di mana Anda dapat membuat unit eksekusi lebih sibuk (meskipun ini dapat memotong dua arah)
Divisi integer masih relatif lambat, jadi menggunakan shift alih-alih pembagian dengan kekuatan 2 masih menang, dan sebagian besar kompiler akan menerapkan ini sebagai optimisasi. Namun perhatikan bahwa agar pengoptimalan ini valid, dividen perlu tidak ditandatangani atau harus diketahui positif. Untuk dividen negatif, shift dan pembagiannya tidak setara!
Keluaran:
Jadi jika Anda ingin membantu kompiler maka pastikan variabel atau ekspresi dalam dividen secara eksplisit tidak ditandatangani.
sumber
Ini sepenuhnya tergantung pada perangkat target, bahasa, tujuan, dll.
Pixel berderak dalam driver kartu video? Sangat mungkin, ya!
Aplikasi bisnis .NET untuk departemen Anda? Sama sekali tidak ada alasan untuk melihatnya.
Untuk game berkinerja tinggi untuk perangkat seluler, mungkin perlu dilakukan pengamatan, tetapi hanya setelah optimasi yang lebih mudah dilakukan.
sumber
Jangan lakukan kecuali Anda benar-benar perlu dan maksud kode Anda memerlukan pengalihan daripada perkalian / pembagian.
Dalam hari-hari biasa - Anda berpotensi dapat menghemat beberapa siklus mesin (atau longgar, karena kompiler lebih tahu apa yang harus dioptimalkan), tetapi biayanya tidak sepadan - Anda menghabiskan waktu pada detail kecil daripada pekerjaan yang sebenarnya, mempertahankan kode menjadi lebih sulit dan rekan kerja Anda akan mengutuk Anda.
Anda mungkin perlu melakukannya untuk perhitungan beban tinggi, di mana setiap siklus yang disimpan berarti menit runtime. Tetapi, Anda harus mengoptimalkan satu tempat pada satu waktu dan melakukan tes kinerja setiap kali untuk melihat apakah Anda benar-benar membuatnya lebih cepat atau mematahkan logika kompiler.
sumber
Sejauh yang saya tahu di beberapa mesin, perkalian bisa membutuhkan hingga 16 hingga 32 siklus mesin. Jadi Ya , tergantung pada jenis alat berat, operator bitshift lebih cepat daripada perkalian / pembagian.
Namun mesin tertentu memang memiliki prosesor matematika mereka, yang berisi instruksi khusus untuk perkalian / pembagian.
sumber
Saya setuju dengan jawaban yang ditandai oleh Drew Hall. Jawabannya bisa menggunakan beberapa catatan tambahan.
Untuk sebagian besar pengembang perangkat lunak, prosesor dan kompiler tidak lagi relevan dengan pertanyaan. Sebagian besar dari kita jauh melampaui 8.088 dan MS-DOS. Mungkin hanya relevan bagi mereka yang masih mengembangkan untuk prosesor tertanam ...
Di perusahaan perangkat lunak saya, Matematika (tambahkan / sub / mul / div) harus digunakan untuk semua matematika. Sedangkan Shift harus digunakan ketika mengkonversi antara tipe data misalnya. ushort ke byte sebagai n >> 8 dan bukan n / 256.
sumber
Dalam kasus bilangan bulat yang ditandatangani dan shift kanan vs divisi, itu bisa membuat perbedaan. Untuk bilangan negatif, giliran putaran putaran ke arah infinity negatif sedangkan putaran pembagian ke nol. Tentu saja kompiler akan mengubah pembagian menjadi sesuatu yang lebih murah, tetapi biasanya akan mengubahnya menjadi sesuatu yang memiliki perilaku pembulatan yang sama dengan pembagian, karena ia tidak dapat membuktikan bahwa variabel tidak akan negatif atau hanya tidak peduli. Jadi, jika Anda dapat membuktikan bahwa angka tidak akan negatif atau jika Anda tidak peduli ke arah mana angka itu akan membulatkannya, Anda dapat melakukan optimasi dengan cara yang lebih mungkin untuk membuat perbedaan.
sumber
unsigned
Tes Python melakukan perkalian yang sama 100 juta kali terhadap angka acak yang sama.
Jadi dalam melakukan pergeseran daripada perkalian / pembagian dengan kekuatan dua di python, ada sedikit peningkatan (~ 10% untuk divisi; ~ 1% untuk perkalian). Jika ini adalah non-kekuatan dua, ada kemungkinan perlambatan yang cukup besar.
Sekali lagi #s ini akan berubah tergantung pada prosesor Anda, kompiler Anda (atau penerjemah - lakukan dengan python untuk kesederhanaan).
Seperti orang lain, jangan optimalkan secara prematur. Tulis kode yang sangat mudah dibaca, profil jika tidak cukup cepat, dan kemudian coba optimalkan bagian yang lambat. Ingat, kompiler Anda jauh lebih baik dalam optimasi daripada Anda.
sumber
Ada optimisasi yang tidak dapat dilakukan oleh kompiler karena mereka hanya bekerja untuk set input yang berkurang.
Di bawah ini ada kode sampel c ++ yang dapat melakukan pembagian yang lebih cepat dengan melakukan 64bits "Penggandaan oleh timbal balik". Baik pembilang dan penyebut harus di bawah ambang tertentu. Perhatikan bahwa itu harus dikompilasi untuk menggunakan instruksi 64 bit agar benar-benar lebih cepat daripada pembagian normal.
sumber
Saya pikir dalam satu kasus bahwa Anda ingin mengalikan atau membagi dengan kekuatan dua, Anda tidak bisa salah dengan menggunakan operator bitshift, bahkan jika kompiler mengubahnya menjadi MUL / DIV, karena beberapa prosesor mikrokode (sungguh, sebuah makro) mereka tetap, jadi untuk kasus-kasus Anda akan mencapai peningkatan, terutama jika pergeseran lebih dari 1. Atau lebih eksplisit, jika CPU tidak memiliki operator bitshift, itu akan tetap menjadi MUL / DIV, tetapi jika CPU memiliki operator bitshift, Anda menghindari cabang mikrokode dan ini sedikit instruksi.
Saya menulis beberapa kode sekarang yang membutuhkan banyak operasi penggandaan / separuh karena bekerja pada pohon biner padat, dan ada satu operasi lagi yang saya curigai mungkin lebih optimal daripada penambahan - kiri (kekuatan dua kali lipat ) bergeser dengan tambahan. Ini dapat diganti dengan shift kiri dan xor jika shift lebih lebar dari jumlah bit yang ingin Anda tambahkan, contoh mudahnya adalah (i << 1) ^ 1, yang menambahkan satu ke nilai dua kali lipat. Ini tentu saja tidak berlaku untuk pergeseran kanan (kekuatan dua membagi) karena hanya pergeseran (endian kecil) kiri mengisi kesenjangan dengan nol.
Dalam kode saya, ini mengalikan / membagi dengan dua dan kekuatan dua operasi sangat intensif digunakan dan karena formula sudah cukup singkat, setiap instruksi yang dapat dihilangkan dapat menjadi keuntungan yang substansial. Jika prosesor tidak mendukung operator bitshift ini, tidak ada keuntungan yang akan terjadi tetapi juga tidak akan ada kerugian.
Juga, dalam algoritma yang saya tulis, mereka secara visual mewakili gerakan yang terjadi sehingga dalam arti mereka sebenarnya lebih jelas. Sisi kiri pohon biner lebih besar, dan kanan lebih kecil. Selain itu, dalam kode saya, angka ganjil dan genap memiliki arti khusus, dan semua anak kiri di pohon itu ganjil dan semua anak kanan, dan akarnya, genap. Dalam beberapa kasus, yang belum saya temui, tetapi mungkin, oh, sebenarnya, saya bahkan tidak memikirkan hal ini, x & 1 mungkin merupakan operasi yang lebih optimal dibandingkan dengan x% 2. x & 1 pada bilangan genap akan menghasilkan nol, tetapi akan menghasilkan 1 untuk bilangan ganjil.
Melangkah lebih jauh dari sekadar identifikasi ganjil / genap, jika saya mendapatkan nol untuk x & 3, saya tahu bahwa 4 adalah faktor nomor kami, dan sama untuk x% 7 untuk 8, dan seterusnya. Saya tahu bahwa kasus-kasus ini mungkin memiliki utilitas terbatas tetapi senang mengetahui bahwa Anda dapat menghindari operasi modulus dan menggunakan operasi logika bitwise sebagai gantinya, karena operasi bitwise hampir selalu yang tercepat, dan paling tidak mungkin ambigu dengan kompiler.
Saya cukup banyak menciptakan bidang pohon biner padat jadi saya berharap bahwa orang tidak dapat memahami nilai komentar ini, karena sangat jarang orang ingin hanya melakukan factorisations hanya pada kekuatan dua, atau hanya memperbanyak / membagi kekuatan dua.
sumber
Apakah itu sebenarnya lebih cepat tergantung pada perangkat keras dan kompiler yang sebenarnya digunakan.
sumber
Jika Anda membandingkan output untuk x + x, x * 2 dan x << 1 sintaks pada kompiler gcc, maka Anda akan mendapatkan hasil yang sama dalam perakitan x86: https://godbolt.org/z/JLpp0j
Jadi, Anda dapat menganggap gcc sebagai cukup pintar untuk menentukan solusi terbaiknya sendiri terlepas dari apa yang Anda ketikkan.
sumber
Saya juga ingin melihat apakah saya bisa mengalahkan House. ini adalah bitwise yang lebih umum untuk sembarang angka dengan sembarang nomor perkalian. macro yang saya buat sekitar 25% lebih banyak dua kali lebih lambat dari perkalian * normal. seperti yang dikatakan oleh orang lain jika itu dekat dengan kelipatan 2 atau terdiri dari beberapa kelipatan 2 Anda mungkin menang. seperti X * 23 terdiri dari (X << 4) + (X << 2) + (X << 1) + X akan lebih lambat maka X * 65 terdiri dari (X << 6) + X.
sumber