Apakah ada perbedaan kinerja antara i ++ dan ++ i di C?

Jawaban:

397

Ringkasan eksekutif: Tidak.

i++berpotensi lebih lambat daripada ++i, karena nilai lama i 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 ++idan i++.

$ cat i++.c
extern void g(int i);
void f()
{
    int i;

    for (i = 0; i < 100; i++)
        g(i);

}

File-file itu sama, kecuali untuk ++idan i++:

$ diff i++.c ++i.c
6c6
<     for (i = 0; i < 100; i++)
---
>     for (i = 0; i < 100; ++i)

Kami akan mengkompilasinya, dan juga mendapatkan assembler yang dihasilkan:

$ gcc -c i++.c ++i.c
$ gcc -S i++.c ++i.c

Dan kita dapat melihat bahwa kedua objek yang dihasilkan dan file assembler adalah sama.

$ md5 i++.s ++i.s
MD5 (i++.s) = 90f620dda862cd0205cd5db1f2c8c06e
MD5 (++i.s) = 90f620dda862cd0205cd5db1f2c8c06e

$ md5 *.o
MD5 (++i.o) = dd3ef1408d3a9e4287facccec53f7d22
MD5 (i++.o) = dd3ef1408d3a9e4287facccec53f7d22
Mark Harrison
sumber
9
Saya tahu pertanyaan ini tentang C, tetapi saya tertarik untuk mengetahui apakah browser dapat melakukan optimasi ini untuk javascript.
TM.
69
Jadi "Tidak" berlaku untuk kompiler yang Anda uji.
Andreas
173
@ Andreas: Pertanyaan bagus. Saya pernah menjadi penulis kompiler, dan berkesempatan untuk menguji kode ini pada banyak CPU, sistem operasi, dan kompiler. Satu-satunya kompiler yang saya temukan yang tidak mengoptimalkan kasing i ++ (pada kenyataannya, kompiler yang membawa perhatian saya secara profesional) adalah kompiler Software Toolworks C80 oleh Walt Bilofsky. Kompiler itu untuk sistem Intel 8080 CP / M. Aman untuk mengatakan bahwa setiap kompiler yang tidak menyertakan optimisasi ini tidak dimaksudkan untuk penggunaan umum.
Mark Harrison
22
Meskipun perbedaan kinerja diabaikan, dan dioptimalkan dalam banyak kasus - harap perhatikan bahwa itu masih praktek yang baik untuk menggunakan ++ibukan i++. 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 ++iseperti halnya mengetik i++, sebenarnya tidak ada alasan untuk tidak menggunakan ++idi tempat pertama.
monokrom
7
@monokrome Karena pengembang dapat menyediakan implementasi mereka sendiri untuk operator awalan dan postfix dalam banyak bahasa, ini mungkin tidak selalu menjadi solusi yang dapat diterima oleh kompiler tanpa terlebih dahulu membandingkan fungsi-fungsi tersebut, yang mungkin non-sepele.
pickypg
112

Dari Efisiensi versus niat oleh Andrew Koenig:

Pertama, jauh dari jelas bahwa ++ilebih efisien daripada i++, setidaknya di mana variabel integer diperhatikan.

Dan:

Jadi pertanyaan yang harus ditanyakan bukanlah yang mana dari dua operasi ini yang lebih cepat, yang mana dari dua operasi ini yang mengungkapkan lebih akurat apa yang ingin Anda capai. Saya menyampaikan bahwa jika Anda tidak menggunakan nilai ekspresi, tidak pernah ada alasan untuk menggunakan i++bukan ++i, karena tidak pernah ada alasan untuk menyalin nilai dari variabel, kenaikan variabel, dan kemudian melemparkan salinan pergi.

Jadi, jika nilai yang dihasilkan tidak digunakan, saya akan gunakan ++i. Tetapi bukan karena lebih efisien: karena dengan tepat menyatakan niat saya.

