Berapa biaya overhead dari pointer pintar dibandingkan dengan pointer normal di C ++ 11? Dengan kata lain, apakah kode saya akan menjadi lebih lambat jika saya menggunakan petunjuk cerdas, dan jika demikian, seberapa lambat?
Secara khusus, saya bertanya tentang C ++ 11 std::shared_ptr
dan std::unique_ptr
.
Jelas, barang-barang yang didorong ke bawah tumpukan akan menjadi lebih besar (setidaknya saya kira begitu), karena penunjuk cerdas juga perlu menyimpan keadaan internalnya (jumlah referensi, dll), pertanyaan sebenarnya adalah, berapa ini akan terjadi? mempengaruhi kinerja saya, jika ada?
Misalnya, saya mengembalikan penunjuk cerdas dari fungsi alih-alih penunjuk normal:
std::shared_ptr<const Value> getValue();
// versus
const Value *getValue();
Atau, misalnya, ketika salah satu fungsi saya menerima penunjuk cerdas sebagai parameter alih-alih penunjuk normal:
void setValue(std::shared_ptr<const Value> val);
// versus
void setValue(const Value *val);
sumber
std::unique_ptr
ataustd::shared_ptr
?Jawaban:
std::unique_ptr
memiliki overhead memori hanya jika Anda menyediakannya dengan beberapa penghapus non-sepele.std::shared_ptr
selalu memiliki overhead memori untuk penghitung referensi, meskipun sangat kecil.std::unique_ptr
memiliki overhead waktu hanya selama konstruktor (jika harus menyalin penghapus yang disediakan dan / atau menginisialisasi null penunjuk) dan selama destruktor (untuk menghancurkan objek yang dimiliki).std::shared_ptr
memiliki waktu tambahan di konstruktor (untuk membuat penghitung referensi), di destruktor (untuk mengurangi penghitung referensi dan mungkin menghancurkan objek) dan dalam operator penugasan (untuk menambah penghitung referensi). Karena jaminan keamanan utasstd::shared_ptr
, kenaikan / penurunan ini bersifat atomik, sehingga menambah overhead lagi.Perhatikan bahwa tidak ada dari mereka yang memiliki overhead waktu dalam dereferensi (dalam mendapatkan referensi ke objek yang dimiliki), sementara operasi ini tampaknya paling umum untuk pointer.
Singkatnya, ada beberapa overhead, tetapi itu seharusnya tidak membuat kode menjadi lambat kecuali Anda terus membuat dan menghancurkan pointer pintar.
sumber
unique_ptr
tidak memiliki overhead di destruktor. Ini persis sama seperti yang Anda lakukan dengan pointer mentah.std::unique_ptr
? Jika Anda membuatstd::unique_ptr<int>
, internalint*
diinisialisasinullptr
apakah Anda suka atau tidak.Seperti halnya semua performa kode, satu-satunya cara yang benar-benar andal untuk memperoleh informasi penting adalah mengukur dan / atau memeriksa kode mesin.
Bisa dikatakan, penalaran sederhana mengatakan itu
Anda dapat mengharapkan beberapa overhead dalam build debug, karena misalnya
operator->
harus dijalankan sebagai pemanggilan fungsi sehingga Anda dapat melangkah ke dalamnya (hal ini pada gilirannya karena kurangnya dukungan untuk menandai kelas dan fungsi sebagai non-debug).Karena
shared_ptr
Anda dapat mengharapkan beberapa overhead dalam pembuatan awal, karena itu melibatkan alokasi dinamis dari blok kontrol, dan alokasi dinamis jauh lebih lambat daripada operasi dasar lainnya di C ++ (gunakanmake_shared
bila memungkinkan, untuk meminimalkan overhead itu).Juga karena
shared_ptr
ada beberapa overhead minimal dalam mempertahankan jumlah referensi, misalnya saat meneruskanshared_ptr
nilai dengan, tetapi tidak ada biaya tambahan untukunique_ptr
.Dengan mengingat poin pertama di atas, saat Anda mengukur, lakukan itu untuk men-debug dan merilis build.
Komite standardisasi internasional C ++ telah menerbitkan laporan teknis tentang kinerja , tetapi ini pada tahun 2006, sebelumnya
unique_ptr
danshared_ptr
ditambahkan ke perpustakaan standar. Namun, petunjuk cerdas adalah topi lama pada saat itu, jadi laporan itu juga mempertimbangkan itu. Mengutip bagian yang relevan:Sebagai tebakan yang diinformasikan, "baik dalam keadaan seni" telah dicapai dengan kompiler paling populer saat ini, pada awal 2014.
sumber
Jawaban saya berbeda dari yang lain dan saya benar-benar bertanya-tanya apakah mereka pernah membuat profil kode.
shared_ptr memiliki overhead yang signifikan untuk pembuatan karena alokasi memorinya untuk blok kontrol (yang membuat penghitung ref dan daftar penunjuk ke semua referensi yang lemah). Ia juga memiliki overhead memori yang besar karena ini dan fakta bahwa std :: shared_ptr selalu merupakan tuple 2 pointer (satu ke objek, satu ke blok kontrol).
Jika Anda meneruskan shared_pointer ke suatu fungsi sebagai parameter nilai maka akan setidaknya 10 kali lebih lambat dari panggilan normal dan membuat banyak kode di segmen kode untuk pelepasan tumpukan. Jika Anda melewatkannya dengan referensi, Anda mendapatkan tipuan tambahan yang juga bisa lebih buruk dalam hal kinerja.
Itulah mengapa Anda tidak boleh melakukan ini kecuali fungsinya benar-benar terlibat dalam manajemen kepemilikan. Jika tidak, gunakan "shared_ptr.get ()". Itu tidak dirancang untuk memastikan objek Anda tidak mati selama panggilan fungsi normal.
Jika Anda menjadi gila dan menggunakan shared_ptr pada objek kecil seperti pohon sintaks abstrak dalam kompiler atau pada node kecil dalam struktur grafik lainnya, Anda akan melihat penurunan performa yang besar dan peningkatan memori yang besar. Saya telah melihat sistem parser yang ditulis ulang segera setelah C ++ 14 masuk pasar dan sebelum pemrogram belajar menggunakan petunjuk pintar dengan benar. Penulisan ulang itu jauh lebih lambat dari kode lama.
Ini bukan peluru perak dan petunjuk mentah juga tidak buruk menurut definisi. Pemrogram yang buruk itu buruk dan desain yang buruk itu buruk. Desain dengan hati-hati, desain dengan kepemilikan yang jelas dalam pikiran dan coba gunakan shared_ptr sebagian besar pada batas API subsistem.
Jika Anda ingin mempelajari lebih lanjut, Anda dapat menonton Nicolai M. Josuttis pembicaraan yang baik tentang "Harga Nyata Pointer Bersama di C ++" https://vimeo.com/131189627
Ini membahas jauh ke dalam detail implementasi dan arsitektur CPU untuk hambatan tulis, atomic kunci dll. setelah mendengarkan Anda tidak akan pernah membicarakan tentang fitur ini murah. Jika Anda hanya ingin bukti besarnya lebih lambat, lewati 48 menit pertama dan lihat dia menjalankan kode contoh yang berjalan hingga 180 kali lebih lambat (dikompilasi dengan -O3) saat menggunakan penunjuk bersama di mana-mana.
sumber
std::make_shared()
? Juga, saya menemukan demonstrasi penyalahgunaan terang-terangan menjadi buruk agak membosankan ...Dengan kata lain, apakah kode saya akan menjadi lebih lambat jika saya menggunakan petunjuk cerdas, dan jika demikian, seberapa lambat?
Lebih lambat? Kemungkinan besar tidak, kecuali Anda membuat indeks besar menggunakan shared_ptrs dan Anda tidak memiliki cukup memori sampai komputer Anda mulai berkerut, seperti seorang wanita tua yang jatuh ke tanah oleh kekuatan yang tak tertahankan dari jauh.
Apa yang akan membuat kode Anda lebih lambat adalah pencarian yang lambat, pemrosesan loop yang tidak perlu, salinan data yang besar, dan banyak operasi tulis ke disk (seperti ratusan).
Keuntungan dari pointer pintar semuanya terkait dengan manajemen. Tetapi apakah biaya overhead diperlukan? Ini tergantung pada penerapan Anda. Misalkan Anda melakukan iterasi pada larik 3 fase, setiap fase memiliki larik 1024 elemen. Membuat sebuah
Tetapi apakah Anda benar-benar ingin melakukan itu?smart_ptr
untuk proses ini mungkin berlebihan, karena setelah iterasi selesai Anda akan tahu bahwa Anda harus menghapusnya. Jadi, Anda bisa mendapatkan memori tambahan karena tidak menggunakan filesmart_ptr
...Kebocoran memori tunggal dapat membuat produk Anda mengalami titik kegagalan (misalkan program Anda membocorkan 4 megabyte setiap jam, akan memakan waktu berbulan-bulan untuk merusak komputer, namun, komputer akan rusak, Anda tahu itu karena ada kebocoran) .
Seperti mengatakan "perangkat lunak Anda dijamin selama 3 bulan, lalu, hubungi saya untuk diservis".
Jadi pada akhirnya masalahnya adalah ... dapatkah Anda menangani risiko ini? tidak menggunakan pointer mentah untuk menangani pengindeksan Anda lebih dari ratusan objek yang berbeda layak kehilangan kendali atas memori.
Jika jawabannya ya, gunakan pointer mentah.
Jika Anda bahkan tidak ingin mempertimbangkannya, a
smart_ptr
adalah solusi yang baik, layak, dan mengagumkan.sumber
smart_ptr
sangat berguna untuk tim besarThats why you should not do this unless the function is really involved in ownership management
... jawaban yang bagus, terima kasih, suara positifHanya untuk sekilas dan hanya untuk
[]
operator, ini ~ 5X lebih lambat dari pointer mentah seperti yang ditunjukkan dalam kode berikut, yang dikompilasi menggunakangcc -lstdc++ -std=c++14 -O0
dan menampilkan hasil ini:Saya mulai belajar c ++, saya memikirkannya: Anda selalu perlu tahu apa yang Anda lakukan dan meluangkan lebih banyak waktu untuk mengetahui apa yang telah dilakukan orang lain di c ++ Anda.
EDIT
Seperti yang ditunjukkan oleh @Mohan Kumar, saya memberikan detail lebih lanjut. Versi gcc adalah
7.4.0 (Ubuntu 7.4.0-1ubuntu1~14.04~ppa1)
, Hasil di atas diperoleh saat-O0
digunakan, namun, saat saya menggunakan flag '-O2', saya mendapatkan ini:Kemudian bergeser ke
clang version 3.9.0
,-O0
adalah:-O2
dulu:Hasil dentang
-O2
luar biasa.sumber
-O0
atau men-debug kode. Outputnya akan sangat tidak efisien . Selalu gunakan setidaknya-O2
(atau-O3
saat ini karena beberapa vektorisasi tidak dilakukan di-O2
)free
panggilan dalam pengujian malloc, dandelete[]
untuk baru (atau membuat variabela
statis), karenaunique_ptr
s memanggildelete[]
bawah tenda, di destruktornya.