Wadah STL mana yang paling sesuai dengan kebutuhan saya? Saya pada dasarnya memiliki wadah lebar 10 elemen di mana saya terus push_back
elemen baru sementara pop_front
elemen tertua (sekitar satu juta kali).
Saat ini saya menggunakan std::deque
untuk tugas tersebut tetapi bertanya-tanya apakah a std::list
akan lebih efisien karena saya tidak perlu mengalokasikan ulang sendiri (atau mungkin saya salah mengira std::deque
a std::vector
?). Atau adakah wadah yang lebih efisien untuk kebutuhan saya?
PS Saya tidak perlu akses acak
std::deque
tidak akan dialokasikan kembali. Ini adalah hibrida dari astd::list
dan distd::vector
mana ia mengalokasikan potongan yang lebih besar daripada astd::list
tetapi tidak akan dialokasikan seperti astd::vector
.Jawaban:
Karena ada banyak sekali jawaban, Anda mungkin bingung, tetapi untuk diringkas:
Gunakan a
std::queue
. Alasannya sederhana: ini adalah struktur FIFO. Anda menginginkan FIFO, Anda menggunakan filestd::queue
.Itu membuat niat Anda jelas bagi orang lain, dan bahkan diri Anda sendiri. A
std::list
ataustd::deque
tidak. Sebuah daftar dapat disisipkan dan dihapus di mana saja, yang tidak seharusnya dilakukan oleh struktur FIFO, dandeque
dapat menambah dan menghapus dari kedua ujungnya, yang juga merupakan sesuatu yang tidak dapat dilakukan oleh struktur FIFO.Inilah mengapa Anda harus menggunakan file
queue
.Sekarang, Anda bertanya tentang kinerja. Pertama, selalu ingat aturan praktis yang penting ini: Kode bagus dulu, performa terakhir.
Alasannya sederhana: orang yang mengupayakan penampilan sebelum kebersihan dan keanggunan hampir selalu menjadi yang terakhir. Kode mereka menjadi omong kosong, karena mereka telah meninggalkan semua yang baik untuk benar-benar mendapatkan apa-apa darinya.
Dengan menulis kode yang bagus dan dapat dibaca terlebih dahulu, sebagian besar masalah kinerja Anda akan teratasi sendiri. Dan jika nanti Anda menemukan kinerja Anda kurang, sekarang mudah untuk menambahkan profiler ke kode Anda yang bagus dan bersih, dan mencari tahu di mana masalahnya.
Itu semua dikatakan,
std::queue
hanya adaptor. Ini menyediakan antarmuka yang aman, tetapi menggunakan wadah yang berbeda di dalam. Anda dapat memilih wadah dasar ini, dan ini memungkinkan banyak fleksibilitas.Jadi, wadah dasar mana yang harus Anda gunakan? Kita tahu bahwa
std::list
danstd::deque
keduanya memberikan fungsi yang diperlukan (push_back()
,pop_front()
, danfront()
), jadi bagaimana kita memutuskan?Pertama, pahami bahwa mengalokasikan (dan membatalkan alokasi) memori bukanlah hal yang cepat untuk dilakukan, umumnya, karena melibatkan keluar ke OS dan memintanya untuk melakukan sesuatu. A
list
harus mengalokasikan memori setiap kali ada sesuatu yang ditambahkan, dan mengalokasikannya ketika hilang.A
deque
, di sisi lain, mengalokasikan dalam potongan. Ini akan mengalokasikan lebih jarang dari alist
. Anggap saja sebagai daftar, tetapi setiap bagian memori dapat menampung banyak node. (Tentu saja, saya menyarankan agar Anda benar-benar mempelajari cara kerjanya .)Jadi, dengan itu saja a
deque
harus berkinerja lebih baik, karena tidak sering berurusan dengan memori. Dicampur dengan fakta Anda menangani data dengan ukuran konstan, mungkin tidak perlu mengalokasikan setelah melewati pertama melalui data, sedangkan daftar akan terus mengalokasikan dan membatalkan alokasi.Hal kedua yang perlu dipahami adalah kinerja cache . Keluar ke RAM itu lambat, jadi ketika CPU benar-benar membutuhkannya, itu memanfaatkan yang terbaik dari waktu ini dengan mengambil sebagian memori bersamanya, ke dalam cache. Karena a
deque
mengalokasikan dalam potongan memori, kemungkinan mengakses elemen dalam penampung ini akan menyebabkan CPU mengembalikan sisa penampungnya juga. Sekarang akses lebih lanjut kedeque
akan lebih cepat, karena datanya ada dalam cache.Ini tidak seperti daftar, di mana data dialokasikan satu per satu. Ini berarti bahwa data dapat tersebar di semua tempat di memori, dan kinerja cache akan menjadi buruk.
Jadi, mengingat itu,
deque
seharusnya menjadi pilihan yang lebih baik. Inilah mengapa ini adalah penampung default saat menggunakan filequeue
. Meskipun demikian, ini masih hanya tebakan (sangat) terpelajar: Anda harus membuat profil kode ini, menggunakandeque
tes dalam satu dan teslist
lain untuk benar-benar mengetahui dengan pasti.Tapi ingat: dapatkan kode yang berfungsi dengan antarmuka yang bersih, lalu khawatirkan performa.
John menimbulkan kekhawatiran bahwa membungkus
list
ataudeque
akan menyebabkan penurunan kinerja. Sekali lagi, dia dan saya tidak dapat mengatakan dengan pasti tanpa membuat profilnya sendiri, tetapi kemungkinan besar kompilator akan menyebariskan panggilan yangqueue
dibuat. Artinya, ketika Anda mengatakanqueue.push()
, itu benar-benar hanya akan mengatakanqueue.container.push_back()
, melewatkan panggilan fungsi sepenuhnya.Sekali lagi, ini hanya tebakan, tetapi menggunakan
queue
tidak akan menurunkan kinerja, jika dibandingkan dengan menggunakan wadah mentah yang mendasarinya. Seperti yang saya katakan sebelumnya, gunakanqueue
, karena bersih, mudah digunakan, dan aman, dan jika benar-benar menjadi profil masalah dan uji.sumber
Lihat
std::queue
. Ini membungkus jenis penampung yang mendasari, dan penampung default adalahstd::deque
.sumber
queue
apakah tipe itu. Kode bagus dulu, performa nanti. Sial, sebagian besar kinerja keluar dari penggunaan kode yang baik di tempat pertama.queue
tidak akan meningkatkan kinerja, seperti yang telah saya katakan. Anda menyarankan alist
, yang mungkin akan berkinerja lebih buruk.Jika kinerja sangat penting, lihat pustaka buffer melingkar Boost .
sumber
Sejuta bukanlah angka yang besar dalam komputasi. Seperti yang disarankan orang lain, gunakan a
std::queue
sebagai solusi pertama Anda. Jika terjadi terlalu lambat, identifikasi hambatan menggunakan profiler (jangan tebak!) Dan implementasikan ulang menggunakan penampung berbeda dengan antarmuka yang sama.sumber
Mengapa tidak
std::queue
? Semua yang dimilikinya adalahpush_back
danpop_front
.sumber
Sebuah antrian mungkin antarmuka sederhana daripada deque tapi untuk seperti daftar kecil, perbedaan kinerja mungkin diabaikan.
Sama halnya dengan daftar . Ini tergantung pada pilihan API yang Anda inginkan.
sumber
Gunakan a
std::queue
, tetapi perhatikan pengorbanan kinerja dari duaContainer
kelas standar .Secara default,
std::queue
adalah adaptor di atasstd::deque
. Biasanya, itu akan memberikan kinerja yang baik di mana Anda memiliki sejumlah kecil antrian yang berisi banyak entri, yang bisa dibilang kasus umum.Namun, jangan buta terhadap implementasi std :: deque . Secara khusus:
"... deques biasanya memiliki biaya memori minimal yang besar; deque yang hanya menampung satu elemen harus mengalokasikan array internal penuhnya (misalnya 8 kali ukuran objek pada 64-bit libstdc ++; 16 kali ukuran objek atau 4096 byte, mana saja yang lebih besar , di 64-bit libc ++). "
Untuk menjernihkannya, anggaplah bahwa entri antrian adalah sesuatu yang Anda ingin antri, misalnya ukurannya cukup kecil, maka jika Anda memiliki 4 antrian, masing-masing berisi 30.000 entri,
std::deque
implementasi akan menjadi opsi pilihan. Sebaliknya, jika Anda memiliki 30.000 antrian, masing-masing berisi 4 entri, maka kemungkinan besarstd::list
penerapannya akan optimal, karena Anda tidak akan pernah mengamortisasistd::deque
biaya overhead dalam skenario itu.Anda akan membaca banyak opini tentang bagaimana cache adalah raja, bagaimana Stroustrup membenci daftar tertaut, dll., Dan semua itu benar, dalam kondisi tertentu. Hanya saja, jangan menerimanya dengan keyakinan buta, karena dalam skenario kedua kami di sana, sangat tidak mungkin
std::deque
implementasi default akan berfungsi. Evaluasi penggunaan dan ukuran Anda.sumber
Kasus ini cukup sederhana sehingga Anda bisa menuliskannya sendiri. Ini adalah sesuatu yang bekerja dengan baik untuk situasi mikrokontroler di mana penggunaan STL memakan terlalu banyak ruang. Ini adalah cara yang bagus untuk melewatkan data dan sinyal dari penangan interupsi ke loop utama Anda.
// FIFO with circular buffer #define fifo_size 4 class Fifo { uint8_t buff[fifo_size]; int writePtr = 0; int readPtr = 0; public: void put(uint8_t val) { buff[writePtr%fifo_size] = val; writePtr++; } uint8_t get() { uint8_t val = NULL; if(readPtr < writePtr) { val = buff[readPtr%fifo_size]; readPtr++; // reset pointers to avoid overflow if(readPtr > fifo_size) { writePtr = writePtr%fifo_size; readPtr = readPtr%fifo_size; } } return val; } int count() { return (writePtr - readPtr);} };
sumber