Sébastien RoccaSerra
sumber
5
Jangan lupa bahwa operator unary lainnya juga awalan. Saya pikir ++ i adalah cara "semantik" untuk menggunakan operator unary, sementara i ++ ada untuk memenuhi kebutuhan tertentu (evaluasi sebelum penambahan).
TM.
9
Jika nilai yang dihasilkan tidak digunakan, tidak ada perbedaan dalam semantik: yaitu, tidak ada dasar untuk memilih satu atau yang lain membangun.
Eamon Nerbonne
3
Sebagai pembaca, saya melihat perbedaan. Jadi sebagai penulis, saya akan menunjukkan niat saya dengan memilih satu dari yang lain. Ini hanya kebiasaan yang saya miliki, untuk mencoba berkomunikasi dengan teman-teman programmer dan rekan tim saya melalui kode :)
Sébastien RoccaSerra
11
Mengikuti saran Koenig, saya membuat kode i++dengan cara yang sama saya akan kode i += natau i = 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++
David R Tribble
4
Jika Anda mencoba meningkatkan i, maka i ++ dan ++ i keduanya menyatakan maksud Anda dengan benar. Tidak ada alasan untuk lebih menyukai yang satu daripada yang lain. Sebagai pembaca saya tidak melihat perbedaan, apakah rekan kerja Anda menjadi bingung ketika mereka melihat i ++ dan berpikir, mungkin ini salah ketik dan dia tidak bermaksud menambah saya?
dan carter
46

Jawaban yang lebih baik adalah bahwa ++iterkadang akan lebih cepat tetapi tidak pernah lebih lambat.

Semua orang tampaknya berasumsi bahwa itu iadalah tipe bawaan seperti int. Dalam hal ini tidak akan ada perbedaan yang terukur.

Namun jika itipe 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 ++itAnda hanya dapat mengembalikan nilai akhir.

Foo Foo::operator++()
{
  Foo oldFoo = *this; // copy existing value - could be slow
  // yadda yadda, do increment
  return oldFoo;
}

Perbedaan lainnya adalah bahwa ++iAnda 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 ++ialih-alih i++ketika hasilnya tidak terpengaruh.

Andrew Grant
sumber
36
Pertanyaannya secara eksplisit menyatakan C, tidak ada referensi ke C ++.
Dan Cristoloveanu
5
Pertanyaan ini (memang sudah tua) adalah tentang C, bukan C ++, tapi saya pikir itu juga layak disebutkan bahwa, di C ++, bahkan jika sebuah kelas mengimplementasikan operator post dan pre-fix, mereka bahkan belum tentu terkait. Misalnya bilah ++ dapat menambah satu anggota data, sementara bilah ++ dapat menambah anggota data yang berbeda, dan dalam hal ini, Anda tidak akan memiliki opsi untuk menggunakan keduanya, karena semantiknya berbeda.
Kevin
-1 Meskipun ini adalah saran yang bagus untuk programmer C ++, itu tidak menjawab pertanyaan, yang ditandai C, sedikit pun. Dalam C, sama sekali tidak ada bedanya apakah Anda menggunakan awalan atau postfix.
Lundin
@Pacerier Pertanyaan ini hanya ditandai C, dan C. Mengapa Anda menganggap mereka tidak tertarik pada hal itu? Mengingat cara kerja SO, bukankah lebih pintar untuk mengasumsikan mereka hanya tertarik pada C, daripada Java, C #, COBOL atau bahasa lain di luar topik?
Lundin
18

Jawaban singkat:

Tidak pernah ada perbedaan antara i++dan ++idalam 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 versus i++hanya masuk akal dalam ekspresi yang ditemukan.

Dalam kasus for(i=0; i<n; i++), i++sendirian dalam ekspresi sendiri: ada titik urutan sebelum i++dan ada satu setelahnya. Dengan demikian satu-satunya kode mesin yang dihasilkan adalah "meningkat idengan 1" 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 "meningkat isebesar1 ".

Perbedaan antara ++idan i++hanya masalah dalam ekspresi seperti array[i++] = x;versusarray[++i] = x; . Beberapa orang mungkin berdebat dan mengatakan bahwa postfix akan lebih lambat dalam operasi seperti itu karena register tempat itinggal 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:

  • Nilai toko dari i dalam register A.
  • Menyimpan alamat array dalam register B.
  • Tambahkan A dan B, simpan hasilnya di A.
  • Pada alamat baru yang diwakili oleh A, simpan nilai x.
  • Nilai toko dari i dalam register A // tidak efisien karena instruksi tambahan di sini, kami sudah melakukan ini sekali.
  • Daftar kenaikan A.
  • Daftar toko A in i.

kompiler mungkin juga menghasilkan kode lebih efisien, seperti:

  • Nilai toko dari i dalam register A.
  • Menyimpan alamat array dalam register B.
  • Tambahkan A dan B, simpan hasil dalam B.
  • Daftar kenaikan A.
  • Daftar toko A in i.
  • ... // sisa kode.

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.

