Apakah perbandingan 1 <10 lebih murah dari 1 <1000000?

65

Saya hanya menggunakan ~ 1 miliar sebagai hitungan untuk z-indexdalam CSS, dan sedang memikirkan perbandingan yang harus dilakukan. Apakah ada perbedaan kinerja pada tingkat ALU dalam perbandingan antara jumlah yang sangat besar vs yang sangat kecil?

Misalnya, apakah salah satu dari dua cuplikan ini lebih mahal daripada yang lain?

snippet 1

for (int i = 0; i < 10000000; i++){
    if (i < 10000000000000) {
        //do nothing
    }
}

snippet 2

for (int i = 0; i < 10000000; i++){
    if (i < 1000) {
        //do nothing
    }
}
Viziionary
sumber
9
apakah Anda sadar tentang cara kerja prediksi cabang ?
nyamuk
12
OP tidak menanyakan berapa lama waktu yang dibutuhkan percabangan. Jelas, contoh ini dimaksudkan untuk memastikan bahwa dibutuhkan waktu yang persis sama di kedua cuplikan. Pertanyaannya adalah apakah CMPinstruksi mesin individual akan lebih lambat jika ilebih besar.
Kilian Foth
18
Karena ini dilakukan dalam CSS, mengonversi string ke integer kemungkinan akan mendominasi operasi perbandingan itu sendiri dalam hal waktu yang dihabiskan untuk mengeksekusi.
58
Jika Anda perlu menggunakan 1000000000 sebagai indeks-z dalam file CSS, Anda telah melakukan kesalahan.
Bergi
6
Untuk CSS, biaya overhead konversi teks menjadi bilangan bulat akan tergantung pada jumlah digit yang dikonversi (di mana angka 6 digit seperti 1000000 mungkin sekitar 6 kali lebih mahal dari angka 1 digit seperti 1); dan overhead ini mungkin merupakan urutan besarnya lebih besar dari overhead perbandingan integer.
Brendan

Jawaban:

82

Setiap prosesor yang saya kerjakan melakukan perbandingan dengan mengurangi salah satu operan dari yang lain, membuang hasilnya dan meninggalkan bendera prosesor (nol, negatif, dll.) Sendirian. Karena pengurangan dilakukan sebagai operasi tunggal, isi operan tidak penting.

Cara terbaik untuk menjawab pertanyaan dengan pasti adalah mengkompilasi kode Anda ke dalam perakitan dan berkonsultasi dengan dokumentasi prosesor target untuk instruksi yang dihasilkan. Untuk CPU Intel saat ini, itu akan menjadi Panduan Pengembang Perangkat Lunak Arsitektur Intel 64 dan IA-32 .

Deskripsi CMPinstruksi ("bandingkan") ada di volume 2A, halaman 3-126, atau halaman 618 dari PDF, dan menjelaskan operasinya sebagai:

temp ← SRC1 − SignExtend(SRC2);
ModifyStatusFlags; (* Modify status flags in the same manner as the SUB instruction*)

Ini berarti operan kedua diperpanjang jika perlu, dikurangi dari operan pertama dan hasilnya ditempatkan di area sementara dalam prosesor. Kemudian bendera status diatur dengan cara yang sama seperti untuk SUBinstruksi ("kurangi") (halaman 1492 dari PDF).

Tidak ada disebutkan dalam CMPatau SUBdokumentasi bahwa nilai operan memiliki kaitan dengan latensi, sehingga nilai apa pun yang Anda gunakan aman.

