Kadang-kadang diklaim bahwa C ++ 11/14 dapat membuat Anda meningkatkan kinerja bahkan ketika hanya mengkompilasi kode C ++ 98. Pembenaran biasanya sepanjang garis semantik bergerak, karena dalam beberapa kasus pembangun nilai secara otomatis dihasilkan atau sekarang bagian dari STL. Sekarang saya bertanya-tanya apakah kasus-kasus ini sebelumnya sebenarnya sudah ditangani oleh RVO atau optimisasi kompiler serupa.
Pertanyaan saya kemudian adalah apakah Anda dapat memberi saya contoh aktual dari kode C ++ 98 yang, tanpa modifikasi, berjalan lebih cepat menggunakan kompiler yang mendukung fitur bahasa baru. Saya mengerti bahwa kompiler penyesuai standar tidak diharuskan untuk melakukan copy elision dan hanya dengan alasan itu semantik bergerak dapat menghasilkan kecepatan, tetapi saya ingin melihat kasus yang kurang patologis, jika Anda mau.
EDIT: Hanya untuk memperjelas, saya tidak bertanya apakah kompiler baru lebih cepat dari kompiler lama, tetapi jika ada kode di mana menambahkan -std = c ++ 14 ke flag kompiler saya akan berjalan lebih cepat (hindari salinan, tetapi jika Anda dapat datang dengan hal lain selain memindahkan semantik, saya akan tertarik juga)
sumber
std::move
dan pemindahan konstruktor (yang akan membutuhkan modifikasi pada kode yang ada). Satu-satunya hal yang benar-benar terkait dengan pertanyaan saya adalah kalimat "Anda mendapatkan keuntungan kecepatan langsung hanya dengan mengkompilasi ulang", yang tidak didukung oleh contoh apa pun (itu menyebutkan STL pada slide yang sama, seperti yang saya lakukan dalam pertanyaan saya, tetapi tidak ada yang spesifik ). Saya meminta beberapa contoh. Jika saya salah membaca slide, beri tahu saya.Jawaban:
Saya mengetahui 5 kategori umum di mana mengkompilasi ulang kompiler C ++ 03 karena C ++ 11 dapat menyebabkan peningkatan kinerja tanpa batas yang secara praktis tidak terkait dengan kualitas implementasi. Ini semua adalah variasi dari semantik gerakan.
std::vector
realokasisetiap kali
foo
buffer dialokasikan kembali dalam C ++ 03 itu disalin setiapvector
dalambar
.Dalam C ++ 11 ia malah memindahkan
bar::data
s, yang pada dasarnya gratis.Dalam hal ini, ini bergantung pada optimasi di dalam
std
wadahvector
. Dalam setiap kasus di bawah ini, penggunaanstd
kontainer hanya karena mereka adalah objek C ++ yang memilikimove
semantik efisien dalam C ++ 11 "secara otomatis" ketika Anda meningkatkan kompiler Anda. Objek yang tidak memblokirnya yang berisistd
wadah juga mewarisimove
konstruktor yang ditingkatkan secara otomatis .Kegagalan NRVO
Ketika NRVO (bernama optimisasi nilai balik) gagal, dalam C ++ 03 jatuh kembali pada salinan, pada C ++ 11 jatuh kembali bergerak. Kegagalan NRVO mudah:
atau bahkan:
Kami memiliki tiga nilai - nilai kembali, dan dua nilai berbeda dalam fungsi. Elision memungkinkan nilai-nilai dalam fungsi untuk 'digabung' dengan nilai kembali, tetapi tidak dengan satu sama lain. Keduanya tidak dapat digabungkan dengan nilai pengembalian tanpa bergabung satu sama lain.
Masalah mendasarnya adalah bahwa NRVO elision rapuh, dan kode dengan perubahan tidak di dekat
return
situs tiba-tiba dapat memiliki pengurangan kinerja besar-besaran di tempat itu tanpa diagnostik yang dikeluarkan. Dalam kebanyakan kasus kegagalan NRVO C ++ 11 berakhir dengan amove
, sedangkan C ++ 03 berakhir dengan salinan.Mengembalikan argumen fungsi
Penghilangan juga tidak mungkin di sini:
di C ++ 11 ini murah: di C ++ 03 tidak ada cara untuk menghindari salinan. Argumen untuk fungsi tidak dapat dielakkan dengan nilai kembali, karena masa pakai dan lokasi parameter dan nilai kembali dikelola oleh kode panggilan.
Namun, C ++ 11 dapat berpindah dari satu ke yang lain. (Dalam contoh mainan yang kurang, sesuatu mungkin dilakukan untuk
set
).push_back
atauinsert
Akhirnya, elisi ke dalam wadah tidak terjadi: tetapi C ++ 11 membebani secara berlebihan nilai memindahkan operator insert, yang menyimpan salinan.
di C ++ 03
whatever
dibuat sementara , kemudian disalin ke dalam vektorv
. 2std::string
buffer dialokasikan, masing-masing dengan data yang identik, dan satu dibuang.Dalam C ++ 11 sementara
whatever
dibuat. Thewhatever&&
push_back
berlebihan makamove
s yang sementara ke vektorv
. Satustd::string
buffer dialokasikan, dan dipindahkan ke vektor. Sebuah kosongstd::string
dibuang.Tugas
Dicuri dari jawaban @ Jarod42 di bawah ini.
Elision tidak dapat terjadi dengan penugasan, tetapi pindah-dari bisa.
di sini
some_function
mengembalikan kandidat untuk dielakkan, tetapi karena tidak digunakan untuk membangun objek secara langsung, itu tidak dapat dielakkan. Dalam C ++ 03, hasil di atas dalam konten sementara disalin kesome_value
. Di C ++ 11, ia dipindahkan kesome_value
, yang pada dasarnya gratis.Untuk efek penuh dari hal di atas, Anda memerlukan kompiler yang mensintesis pemindahan konstruktor dan tugas untuk Anda.
MSVC 2013 mengimplementasikan gerakan konstruktor dalam
std
wadah, tetapi tidak mensintesis gerakan konstruktor pada tipe Anda.Jadi tipe yang mengandung
std::vector
dan sejenisnya tidak mendapatkan peningkatan seperti itu di MSVC2013, tetapi akan mulai mendapatkannya di MSVC2015.dentang dan gcc telah lama diimplementasikan konstruktor bergerak implisit. Kompiler Intel 2013 akan mendukung pembuatan konstruktor pemindahan implisit jika Anda lulus
-Qoption,cpp,--gen_move_operations
(mereka tidak melakukannya secara default dalam upaya untuk kompatibel dengan MSVC2013).sumber
std
kontainer perpustakaan akan diperbarui denganmove
konstruktor "gratis", dan (jika Anda tidak memblokirnya) konstruksi yang menggunakan objek tersebut ( dan kata benda) akan mulai mendapatkan konstruksi gerakan bebas dalam sejumlah situasi. Banyak dari situasi tersebut dicakup oleh elision di C ++ 03: tidak semua.std
kontainer di atas sebagian besar karena mereka murah untuk memindahkan exoensive untuk menyalin jenis yang Anda dapatkan 'gratis' di C ++ 11 ketika mengkompilasi ulang C ++ 03. Thevector::resize
adalah pengecualian: menggunakanmove
C ++ 11.jika Anda memiliki sesuatu seperti:
Anda mendapat salinan di C ++ 03, sedangkan Anda mendapat tugas pemindahan di C ++ 11. sehingga Anda memiliki optimasi gratis dalam hal ini.
sumber
foo().swap(v);