Lundin
sumber
3
Tidak ada yang mengatakan "Awalan ++ selalu lebih cepat". Itu salah kutip. Apa yang mereka katakan adalah "Postfix ++ tidak pernah lebih cepat".
Pacerier
2
@Pacerier Saya tidak mengutip orang tertentu, tetapi hanya keyakinan yang salah.
Lundin
@rbaleksandar C ++! = C.
Lundin
@rbaleksandar Pertanyaan dan jawaban keduanya berbicara tentang C ++. Yang berbeda dari C, karena operator kelebihan muatan dan menyalin konstruktor dll.
Lundin
3
@rbaleksandar c ++ == c && ++ c! = c
sadljkfhalskdjfh
17

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.

// Prefix
Integer& Integer::operator++()
{
    *this += 1;
    return *this;
}

// Postfix
const Integer Integer::operator++(int)
{
    Integer oldValue = *this;
    ++(*this);
    return oldValue;
}

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.

Pemrogram JP
sumber
4
Saya pikir pertanyaannya diarahkan pada C, tetapi untuk C ++ Anda benar sekali, dan lebih lanjut orang C harus mengadopsi ini karena mereka dapat menggunakannya untuk C ++ juga. Saya terlalu sering melihat programmer C menggunakan sintaks postfix ;-)
Anders Rune Jensen
-1 Meskipun ini adalah saran yang bagus untuk programmer C ++, itu tidak menjawab pertanyaan, yang ditandai C, sedikit pun. Dalam C, sama sekali tidak ada bedanya apakah Anda menggunakan awalan atau postfix.
Lundin
1
@Lundin prefix dan postifx penting di C sesuai jawaban Andreas . Anda tidak dapat berasumsi bahwa kompiler akan dioptimalkan, dan itu hanya praktik yang baik untuk bahasa apa pun untuk Alwas lebih suka ++ i over i ++
JProgrammer
@ JProgrammer Mereka tidak masalah sesuai jawaban Anda dengan tulus :) Jika Anda menemukan mereka menghasilkan kode yang berbeda, itu baik karena Anda gagal mengaktifkan optimasi, atau karena kompilernya buruk. Bagaimanapun, jawaban Anda di luar topik karena pertanyaannya adalah tentang C.
Lundin
16

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:

for (i = 0; i < 100; i++)

Pada setiap loop Anda, Anda akan memiliki satu instruksi masing-masing untuk:

  1. Menambahkan 1ke i.
  2. Bandingkan apakah ikurang dari a 100.
  3. Cabang bersyarat jika ikurang dari a 100.

Sedangkan loop decrementing:

for (i = 100; i != 0; i--)

Loop akan memiliki instruksi untuk masing-masing:

  1. Decrement i, mengatur flag status register CPU.
  2. Cabang bersyarat tergantung pada status register CPU ( Z==0).

Tentu saja ini hanya berfungsi ketika mengurangi ke nol!

Diingat dari Panduan Pengembang Sistem ARM.

tonylo
sumber
Bagus Tapi bukankah ini membuat hit cache lebih sedikit?
AndreasT
Ada trik aneh lama dari buku pengoptimalan kode, menggabungkan keuntungan dari cabang uji nol dengan alamat yang bertambah. Berikut ini contoh dalam bahasa tingkat tinggi (mungkin tidak berguna, karena banyak kompiler cukup pintar untuk menggantinya dengan kode loop yang kurang efisien tetapi lebih umum): int a [N]; untuk (i = -N; i; ++ i) a [N + i] + = 123;
noop
@noop, bisakah Anda menguraikan buku pengoptimalan kode mana yang Anda rujuk? Saya telah berjuang untuk menemukan yang baik.
mezamorphic
7
Jawaban ini sama sekali bukan jawaban untuk pertanyaan itu.
monokrom
@mezamorphic Untuk x86, sumber saya adalah: manual pengoptimalan Intel, Agner Fog, Paul Hsieh, Michael Abrash.
noop
11

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.