Blrfl
sumber
5
Bagaimana jika jumlahnya terlalu besar untuk aritmatika 32-bit? Apakah kemudian tidak akan dipecah menjadi perhitungan yang lebih lambat?
Falco
3
@ Falco Tidak pada CPU dengan 64-bit ALU (yang cukup banyak dari mereka semua kecuali di ruang tertanam hari ini.)
reirab
8
@ Falco: Ya, tetapi karena pertanyaannya tentang kinerja ALU, implikasinya adalah bahwa nilai-nilai tersebut sesuai dengan ukuran kata CPU atau kemampuan instruksi SIMD yang mungkin dimilikinya. Beroperasi pada angka yang lebih besar dari itu harus diimplementasikan dengan banyak instruksi di luar CPU. Itu sangat umum 30 tahun yang lalu ketika Anda baru saja mendaftar 8- atau 16-bit untuk bekerja dengannya.
Blrfl
6
@ Falco Bagaimana itu membutuhkan debugging? Itu bukan bug; itu hanya sedikit lebih lambat untuk melakukan operasi 64-bit pada CPU yang tidak mendukung operasi 64-bit. Menyarankan bahwa seseorang tidak boleh menggunakan angka di atas 2 ^ 31-1 tampaknya agak konyol.
reirab
2
@ Falco Setelah mengatakan itu, apakah mesin rendering di browser bahkan menggunakan integer untuk mewakili indeks-z? Sebagian besar mesin rendering yang saya kenal menggunakan float presisi tunggal untuk semuanya (sampai tahap rasterisasi akhir), tetapi saya belum benar-benar mempelajari mesin rendering browser.
reirab
25

Apakah ada perbedaan kinerja pada tingkat ALU dalam perbandingan antara jumlah yang sangat besar vs yang sangat kecil?

Ini sangat tidak mungkin, kecuali beralih dari angka kecil ke angka besar mengubah tipe numerik Anda, katakanlah dari a intke a long. Meski begitu, perbedaannya mungkin tidak signifikan. Anda lebih mungkin melihat perbedaan jika bahasa pemrograman Anda secara diam-diam beralih ke aritmatika presisi sewenang - wenang di bawah penutup.

Meskipun demikian, kompiler khusus Anda mungkin melakukan beberapa optimasi pintar yang tidak Anda sadari. Cara Anda mengetahuinya adalah dengan mengukur. Jalankan profiler pada kode Anda; lihat perbandingan mana yang paling lama. Atau cukup memulai dan menghentikan timer.

Robert Harvey
sumber
Harus disebutkan, bahwa Bilangan yang diusulkan dalam Pertanyaan memiliki tipe numerik yang berbeda dalam tipe bilangan bulat 32-bit yang khas ...
Falco
19

Banyak prosesor memiliki instruksi "kecil" yang dapat melakukan operasi aritmatika, termasuk perbandingan, pada operan tertentu yang segera ditentukan. Operand selain nilai-nilai khusus tersebut harus menggunakan format instruksi yang lebih besar atau, dalam beberapa kasus, harus menggunakan instruksi "load value from memory". Dalam set instruksi ARM Cortex-M3, misalnya, setidaknya ada lima cara nilai dapat dibandingkan dengan konstanta:

    cmp r0,#1      ; One-word instruction, limited to values 0-255

    cmp r0,#1000   ; Two-word instruction, limited to values 0-255 times a power of 2

    cmn r0,#1000   ; Equivalent to comparing value with -1000
                   ; Two-word instruction, limited to values 0-255 times a power of 2

    mov r1,#30000  ; Two words; can handle any value 0-65535
    cmp r0,r1      ; Could use cmn to compare to values -1 to -65535

    ldr r1,[constant1000000] ; One or two words, based upon how nearby the constant is
    cmp r0,r1
    ...

constant1000000:
    dd  1000000

Bentuk pertama adalah yang terkecil; bentuk kedua dan ketiga mungkin atau tidak mungkin dijalankan dengan cepat, tergantung pada kecepatan memori dari mana kode diambil. Bentuk keempat bentuk hampir pasti akan lebih lambat dari tiga yang pertama, dan bentuk kelima bahkan lebih lambat, tetapi yang terakhir dapat digunakan dengan nilai 32-bit.

Pada prosesor x86 yang lebih lama, instruksi membandingkan formulir pendek akan mengeksekusi lebih cepat daripada yang panjang, tetapi banyak prosesor yang lebih baru akan mengonversi bentuk panjang dan pendek ke representasi yang sama ketika mereka pertama kali diambil, dan menyimpan representasi seragam dalam cache. Jadi, sementara pengontrol yang tertanam (seperti yang ditemukan pada banyak platform seluler) akan memiliki perbedaan kecepatan, banyak komputer berbasis x86 tidak akan melakukannya.

Perhatikan juga bahwa dalam banyak kasus di mana konstanta banyak digunakan dalam satu loop, kompiler hanya perlu memuat konstanta ke register sekali - sebelum loop dimulai - rendering perbedaan waktu diperdebatkan. Di sisi lain, ada beberapa situasi, bahkan dalam loop kecil, di mana itu tidak akan selalu terjadi; jika loop kecil tetapi banyak dieksekusi, kadang-kadang mungkin ada kinerja besar antara perbandingan yang melibatkan nilai langsung pendek dan yang melibatkan yang lebih lama.

