Ini sedikit dari dua pertanyaan bagian, semua tentang atomisitas std::shared_ptr
:
1.
Sejauh yang saya tahu, std::shared_ptr
adalah satu-satunya penunjuk cerdas dalam <memory>
atom itu. Saya bertanya-tanya apakah ada versi non-atomik yang std::shared_ptr
tersedia (saya tidak dapat melihat apa pun di dalamnya <memory>
, jadi saya juga terbuka untuk saran di luar standar, seperti yang ada di Boost). Saya tahu boost::shared_ptr
juga atom (jika BOOST_SP_DISABLE_THREADS
tidak ditentukan), tetapi mungkin ada alternatif lain? Saya mencari sesuatu yang memiliki semantik yang sama std::shared_ptr
, tetapi tanpa atomicity.
2. Saya mengerti mengapa std::shared_ptr
atom; itu bagus. Namun, itu tidak baik untuk setiap situasi, dan C ++ secara historis memiliki mantra "hanya bayar untuk apa yang Anda gunakan." Jika saya tidak menggunakan banyak utas, atau jika saya menggunakan beberapa utas tetapi tidak berbagi kepemilikan penunjuk di utas, penunjuk cerdas atom berlebihan. Pertanyaan kedua saya adalah mengapa tidak ada versi non-atomik yang std::shared_ptr
disediakan dalam C ++ 11 ? (dengan asumsi ada alasannya ) (jika jawabannya hanya "versi non-atom tidak pernah dipertimbangkan" atau "tidak ada yang pernah meminta versi non-atom" itu bagus!).
Dengan pertanyaan # 2, saya bertanya-tanya apakah seseorang pernah mengusulkan versi non-atomik shared_ptr
(baik untuk Boost atau komite standar) (bukan untuk menggantikan versi atom shared_ptr
, tetapi untuk hidup berdampingan dengannya) dan itu ditolak untuk a alasan spesifik.
sumber
shared_ptr
mengalami perlambatan yang signifikan karena atomicity, dan mendefinisikanBOOST_DISABLE_THREADS
membuat perbedaan yang nyata (saya tidak tahu apakahstd::shared_ptr
akan memiliki biaya yang sama dengan yangboost::shared_ptr
terjadi).shared_ptr
tidak menggunakan operasi atom untuk refcount. Lihat (2) di gcc.gnu.org/ml/libstdc++/2007-10/msg00180.html untuk patch ke GCC untuk memungkinkan implementasi non-atomik digunakan bahkan di aplikasi multithread, untukshared_ptr
objek yang tidak dibagi antara benang. Saya telah duduk di tambalan itu selama bertahun-tahun tetapi saya akhirnya mempertimbangkan untuk melakukannya untuk GCC 4.9Jawaban:
Tidak disediakan oleh standar. Mungkin ada satu yang disediakan oleh perpustakaan "pihak ketiga". Memang, sebelum C ++ 11, dan sebelum Boost, sepertinya semua orang menulis referensi mereka sendiri yang dihitung oleh penunjuk cerdas (termasuk saya).
Pertanyaan ini dibahas pada pertemuan Rapperswil tahun 2010. Hal tersebut dikemukakan oleh National Body Comment # 20 oleh Swiss. Ada argumen kuat di kedua sisi perdebatan, termasuk yang Anda berikan dalam pertanyaan Anda. Namun, di akhir diskusi, pemungutan suara sangat (tetapi tidak bulat) menentang penambahan versi yang tidak sinkron (non-atomik) dari
shared_ptr
.Argumen yang menentang termasuk:
Kode yang ditulis dengan shared_ptr yang tidak disinkronkan mungkin akan digunakan dalam kode berulir di masa mendatang, yang akhirnya menyebabkan kesulitan untuk men-debug masalah tanpa peringatan.
Memiliki satu "universal" shared_ptr yang merupakan "satu cara" untuk memproses penghitungan referensi memiliki keuntungan: Dari proposal asli :
Biaya atom, meskipun tidak nol, tidak terlalu besar. Biaya dikurangi dengan penggunaan konstruksi bergerak dan tugas pindah yang tidak perlu menggunakan operasi atom. Operasi semacam itu biasanya digunakan dalam
vector<shared_ptr<T>>
menghapus dan menyisipkan.Tidak ada yang melarang orang untuk menulis penunjuk cerdas yang dihitung referensi non-atomik mereka sendiri jika itu benar-benar yang ingin mereka lakukan.
Kata terakhir dari POKJA di Rapperswil hari itu adalah:
sumber
Has the same object type regardless of features used, greatly facilitating interoperability between libraries, including third-party libraries.
itu alasan yang sangat aneh. Library pihak ketiga akan menyediakan tipenya sendiri, jadi mengapa penting jika mereka menyediakannya dalam bentuk std :: shared_ptr <CustomType>, std :: non_atomic_shared_ptr <CustomType>, dll? Anda harus selalu menyesuaikan kode Anda dengan apa yang dikembalikan perpustakaanstd::shared_ptr<std::string>
di suatu tempat. Jika perpustakaan orang lain menggunakan jenis itu juga, penelepon dapat memberikan string yang sama kepada kami berdua tanpa ketidaknyamanan atau overhead untuk mengonversi antara representasi yang berbeda, dan itu adalah kemenangan kecil bagi semua orang.Howard sudah menjawab pertanyaan itu dengan baik, dan Nicol membuat beberapa poin bagus tentang manfaat memiliki satu tipe penunjuk bersama standar, daripada banyak yang tidak kompatibel.
Sementara saya sepenuhnya setuju dengan keputusan komite, saya pikir ada beberapa manfaat untuk menggunakan unsynchronized
shared_ptr
jenis -seperti dalam kasus khusus , jadi saya sudah menyelidiki topik beberapa kali.Dengan GCC, saat program Anda tidak menggunakan beberapa utas, shared_ptr tidak menggunakan operasi atomik untuk refcount tersebut. Ini dilakukan dengan memperbarui jumlah referensi melalui fungsi pembungkus yang mendeteksi apakah program multithread (pada GNU / Linux ini dilakukan hanya dengan mendeteksi apakah program terhubung ke
libpthread.so
) dan mengirimkannya ke operasi atom atau non-atomik sesuai.Saya menyadari bertahun-tahun yang lalu bahwa karena GCC
shared_ptr<T>
diimplementasikan dalam hal__shared_ptr<T, _LockPolicy>
kelas dasar , dimungkinkan untuk menggunakan kelas dasar dengan kebijakan penguncian utas tunggal bahkan dalam kode multithread, dengan menggunakan secara eksplisit__shared_ptr<T, __gnu_cxx::_S_single>
. Sayangnya, karena itu bukan kasus penggunaan yang dimaksudkan, ia tidak bekerja secara optimal sebelum GCC 4.9, dan beberapa operasi masih menggunakan fungsi pembungkus sehingga dikirim ke operasi atomik meskipun Anda secara eksplisit meminta_S_single
kebijakan tersebut. Lihat poin (2) di http://gcc.gnu.org/ml/libstdc++/2007-10/msg00180.htmluntuk mengetahui detail selengkapnya dan patch ke GCC untuk memungkinkan implementasi non-atomik digunakan bahkan dalam aplikasi multithread. Saya duduk di tambalan itu selama bertahun-tahun tetapi akhirnya saya berkomitmen untuk GCC 4.9, yang memungkinkan Anda menggunakan templat alias seperti ini untuk menentukan jenis penunjuk bersama yang tidak aman untuk utas, tetapi sedikit lebih cepat:template<typename T> using shared_ptr_unsynchronized = std::__shared_ptr<T, __gnu_cxx::_S_single>;
Jenis ini tidak akan dapat dioperasikan dengan
std::shared_ptr<T>
dan hanya akan aman digunakan jika ada jaminan bahwashared_ptr_unsynchronized
objek tidak akan pernah dibagikan di antara utas tanpa sinkronisasi tambahan yang disediakan pengguna.Ini tentu saja sepenuhnya non-portabel, tetapi terkadang tidak apa-apa. Dengan peretasan preprocessor yang tepat, kode Anda masih akan berfungsi dengan baik dengan implementasi lain jika
shared_ptr_unsynchronized<T>
merupakan aliasshared_ptr<T>
, hanya akan sedikit lebih cepat dengan GCC.Jika Anda menggunakan GCC sebelum versi 4.9, Anda dapat menggunakannya dengan menambahkan
_Sp_counted_base<_S_single>
spesialisasi eksplisit ke kode Anda sendiri (dan memastikan tidak ada yang membuat instance__shared_ptr<T, _S_single>
tanpa menyertakan spesialisasi, untuk menghindari pelanggaran ODR.) Menambahkan spesialisasistd
jenis semacam itu secara teknis tidak ditentukan, tetapi akan berfungsi dalam praktik, karena dalam hal ini tidak ada perbedaan antara saya menambahkan spesialisasi ke GCC atau Anda menambahkannya ke kode Anda sendiri.sumber
std::shared_ptr
,std::__shared_ptr
,__default_lock_policy
dan semacamnya. Jawaban ini menegaskan apa yang saya pahami dari kode itu.Seseorang dapat dengan mudah bertanya mengapa tidak ada penunjuk yang mengganggu, atau sejumlah kemungkinan variasi lain dari penunjuk bersama yang dapat dimiliki.
Desain
shared_ptr
, yang diturunkan dari Boost, telah menciptakan bahasa standar minimum untuk penunjuk pintar. Itu, secara umum, Anda bisa menarik ini dari dinding dan menggunakannya. Itu adalah sesuatu yang akan digunakan secara umum, di berbagai aplikasi. Anda dapat meletakkannya di antarmuka, dan kemungkinan besar orang baik akan mau menggunakannya.Threading hanya akan menjadi lebih umum di masa mendatang. Memang, seiring berjalannya waktu, threading pada umumnya akan menjadi salah satu cara utama untuk mencapai kinerja. Mewajibkan penunjuk cerdas dasar untuk melakukan hal minimum yang diperlukan untuk mendukung penguliran memfasilitasi kenyataan ini.
Membuang setengah lusin pointer pintar dengan variasi kecil di antara mereka ke dalam standar, atau bahkan lebih buruk lagi, pointer pintar berbasis kebijakan, akan sangat buruk. Setiap orang akan memilih penunjuk yang paling mereka sukai dan mengabaikan semua yang lain. Tidak ada yang bisa berkomunikasi dengan orang lain. Ini akan seperti situasi saat ini dengan string C ++, di mana setiap orang memiliki tipenya sendiri. Hanya saja jauh lebih buruk, karena interoperation dengan string jauh lebih mudah daripada interoperation antara kelas-kelas pointer pintar.
Boost, dan panitia, memilih smart pointer tertentu untuk digunakan. Ini memberikan keseimbangan fitur yang baik dan secara luas dan umum digunakan dalam praktik.
std::vector
memiliki beberapa inefisiensi dibandingkan dengan array telanjang dalam beberapa kasus sudut juga. Ini memiliki beberapa batasan; beberapa penggunaan benar-benar ingin memiliki batasan keras pada ukuran avector
, tanpa menggunakan pengalokasi lemparan. Namun, panitia tidak mendesainvector
menjadi segalanya untuk semua orang. Ini dirancang untuk menjadi default yang baik untuk sebagian besar aplikasi. Mereka yang tidak dapat melakukannya dapat menulis alternatif yang sesuai dengan kebutuhan mereka.Seperti yang Anda bisa untuk penunjuk cerdas jika
shared_ptr
atomicity adalah beban. Kemudian lagi, seseorang mungkin juga mempertimbangkan untuk tidak terlalu sering menyalinnya.sumber
Saya sedang mempersiapkan pembicaraan tentang shared_ptr di tempat kerja. Saya telah menggunakan boost shared_ptr yang dimodifikasi dengan menghindari malloc terpisah (seperti yang dapat dilakukan make_shared) dan parameter template untuk kebijakan kunci seperti shared_ptr_unsynchronized yang disebutkan di atas. Saya menggunakan program dari
http://flyingfrogblog.blogspot.hk/2011/01/boosts-sharedptr-up-to-10-slower-than.html
sebagai ujian, setelah membersihkan salinan shared_ptr yang tidak perlu. Program hanya menggunakan utas utama dan argumen uji ditampilkan. Tes env adalah notebook yang menjalankan linuxmint 14. Berikut waktu yang dibutuhkan dalam hitungan detik:
Hanya versi 'std' yang menggunakan -std = cxx11, dan -pthread kemungkinan akan mengganti lock_policy di kelas g ++ __shared_ptr.
Dari angka-angka ini, saya melihat dampak instruksi atom pada pengoptimalan kode. Kasus uji tidak menggunakan wadah C ++ apa pun, tetapi
vector<shared_ptr<some_small_POD>>
kemungkinan akan rusak jika objek tidak memerlukan perlindungan utas. Boost tidak terlalu menderita karena malloc tambahan membatasi jumlah optimisasi kode dan inline.Saya belum menemukan mesin dengan core yang cukup untuk menguji skalabilitas instruksi atom, tetapi menggunakan std :: shared_ptr hanya jika diperlukan mungkin lebih baik.
sumber
Boost memberikan
shared_ptr
yang non-atom. Ini disebutlocal_shared_ptr
, dan dapat ditemukan di pustaka penunjuk cerdas peningkatan.sumber
shared_ptr
dengan konter, meskipun itu lokal? Atau maksud Anda ada masalah lain dengan itu? Para dokter mengatakan bahwa satu - satunya perbedaan adalah bahwa ini bukan atom.local_shared_ptr
danshared_ptr
identik kecuali untuk atom. Saya benar-benar tertarik untuk mengetahui apakah yang Anda katakan itu benar karena saya gunakanlocal_shared_ptr
dalam aplikasi yang membutuhkan kinerja tinggi.