Andy Lester
sumber
3
Saya percaya itu salah untuk lebih memilih peningkatan keterbacaan yang tidak jelas untuk keuntungan efisiensi aktual dan kejelasan niat secara keseluruhan.
noop
4
Istilah saya "waktu membaca programmer" kira-kira analog dengan "kejelasan niat". "Keuntungan efisiensi aktual" seringkali tak terukur, cukup dekat ke nol untuk menyebutnya nol. Dalam kasus OP, kecuali kode telah diprofilkan untuk menemukan bahwa ++ i adalah hambatan, pertanyaan mana yang lebih cepat adalah buang-buang waktu dan pemikir unit pemikir.
Andy Lester
2
Perbedaan dalam keterbacaan antara ++ i dan i ++ hanya masalah preferensi pribadi, tetapi ++ i jelas menyiratkan operasi lebih sederhana daripada i ++, meskipun hasilnya setara untuk kasus-kasus sepele dan tipe data sederhana ketika mengoptimalkan kompiler terlibat. Oleh karena itu, + + i adalah pemenang bagi saya, ketika properti spesifik pasca kenaikan tidak diperlukan.
noop
Anda mengatakan apa yang saya katakan. Lebih penting untuk menunjukkan niat dan meningkatkan keterbacaan daripada khawatir tentang "efisiensi."
Andy Lester
2
Saya masih tidak setuju. Jika keterbacaan lebih tinggi dalam daftar prioritas Anda maka mungkin pilihan bahasa pemrograman Anda salah. C / C ++ tujuan utama adalah menulis kode yang efisien.
noor
11

Pertama-tama: Perbedaan antara i++dan ++idapat diabaikan dalam C.


Untuk detailnya.

1. Masalah C ++ yang terkenal: ++ilebih cepat

Dalam C ++, ++ilebih efisien jika iff iadalah 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 adalah foo(i++)karena sekarang kenaikan harus dilakukan sebelum foo()dipanggil, tetapi nilai lama perlu diteruskan ke foo(). Akibatnya, kompiler terpaksa membuat salinan isebelum 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 cepat

Jika tidak ada konstruktor / destruktor yang perlu dipanggil, yang selalu terjadi di C, ++idan i++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. Dengan i++, 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.

cmaster - mengembalikan monica
sumber
Tentang poin Anda 2: Jika ++iatau i++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, misalnya INCinstruksi perakitan.
Shahbaz
1
@ Shahbaz Itu benar sekali, tapi bukan itu intinya. 1) Meskipun semantiknya berbeda, keduanya i++dan ++idapat 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 ini i++, CPU dapat menghitung kenaikan secara paralel dengan beberapa instruksi lain yang menggunakan nilai yang sama (CPU benar-benar melakukan ini!), Sedangkan dengan ++iCPU harus menjadwalkan instruksi lainnya setelah kenaikan.
cmaster - mengembalikan monica
4
@ Shahbaz Sebagai contoh: if(++foo == 7) bar();dan if(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.
cmaster - mengembalikan monica
1
Poin yang bagus. Konstanta banyak muncul (dan juga <misalnya vs <=) di mana ++biasanya digunakan, sehingga konversi antara meskipun sering mudah dilakukan.
Shahbaz
1
Saya suka poin 2, tapi itu hanya berlaku jika nilainya digunakan, oke? Pertanyaannya mengatakan "jika nilai yang dihasilkan tidak digunakan?", Jadi itu bisa membingungkan.
jinawee
7

@ 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 ++.

Andreas
sumber
Hanya ingin tahu ... mengapa Anda menggunakan 4 kompiler C yang berbeda dalam satu proyek? Atau dalam satu tim atau satu perusahaan, dalam hal ini?
Lawrence Dol
3
Saat membuat game untuk konsol, setiap platform membawa kompiler / toolchain sendiri. Di dunia yang sempurna kita bisa menggunakan gcc / clang / llvm untuk semua target, tetapi di dunia ini kita harus tahan dengan Microsoft, Intel, Metroworks, Sony, dll.
Andreas
5

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.

Kristopher Johnson
sumber
4

Saya bisa memikirkan situasi di mana postfix lebih lambat daripada peningkatan awalan:

Bayangkan sebuah prosesor dengan register Adigunakan 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:

a = ++b + c;

; increment b
LD    A, [&b]
INC   A
ST    A, [&b]

; add with c
ADD   A, [&c]

; store in a
ST    A, [&a]

Peningkatan postfix:

a = b++ + c;

; load b
LD    A, [&b]

; add with c
ADD   A, [&c]

; store in a
ST    A, [&a]

; increment b
LD    A, [&b]
INC   A
ST    A, [&b]

Perhatikan bagaimana nilai bpaksa 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 ++btanpa usaha tambahan (misalnya dengan menambahkan a - 1). Jadi membandingkan keduanya jika mereka adalah bagian dari ekspresi tidak benar-benar valid. Seringkali, di mana Anda menggunakan b++di dalam ekspresi yang tidak dapat Anda gunakan ++b, jadi bahkan jika ++bberpotensi lebih efisien, itu hanya akan salah. Pengecualian tentu saja jika ungkapan itu memohon untuk itu (misalnyaa = b++ + 1; yang dapat diubah menjadia = ++b; ).

Shahbaz
sumber
4

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 daripada i--). 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).