supercat
sumber
Pada MIPS Anda hanya dapat memiliki 16-bit dengan segera, jadi pasti perbandingan dengan 1 akan lebih pendek dan (mungkin) lebih cepat dari 1000000. Mungkin sama untuk Sparc dan PowerPC. Dan saya pikir saya telah membaca dari beberapa sumber bahwa Intel juga mengoptimalkan operasi pada segera kecil dalam beberapa kasus tapi saya tidak yakin untuk perbandingan atau tidak
phuclv
@ LưuVĩnhPhúc: Daftar dapat dimuat sebelum loop. Pada titik itu, perbandingan aktual akan menjadi jumlah instruksi yang sama dalam kedua kasus.
cao
Karena Loop hanyalah sebuah contoh oleh op dan pertanyaannya adalah misalnya indeks-z, jika Anda memiliki 1000 objek, masing-masing dengan indeks-z sendiri dan Anda menetapkannya ke 100000000 ... 1000000999 atau ke 10.000 ... 10999 dan Anda mengulanginya untuk disortir sebelum rendering, ada banyak perbandingan dan banyak instruksi pemuatan. Itu bisa membuat perbedaan!
Falco
@ Falco: Dalam hal itu, segera tidak akan faktor; memuat dan membandingkan dengan register sepertinya tidak bisa dihindari.
cao
@ cHao: Jika seseorang membandingkan indeks Z satu sama lain, mereka akan berada dalam register. Jika seseorang menangani rentang indeks tertentu secara berbeda yang mungkin memerlukan perbandingan langsung. Biasanya konstanta akan dimuat sebelum loop dimulai, tetapi jika misalnya seseorang memiliki loop yang diperlukan untuk membaca pasangan nilai dari memori dan membandingkan nilai pertama dari setiap pasangan dengan lima konstanta yang berbeda (spasi tidak seragam) dalam kisaran 100000 ke 100499, dan nilai lainnya dengan lima konstanta lain, mungkin lebih cepat untuk mengurangi 100250 (disimpan dalam register) dan kemudian membandingkannya dengan nilai -250 hingga 250 ...
supercat
5

Jawaban singkat untuk pertanyaan ini adalah, tidak , tidak ada perbedaan waktu untuk membandingkan dua angka berdasarkan besarnya angka-angka itu dengan asumsi mereka disimpan dalam tipe data yang sama (mis. Baik int 32-bit atau keduanya panjang 64-bit.)

Selain itu, hingga ukuran kata ALU , sangat tidak mungkin bahwa membandingkan dua bilangan bulat satu sama lain akan memakan waktu lebih dari 1 siklus clock, karena ini adalah operasi sepele yang setara dengan pengurangan. Saya pikir setiap arsitektur yang pernah saya tangani memiliki perbandingan integer siklus tunggal.

Satu-satunya kasus yang dapat saya pikirkan yang saya temui di mana perbandingan dua angka bukanlah operasi satu siklus adalah sebagai berikut:

  • Instruksi di mana sebenarnya ada latensi memori dalam mengambil operan, tapi itu tidak ada hubungannya dengan cara kerja perbandingan itu sendiri (dan umumnya tidak mungkin pada arsitektur RISC, meskipun biasanya dimungkinkan pada desain CISC, seperti x86 / x64.)
  • Perbandingan titik-mengambang mungkin multi-siklus, tergantung pada arsitektur.
  • Angka-angka tersebut tidak sesuai dengan ukuran kata ALU dan, dengan demikian, perbandingan harus dipecah menjadi beberapa instruksi.
reirab
sumber
4

@ RobertHarvey jawabannya bagus; pertimbangkan jawaban ini sebagai pelengkap untuknya.


Anda juga harus mempertimbangkan Prediksi Cabang :

Dalam arsitektur komputer, prediktor cabang adalah sirkuit digital yang mencoba menebak ke arah mana sebuah cabang (misalnya struktur if-then-else) akan berjalan sebelum ini diketahui dengan pasti. Tujuan dari prediktor cabang adalah untuk meningkatkan aliran dalam pipa instruksi. Prediktor cabang memainkan peran penting dalam mencapai kinerja efektif tinggi di banyak arsitektur mikroprosesor pipelined modern seperti x86.

