Saya bisa melihat mengapa auto
tipe C ++ 11 meningkatkan kebenaran dan perawatan. Saya pernah membaca bahwa itu juga dapat meningkatkan kinerja ( Almost Always Auto oleh Herb Sutter), tetapi saya kehilangan penjelasan yang bagus.
- Bagaimana cara
auto
meningkatkan kinerja? - Adakah yang bisa memberi contoh?
c++
performance
c++11
auto
DaBrain
sumber
sumber
Jawaban:
auto
dapat membantu kinerja dengan menghindari konversi tersirat diam . Contoh yang saya temukan menarik adalah sebagai berikut.Lihat bugnya? Kita di sini, berpikir kita secara elegan mengambil setiap item di peta dengan referensi const dan menggunakan rentang-untuk ekspresi baru untuk memperjelas maksud kita, tetapi sebenarnya kita menyalin setiap elemen. Hal ini karena
std::map<Key, Val>::value_type
inistd::pair<const Key, Val>
, tidakstd::pair<Key, Val>
. Jadi, ketika kita (secara implisit) memiliki:Alih-alih mengambil referensi ke objek yang ada dan membiarkannya, kita harus melakukan konversi jenis. Anda diizinkan untuk mengambil referensi const ke objek (atau sementara) dari jenis yang berbeda selama ada konversi tersirat yang tersedia, misalnya:
Konversi tipe adalah konversi implisit yang diizinkan untuk alasan yang sama Anda dapat mengonversi a
const Key
menjadiKey
, tetapi kami harus membuat sementara dari tipe baru untuk memungkinkannya. Jadi, secara efektif loop kita tidak:(Tentu saja, sebenarnya tidak ada
__tmp
objek, itu hanya ada untuk ilustrasi, pada kenyataannya sementara yang tidak disebutkan namanya hanya terikatitem
untuk seumur hidup).Hanya mengubah ke:
baru saja menyelamatkan kami dari satu ton salinan - sekarang jenis yang direferensikan cocok dengan jenis penginisialisasi, jadi tidak ada sementara atau konversi diperlukan, kami hanya dapat melakukan referensi langsung.
sumber
std::pair<const Key, Val> const &
sebagaistd::pair<Key, Val> const &
? Baru menggunakan C ++ 11, tidak yakin bagaimana range-for danauto
memainkan ini.auto
peningkatan kinerja itu. Jadi saya akan menuliskannya dengan kata-kata saya sendiri di bawah ini.auto
meningkatkan kinerja". Itu hanya contoh yang "auto
membantu mencegah kesalahan programmer yang merusak kinerja". Saya sampaikan bahwa ada perbedaan yang halus namun penting antara keduanya. Tetap, +1.Karena
auto
menyimpulkan jenis ekspresi inisialisasi, tidak ada konversi jenis yang terlibat. Dikombinasikan dengan algoritma templated, ini berarti Anda bisa mendapatkan perhitungan yang lebih langsung daripada jika Anda membuat sendiri suatu tipe - terutama ketika Anda berurusan dengan ekspresi yang tipe yang tidak bisa Anda sebutkan!Sebuah contoh khas berasal dari (ab) menggunakan
std::function
:Dengan
cmp2
dancmp3
, seluruh algoritme dapat menyejajarkan panggilan perbandingan, sedangkan jika Anda membuatstd::function
objek, tidak hanya panggilan tidak dapat digarisbawahi, tetapi Anda juga harus melalui pencarian polimorfik di bagian interior yang dihapus dari pembungkus fungsi.Varian lain pada tema ini adalah Anda dapat mengatakan:
Ini selalu merupakan referensi, terikat pada nilai ekspresi pemanggilan fungsi, dan tidak pernah membuat objek tambahan. Jika Anda tidak tahu tipe nilai yang dikembalikan, Anda mungkin dipaksa untuk membangun objek baru (mungkin sebagai sementara) melalui sesuatu seperti
T && f = MakeAThing()
. (Selain itu,auto &&
bahkan berfungsi ketika tipe pengembalian tidak bergerak dan nilai pengembalian adalah nilai awal.)sumber
auto
. Varian Anda yang lain adalah "hindari salinan yang tidak disengaja", tetapi perlu hiasan; mengapaauto
memberi Anda kecepatan lebih dari sekadar mengetik jenis di sana? (Saya pikir jawabannya adalah "Anda salah ketik, dan itu diam-diam mengubah") Yang membuatnya menjadi contoh jawaban Barry yang tidak dijelaskan dengan baik, bukan? Yaitu, ada dua kasus dasar: otomatis untuk menghindari penghapusan tipe, dan otomatis untuk menghindari kesalahan tipe diam yang secara tidak sengaja mengkonversi, yang keduanya memiliki biaya waktu berjalan.std::bind
,std::function
danstd::stable_partition
semuanya telah diuraikan? Atau hanya dalam prakteknya tidak ada kompiler C ++ akan inline cukup agresif untuk memilah kekacauan?std::function
konstruktor, itu akan sangat kompleks untuk melihat panggilan aktual, terutama dengan optimisasi fungsi kecil (sehingga Anda tidak benar-benar ingin devirtualization). Tentu saja pada prinsipnya semuanya seolah-olah ...Ada dua kategori.
auto
dapat menghindari tipe erasure. Ada jenis-jenis yang tidak dapat disebutkan namanya (seperti lambdas), dan jenis-jenis yang hampir tidak dapat disebutkan namanya (seperti hasil daristd::bind
atau ekspresi-templat sejenis lainnya).Tanpa
auto
, Anda akhirnya harus mengetik menghapus data ke sesuatu sepertistd::function
. Penghapusan tipe membutuhkan biaya.task1
memiliki tipe penghapusan overhead - kemungkinan alokasi heap, kesulitan menggarisbawahi, dan overhead tabel fungsi virtual.task2
tidak punya. Lambdas membutuhkan auto atau bentuk pengurangan tipe lainnya untuk disimpan tanpa penghapusan tipe; tipe lain bisa sangat kompleks sehingga mereka hanya membutuhkannya dalam praktik.Kedua, Anda bisa mendapatkan tipe yang salah. Dalam beberapa kasus, tipe yang salah akan bekerja dengan sempurna, tetapi akan menyebabkan salinan.
akan dikompilasi jika
expression()
kembaliBar const&
atauBar
atau bahkanBar&
, di manaFoo
dapat dibangun dariBar
. Suatu sementaraFoo
akan dibuat, kemudian terikatf
, dan masa hidupnya akan diperpanjang sampaif
hilang.Pemrogram mungkin bermaksud
Bar const& f
dan tidak bermaksud membuat salinan di sana, tetapi salinan itu dibuat terlepas.Contoh paling umum adalah jenis
*std::map<A,B>::const_iterator
, yangstd::pair<A const, B> const&
tidakstd::pair<A,B> const&
, tetapi kesalahan adalah kategori kesalahan yang diam-diam biaya kinerja. Anda dapat membangunstd::pair<A, B>
daristd::pair<const A, B>
. (Kunci pada peta adalah const, karena mengeditnya adalah ide yang buruk)@Barry dan @KerrekSB, keduanya mengilustrasikan kedua prinsip ini dalam jawaban mereka. Ini hanyalah upaya untuk menyoroti dua masalah dalam satu jawaban, dengan kata-kata yang mengarah pada masalah daripada menjadi contoh-sentris.
sumber
Tiga jawaban yang ada memberikan contoh di mana menggunakan
auto
membantu "membuatnya lebih kecil kemungkinannya untuk pesimis secara tidak sengaja" secara efektif menjadikannya "meningkatkan kinerja".Ada sisi lain dari koin. Menggunakan
auto
dengan objek yang memiliki operator yang tidak mengembalikan objek dasar dapat menghasilkan kode yang salah (masih dapat dikompilasi dan dapat dijalankan). Sebagai contoh, pertanyaan ini menanyakan bagaimana menggunakanauto
memberi hasil yang berbeda (tidak benar) menggunakan perpustakaan Eigen, yaitu baris berikutmenghasilkan output yang berbeda. Diakui, ini sebagian besar disebabkan oleh evaluasi malas Eigens, tetapi kode itu / harus transparan kepada pengguna (perpustakaan).
Meskipun kinerja tidak terlalu terpengaruh di sini, penggunaan
auto
untuk menghindari pesimisasi yang tidak disengaja dapat diklasifikasikan sebagai pengoptimalan prematur, atau setidaknya salah;).sumber