Apakah ada perbedaan kinerja antara i++
dan ++i
jika nilai yang dihasilkan tidak digunakan?
c
performance
optimization
post-increment
pre-increment
Mark Harrison
sumber
sumber
Jawaban:
Ringkasan eksekutif: Tidak.
i++
berpotensi lebih lambat daripada++i
, karena nilai lamai
mungkin perlu disimpan untuk digunakan nanti, tetapi dalam praktiknya semua kompiler modern akan mengoptimalkan ini.Kita dapat mendemonstrasikan ini dengan melihat kode untuk fungsi ini, baik dengan
++i
dani++
.File-file itu sama, kecuali untuk
++i
dani++
:Kami akan mengkompilasinya, dan juga mendapatkan assembler yang dihasilkan:
Dan kita dapat melihat bahwa kedua objek yang dihasilkan dan file assembler adalah sama.
sumber
++i
bukani++
. Sama sekali tidak ada alasan untuk tidak melakukannya, dan jika perangkat lunak Anda pernah melewati rantai alat yang tidak mengoptimalkannya, perangkat lunak Anda akan lebih efisien. Mengingat itu mudah untuk mengetik++i
seperti halnya mengetiki++
, sebenarnya tidak ada alasan untuk tidak menggunakan++i
di tempat pertama.Dari Efisiensi versus niat oleh Andrew Koenig:
Dan:
Jadi, jika nilai yang dihasilkan tidak digunakan, saya akan gunakan
++i
. Tetapi bukan karena lebih efisien: karena dengan tepat menyatakan niat saya.sumber
i++
dengan cara yang sama saya akan kodei += n
ataui = i + n
, yaitu, dalam bentuk objek kata kerja target , dengan operan target di sebelah kiri operator kata kerja . Dalam kasus , tidak ada objek yang benar , tetapi aturan tetap berlaku, menjaga target di sebelah kiri operator kata kerja .i++
Jawaban yang lebih baik adalah bahwa
++i
terkadang akan lebih cepat tetapi tidak pernah lebih lambat.Semua orang tampaknya berasumsi bahwa itu
i
adalah tipe bawaan sepertiint
. Dalam hal ini tidak akan ada perbedaan yang terukur.Namun jika
i
tipe yang kompleks maka Anda mungkin menemukan perbedaan yang terukur. Untuki++
Anda harus membuat salinan kelas Anda sebelum menambahkannya. Tergantung pada apa yang terlibat dalam salinan itu memang bisa lebih lambat karena dengan++it
Anda hanya dapat mengembalikan nilai akhir.Perbedaan lainnya adalah bahwa
++i
Anda memiliki opsi untuk mengembalikan referensi alih-alih nilai. Sekali lagi, tergantung pada apa yang terlibat dalam membuat salinan objek Anda ini bisa lebih lambat.Contoh dunia nyata di mana ini bisa terjadi adalah penggunaan iterator. Menyalin iterator tidak mungkin menjadi masalah besar dalam aplikasi Anda, tetapi masih merupakan praktik yang baik untuk membiasakan diri menggunakan
++i
alih-alihi++
ketika hasilnya tidak terpengaruh.sumber
Jawaban singkat:
Tidak pernah ada perbedaan antara
i++
dan++i
dalam hal kecepatan. Kompiler yang baik seharusnya tidak menghasilkan kode yang berbeda dalam dua kasus.Jawaban panjang:
Apa yang gagal dijawab oleh setiap jawaban lainnya adalah perbedaannya
++i
versusi++
hanya masuk akal dalam ekspresi yang ditemukan.Dalam kasus
for(i=0; i<n; i++)
,i++
sendirian dalam ekspresi sendiri: ada titik urutan sebelumi++
dan ada satu setelahnya. Dengan demikian satu-satunya kode mesin yang dihasilkan adalah "meningkati
dengan1
" dan itu didefinisikan dengan baik bagaimana ini diurutkan sehubungan dengan sisa program. Jadi jika Anda mengubahnya menjadi awalan++
, itu tidak masalah sedikit pun, Anda masih akan mendapatkan kode mesin "meningkati
sebesar1
".Perbedaan antara
++i
dani++
hanya masalah dalam ekspresi sepertiarray[i++] = x;
versusarray[++i] = x;
. Beberapa orang mungkin berdebat dan mengatakan bahwa postfix akan lebih lambat dalam operasi seperti itu karena register tempati
tinggal harus dimuat ulang nanti. Tetapi kemudian perhatikan bahwa kompiler bebas untuk memesan instruksi Anda dengan cara apa pun yang diinginkan, asalkan itu tidak "mematahkan perilaku mesin abstrak" sebagaimana standar C menyebutnya.Jadi, sementara Anda mungkin menganggap bahwa
array[i++] = x;
akan diterjemahkan ke kode mesin sebagai:i
dalam register A.i
dalam register A // tidak efisien karena instruksi tambahan di sini, kami sudah melakukan ini sekali.i
.kompiler mungkin juga menghasilkan kode lebih efisien, seperti:
i
dalam register A.i
.Hanya karena Anda sebagai programmer C dilatih untuk berpikir bahwa postfix itu
++
terjadi pada akhirnya, kode mesin tidak harus dipesan dengan cara itu.Jadi tidak ada perbedaan antara prefix dan postfix
++
dalam C. Sekarang apa yang Anda sebagai programmer C harus bervariasi, adalah orang-orang yang tidak konsisten menggunakan awalan dalam beberapa kasus dan postfix dalam kasus lain, tanpa alasan mengapa. Ini menunjukkan bahwa mereka tidak yakin tentang bagaimana C bekerja atau bahwa mereka memiliki pengetahuan bahasa yang salah. Ini selalu pertanda buruk, yang pada gilirannya menunjukkan bahwa mereka membuat keputusan yang dipertanyakan lainnya dalam program mereka, berdasarkan takhayul atau "dogma agama"."Awalan
++
selalu lebih cepat" memang merupakan salah satu dogma palsu yang umum di antara calon programmer C.sumber
Mengambil daun dari Scott Meyers, Lebih Efektif c ++ Item 6: Bedakan antara bentuk peningkatan dan penurunan awalan dan postfix .
Versi awalan selalu lebih disukai daripada postfix sehubungan dengan objek, terutama dalam hal iterator.
Alasan untuk ini jika Anda melihat pola panggilan operator.
Melihat contoh ini, mudah untuk melihat bagaimana operator awalan akan selalu lebih efisien daripada postfix. Karena kebutuhan akan objek sementara dalam penggunaan postfix.
Inilah sebabnya mengapa ketika Anda melihat contoh menggunakan iterator mereka selalu menggunakan versi awalan.
Tetapi ketika Anda menunjukkan untuk int tidak ada perbedaan secara efektif karena optimasi kompiler yang dapat terjadi.
sumber
Berikut ini pengamatan tambahan jika Anda khawatir tentang optimasi mikro. Mengurangi loop bisa 'mungkin' lebih efisien daripada menambah loop (tergantung pada arsitektur set instruksi misalnya ARM), mengingat:
Pada setiap loop Anda, Anda akan memiliki satu instruksi masing-masing untuk:
1
kei
.i
kurang dari a100
.i
kurang dari a100
.Sedangkan loop decrementing:
Loop akan memiliki instruksi untuk masing-masing:
i
, mengatur flag status register CPU.Z==0
).Tentu saja ini hanya berfungsi ketika mengurangi ke nol!
Diingat dari Panduan Pengembang Sistem ARM.
sumber
Tolong jangan biarkan pertanyaan "mana yang lebih cepat" menjadi faktor penentu yang digunakan. Kemungkinannya adalah Anda tidak akan terlalu peduli, dan selain itu, waktu membaca programmer jauh lebih mahal daripada waktu mesin.
Gunakan yang paling masuk akal bagi manusia yang membaca kode.
sumber
Pertama-tama: Perbedaan antara
i++
dan++i
dapat diabaikan dalam C.Untuk detailnya.
1. Masalah C ++ yang terkenal:
++i
lebih cepatDalam C ++,
++i
lebih efisien jika iffi
adalah semacam objek dengan operator kenaikan berlebih.Mengapa?
Dalam
++i
, objek pertama kali bertambah, dan selanjutnya dapat dilewatkan sebagai referensi const ke fungsi lainnya. Ini tidak mungkin jika ungkapannya adalahfoo(i++)
karena sekarang kenaikan harus dilakukan sebelumfoo()
dipanggil, tetapi nilai lama perlu diteruskan kefoo()
. Akibatnya, kompiler terpaksa membuat salinani
sebelum mengeksekusi operator kenaikan pada aslinya. Panggilan konstruktor / destruktor tambahan adalah bagian yang buruk.Seperti disebutkan di atas, ini tidak berlaku untuk tipe fundamental.
2. Fakta kecil yang diketahui:
i++
mungkin lebih cepatJika tidak ada konstruktor / destruktor yang perlu dipanggil, yang selalu terjadi di C,
++i
dani++
harus sama cepatnya, bukan? Tidak. Mereka sebenarnya sama cepatnya, tetapi mungkin ada perbedaan kecil, yang dijawab oleh sebagian besar penjawab lain.Bagaimana bisa
i++
lebih cepat?Intinya adalah ketergantungan data. Jika nilai perlu dimuat dari memori, dua operasi selanjutnya harus dilakukan dengan itu, menambahnya, dan menggunakannya. Dengan
++i
, incrementation perlu dilakukan sebelum nilainya dapat digunakan. Dengani++
, penggunaan tidak tergantung pada kenaikan, dan CPU dapat melakukan operasi penggunaan secara paralel dengan operasi kenaikan. Perbedaannya paling banyak adalah satu siklus CPU, sehingga benar-benar dapat diabaikan, tetapi itu ada. Dan sebaliknya, banyak orang akan berharap.sumber
++i
ataui++
digunakan dalam ekspresi lain, mengubah di antara mereka mengubah semantik ekspresi, sehingga kemungkinan untung / rugi kinerja yang mungkin adalah pertanyaan. Jika mereka berdiri sendiri, yaitu, hasil operasi tidak digunakan segera, maka setiap kompiler yang layak akan mengkompilasinya ke hal yang sama, misalnyaINC
instruksi perakitan.i++
dan++i
dapat digunakan secara bergantian di hampir setiap situasi yang mungkin dengan menyesuaikan konstanta loop oleh satu, sehingga mereka hampir setara dalam apa yang mereka lakukan untuk programmer. 2) Meskipun keduanya mengkompilasi dengan instruksi yang sama, eksekusi mereka berbeda untuk CPU. Dalam kasus inii++
, CPU dapat menghitung kenaikan secara paralel dengan beberapa instruksi lain yang menggunakan nilai yang sama (CPU benar-benar melakukan ini!), Sedangkan dengan++i
CPU harus menjadwalkan instruksi lainnya setelah kenaikan.if(++foo == 7) bar();
danif(foo++ == 6) bar();
secara fungsional setara. Namun, yang kedua mungkin satu siklus lebih cepat, karena perbandingan dan kenaikannya dapat dihitung secara paralel oleh CPU. Bukan berarti siklus tunggal ini sangat berarti, tetapi perbedaannya ada di sana.<
misalnya vs<=
) di mana++
biasanya digunakan, sehingga konversi antara meskipun sering mudah dilakukan.@ Mark Meskipun kompiler diperbolehkan untuk mengoptimalkan salinan sementara variabel (berbasis stack) dan gcc (dalam versi terbaru) melakukannya, tidak berarti semua kompiler akan selalu melakukannya.
Saya baru saja mengujinya dengan kompiler yang kami gunakan dalam proyek kami saat ini dan 3 dari 4 tidak mengoptimalkannya.
Jangan pernah menganggap kompiler melakukannya dengan benar, terutama jika kode lebih cepat, tetapi tidak pernah lebih lambat, mudah dibaca.
Jika Anda tidak memiliki implementasi yang sangat bodoh dari salah satu operator dalam kode Anda:
Lebih suka ++ i daripada i ++.
sumber
Dalam C, kompiler umumnya dapat mengoptimalkannya sama jika hasilnya tidak digunakan.
Namun, di C ++ jika menggunakan tipe lain yang menyediakan operator ++ mereka sendiri, versi awalan cenderung lebih cepat daripada versi postfix. Jadi, jika Anda tidak memerlukan semantik postfix, lebih baik menggunakan operator awalan.
sumber
Saya bisa memikirkan situasi di mana postfix lebih lambat daripada peningkatan awalan:
Bayangkan sebuah prosesor dengan register
A
digunakan sebagai akumulator dan itu satu-satunya register yang digunakan dalam banyak instruksi (beberapa mikrokontroler kecil sebenarnya seperti ini).Sekarang bayangkan program berikut dan terjemahannya ke dalam kumpulan hipotesis:
Peningkatan awalan:
Peningkatan postfix:
Perhatikan bagaimana nilai
b
paksa dimuat ulang. Dengan kenaikan awalan, kompiler hanya bisa menambah nilai dan terus menggunakannya, mungkin menghindari memuat ulang karena nilai yang diinginkan sudah ada di register setelah kenaikan. Namun, dengan penambahan postfix, kompiler harus berurusan dengan dua nilai, satu yang lama dan satu nilai yang bertambah yang seperti yang saya perlihatkan di atas menghasilkan satu lagi akses memori.Tentu saja, jika nilai kenaikan tidak digunakan, seperti
i++;
pernyataan tunggal , kompiler dapat (dan memang) hanya menghasilkan instruksi kenaikan terlepas dari penggunaan postfix atau awalan.Sebagai catatan tambahan, saya ingin menyebutkan bahwa ekspresi di mana ada
b++
tidak dapat dengan mudah dikonversi menjadi++b
tanpa usaha tambahan (misalnya dengan menambahkan a- 1
). Jadi membandingkan keduanya jika mereka adalah bagian dari ekspresi tidak benar-benar valid. Seringkali, di mana Anda menggunakanb++
di dalam ekspresi yang tidak dapat Anda gunakan++b
, jadi bahkan jika++b
berpotensi lebih efisien, itu hanya akan salah. Pengecualian tentu saja jika ungkapan itu memohon untuk itu (misalnyaa = b++ + 1;
yang dapat diubah menjadia = ++b;
).sumber
Saya telah membaca melalui sebagian besar jawaban di sini dan banyak komentar, dan saya tidak melihat referensi ke salah satu contoh yang saya bisa memikirkan mana
i++
yang lebih efisien daripada++i
(dan mungkin mengejutkan--i
adalah lebih efisien daripadai--
). Itu untuk kompiler C untuk DEC PDP-11!PDP-11 memiliki instruksi perakitan untuk pra-pengurangan register dan pasca kenaikan, tetapi tidak sebaliknya. Instruksi memungkinkan register "tujuan umum" digunakan sebagai penunjuk tumpukan. Jadi jika Anda menggunakan sesuatu seperti
*(i++)
itu bisa dikompilasi menjadi instruksi perakitan tunggal, sementara*(++i)
tidak bisa.Ini jelas merupakan contoh yang sangat esoteris, tetapi itu memberikan pengecualian di mana peningkatan pasca-lebih efisien (atau saya harus katakan adalah , karena tidak ada banyak permintaan untuk kode PDP-11 C hari ini).
sumber
--i
dani++
.Saya selalu lebih suka pra-kenaikan, namun ...
Saya ingin menunjukkan bahwa bahkan dalam kasus memanggil fungsi operator ++, kompiler akan dapat mengoptimalkan sementara sementara jika fungsi mendapat inline. Karena operator ++ biasanya pendek dan sering diimplementasikan dalam header, itu kemungkinan akan diikutsertakan.
Jadi, untuk tujuan praktis, kemungkinan tidak ada banyak perbedaan antara kinerja kedua bentuk. Namun, saya selalu lebih suka pra-kenaikan karena tampaknya lebih baik untuk secara langsung mengungkapkan apa yang saya coba katakan, daripada mengandalkan pengoptimal untuk mencari tahu.
Juga, memberikan optmizer lebih sedikit untuk melakukan kemungkinan berarti kompiler berjalan lebih cepat.
sumber
C saya agak berkarat, jadi saya minta maaf sebelumnya. Secara cepat, saya bisa mengerti hasilnya. Tapi, saya bingung bagaimana kedua file keluar ke hash MD5 yang sama. Mungkin for for menjalankan hal yang sama, tetapi bukankah 2 baris kode berikut menghasilkan rakitan yang berbeda?
vs.
Yang pertama menulis nilai ke array, lalu menambahkan i. Peningkatan kedua saya kemudian menulis ke array. Saya bukan pakar perakitan, tapi saya tidak melihat bagaimana executable yang sama akan dihasilkan oleh 2 baris kode yang berbeda ini.
Hanya dua sen saya.
sumber
foo[i++]
kefoo[++i]
tanpa mengubah apa pun akan jelas mengubah semantik program, tetapi pada beberapa prosesor saat menggunakan compiler tanpa optimasi logika loop-mengangkat, incrementingp
danq
sekali dan kemudian menjalankan loop yang melakukan misalnya*(p++)=*(q++);
akan lebih cepat daripada menggunakan loop yang melakukan*(++pp)=*(++q);
. Untuk pengulangan yang sangat ketat pada beberapa prosesor, perbedaan kecepatan mungkin signifikan (lebih dari 10%), tapi itu mungkin satu-satunya kasus di C di mana kenaikan pasca secara material lebih cepat daripada kenaikan pra.