Pada dasarnya, dalam contoh Anda, jika ifpernyataan di dalam loop selalu mengembalikan jawaban yang sama, maka sistem dapat mengoptimalkannya dengan menebak dengan benar ke mana ia akan bercabang. Dalam contoh Anda, karena ifpernyataan dalam kasus pertama selalu mengembalikan hasil yang sama, itu akan berjalan sedikit lebih cepat daripada kasus kedua.

Pertanyaan Stack Overflow luar biasa pada subjek

durron597
sumber
Prediksi cabang mempengaruhi waktu percabangan, tetapi bukan waktu perbandingan itu sendiri.
reirab
3

Itu tergantung pada implementasinya, tetapi itu akan sangat, sangat tidak mungkin .

Saya akui bahwa saya belum membaca detail implementasi dari berbagai mesin browser, dan CSS tidak menentukan tipe penyimpanan tertentu untuk angka. Tapi saya percaya bahwa aman untuk berasumsi bahwa semua browser utama menggunakan angka floating-point 64-bit double-precision ("doubles", untuk meminjam istilah dari C / C ++) untuk menangani sebagian besar kebutuhan numerik mereka di CSS , karena inilah yang digunakan JavaScript untuk angka, dan dengan menggunakan tipe yang sama membuat integrasi menjadi lebih mudah.

Dari sudut pandang komputer, semua ganda membawa jumlah data yang sama: 64 bit, apakah nilainya 1 atau -3,14 atau 1000000 atau 1e100 . Jumlah waktu yang diperlukan untuk melakukan operasi pada angka-angka ini tidak tergantung pada nilai aktual dari angka-angka itu, karena selalu bekerja pada jumlah data yang sama. Ada tradeoff dalam melakukan hal-hal seperti ini, di mana ganda tidak dapat secara akurat mewakili semua angka (atau bahkan semua angka dalam kisaran mereka), tetapi mereka bisa cukup dekat untuk sebagian besar masalah, dan hal-hal yang dilakukan CSS tidak secara numerik -menuntut cukup untuk membutuhkan lebih banyak presisi dari itu. Kombinasikan ini dengan manfaat kompatibilitas langsung dengan JavaScript, dan Anda memiliki kasus yang cukup kuat untuk ganda.

Bukan tidak mungkin seseorang dapat mengimplementasikan CSS menggunakan pengodean panjang variabel untuk angka. Jika seseorang menggunakan pengkodean panjang variabel, maka membandingkan dengan angka kecil akan lebih murah daripada membandingkan dengan angka besar, karena angka besar memiliki lebih banyak data untuk dikelompokkan . Jenis-jenis pengkodean ini bisa lebih tepat daripada biner, tetapi mereka juga jauh lebih lambat, dan untuk CSS khususnya, perolehan presisi mungkin tidak cukup untuk menjadi layak untuk hit kinerja. Saya akan sangat terkejut mengetahui bahwa browser apa pun melakukan hal seperti ini.

Sekarang, secara teori, ada satu pengecualian yang mungkin untuk semua yang saya katakan di atas: membandingkan dengan nol sering lebih cepat daripada membandingkan dengan angka lainnya . Ini bukan karena nol pendek (jika itu alasannya, maka saya harusnya sama cepatnya, tetapi bukan itu). Itu karena nol memungkinkan Anda menipu. Ini adalah satu-satunya angka di mana semua bit mati, jadi jika Anda tahu bahwa salah satu nilainya adalah nol, Anda bahkan tidak perlu melihat nilai lainnya sebagai angka: jika ada bit yang dihidupkan maka itu tidak sama dengan nol, dan kemudian Anda hanya perlu melihat satu bit untuk melihat apakah itu lebih besar atau kurang dari nol.

Spooniest
sumber
0

Jika kode ini ditafsirkan setiap kali dijalankan, akan ada perbedaan karena lebih lama untuk tokenise dan ditafsirkan 10000000000000dibandingkan 1000. Namun, ini adalah optimasi pertama yang jelas dari penerjemah dalam kasus ini: tokenise sekali dan menafsirkan token.

Mark Hurd
sumber