DaShier
sumber
2
Sangat esoteris, tetapi sangat menarik!
Mark Harrison
@daShier. +1, meskipun saya tidak setuju, ini bukan esoteris, atau setidaknya tidak seharusnya. C dikembangkan bersama dengan Unix di AT&T Bell Labs pada awal 70-an ketika PDP-11 adalah prosesor target. Dalam kode sumber Unix dari kenaikan pasca era ini, "i ++", lebih lazim sebagian karena pengembang tahu ketika nilai diberikan, "j = i ++", atau digunakan sebagai indeks, "a [i ++] = n", kode akan sedikit lebih cepat (dan lebih kecil). Tampaknya mereka mulai terbiasa menggunakan penambahan selisih kecuali jika diperlukan. Yang lain belajar dengan membaca kode mereka dan juga mengambil kebiasaan ini.
jimhark
The 68000 memiliki fitur yang sama, post-increment dan pre-decrement didukung dalam perangkat keras (seperti mode pengalamatan), tetapi tidak sebaliknya. Tim Motorola, um, terinspirasi oleh DEC PDP-11.
jimhark
2
@jimhark, ya, saya salah satu dari programmer PDP-11 yang beralih ke 68000 dan masih menggunakan --idan i++.
DaShier
2

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
Posting Anda adalah C ++ - spesifik sementara pertanyaannya adalah tentang C. Dalam kasus apa pun, jawaban Anda salah: untuk operator pasca kenaikan kustom, kompiler umumnya tidak akan dapat menghasilkan kode yang efisien.
Konrad Rudolph
Dia menyatakan "jika fungsinya digarisbawahi", dan itu membuat alasannya benar.
Blaisorblade
Karena C tidak memberikan kelebihan operator, pertanyaan sebelum dan sesudah sebagian besar tidak menarik. Kompilator dapat mengoptimalkan temp yang tidak digunakan menggunakan logika yang sama yang diterapkan pada nilai lain yang tidak digunakan, yang dihitung secara primitif. Lihat jawaban yang dipilih untuk sampel.
0

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?

myArray[i++] = "hello";

vs.

myArray[++i] = "hello";

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.

Jason Z
sumber
1
@Jason Z Optimalisasi kompiler terjadi sebelum perakitan selesai dibuat, ia akan melihat bahwa variabel i tidak digunakan di tempat lain pada baris yang sama, jadi memegang nilainya akan sia-sia, mungkin efektif membalikkannya ke i ++. Tapi itu hanya dugaan. Saya tidak bisa menunggu sampai salah satu dosen saya mencoba untuk mengatakan itu lebih cepat dan saya bisa menjadi orang yang mengoreksi teori dengan bukti praktis. Aku hampir bisa merasakan permusuhan ^ _ ^
Tarks
3
"C-ku agak karatan, jadi aku minta maaf sebelumnya." Tidak ada yang salah dengan C Anda, tetapi Anda tidak membaca pertanyaan asli secara penuh: "Apakah ada perbedaan kinerja antara i ++ dan ++ i jika nilai yang dihasilkan tidak digunakan? " Perhatikan huruf miring. Dalam contoh Anda, 'hasil' dari i ++ / ++ i yang digunakan, tetapi dalam idiomatik untuk loop, yang 'hasil' dari pra / operator postincrement tidak digunakan sehingga compiler dapat melakukan apa yang suka.
Roddy
2
dalam contoh Anda kode akan berbeda, karena Anda menggunakan nilainya. contoh di mana mereka sama hanya menggunakan ++ untuk peningkatan, dan tidak menggunakan nilai yang dikembalikan oleh salah satunya.
John Gardner
1
Perbaiki: "kompiler akan melihat bahwa nilai kembalinya i ++ tidak digunakan, jadi ia akan mengubahnya menjadi ++ i". Apa yang Anda tulis salah juga karena Anda tidak dapat memiliki saya bersama dengan salah satu dari i ++, i ++ pada baris yang sama (pernyataan), hasil itu tidak terdefinisi.
Blaisorblade
1
Mengubah foo[i++]ke foo[++i]tanpa mengubah apa pun akan jelas mengubah semantik program, tetapi pada beberapa prosesor saat menggunakan compiler tanpa optimasi logika loop-mengangkat, incrementing pdan qsekali 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.
supercat