c++ - सम्मिलित करें या एक std के अंत करने के लिए push_back:: वेक्टर?




performance c++11 (3)

वहाँ एक std::vector के अंत में नए तत्वों को सम्मिलित करने के लिए नीचे दो तरीकों के बीच प्रदर्शन में कोई अंतर है std::vector :

विधि 1

std::vector<int> vec = { 1 };
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);

विधि 2

std::vector<int> vec = { 1 };
int arr[] = { 2,3,4,5 };
vec.insert(std::end(vec), std::begin(arr), std::end(arr));

व्यक्तिगत रूप से, मुझे विधि 2 पसंद है क्योंकि यह अच्छा और संक्षिप्त है और एक बार में एक सरणी से सभी नए तत्वों को सम्मिलित करता है। परंतु

  • क्या प्रदर्शन में कोई अंतर है?
  • आखिरकार, वे एक ही काम करते हैं। क्या वे नहीं?

अपडेट करें

कारण है कि मैं सभी तत्वों के साथ वेक्टर की शुरुआत नहीं कर रहा हूं, इसके साथ शुरू करने के लिए, यह है कि मेरे कार्यक्रम में मैं एक शर्त के आधार पर शेष तत्वों को जोड़ रहा हूं।


आखिरकार, वे एक ही काम करते हैं। क्या वे नहीं?

नहीं। वे अलग हैं। std::vector::push_back का उपयोग करने वाली पहली विधि std::vector::insert की तुलना में कई std::vector::push_back से गुजरना होगा।

insert आंतरिक रूप से, वर्तमान std::vector::capacity अनुसार सीमा को कॉपी करने से पहले मेमोरी को आंतरिक रूप से आवंटित करेगा। अधिक के लिए निम्नलिखित चर्चा देखें:

क्या एसटीडी :: वेक्टर :: परिभाषा द्वारा आरक्षित डालें?

लेकिन क्या प्रदर्शन में कोई अंतर है?

ऊपर बताए गए कारण के कारण, दूसरी विधि मामूली प्रदर्शन में सुधार दिखाएगी। उदाहरण के लिए, http://quick-bench.com का उपयोग करके, नीचे का त्वरित बेन्क-चिह्न देखें:

ऑनलाइन बेंच-मार्क देखें

या प्रदर्शन को मापने के लिए एक परीक्षण कार्यक्रम लिखें (जैसा कि @Some प्रोग्रामर ने टिप्पणी में उल्लिखित है)। निम्नलिखित एक नमूना परीक्षण कार्यक्रम है:

vec.insert(std::end(vec), std::begin(arr), std::end(arr));

मेरे सिस्टम के साथ रिलीज़ बिल्डिंग (MSVS2019: / Ox / std: c ++ 17 , AMD Ryzen 7 2700x (8-core, 3.70 Ghz) , x64 Windows 10 )

#include <iostream>
#include <chrono>
#include <algorithm>
#include <vector>
using namespace std::chrono;

class Timer final
{
private:
    time_point<high_resolution_clock> _startTime;

public:
    Timer() noexcept
        : _startTime{ high_resolution_clock::now() }
    {}
    ~Timer() noexcept {  Stop(); }
    void Stop() noexcept
    {
        const auto endTime = high_resolution_clock::now();
        const auto start = time_point_cast<microseconds>(_startTime).time_since_epoch();
        const auto end = time_point_cast<microseconds>(endTime).time_since_epoch();
        const auto durationTaken = end - start;
        const auto duration_ms = durationTaken * 0.001;
        std::cout << durationTaken.count() << "us (" << duration_ms.count() << "ms)\n";
    }
};
// Method 1: push_back
void push_back()
{
    std::cout << "push_backing:    ";
    Timer time{};
    for (auto i{ 0ULL }; i < 1000'000; ++i)
    {
        std::vector<int> vec = { 1 };
        vec.push_back(2);
        vec.push_back(3);
        vec.push_back(4);
        vec.push_back(5);
    }
}
// Method 2: insert_range
void insert_range()
{
    std::cout << "range-inserting: ";
    Timer time{};
    for (auto i{ 0ULL }; i < 1000'000; ++i)
    {
        std::vector<int> vec = { 1 };
        int arr[] = { 2,3,4,5 };
        vec.insert(std::end(vec), std::cbegin(arr), std::cend(arr));
    }
}

int main()
{
    push_back();
    insert_range();
    return 0;
}

जो दिए गए परिदृश्य के लिए दिखाता है, std::vector::insert इन std::vector::push_back से लगभग 2.7 गुना तेज है।

देखें कि अन्य कंपाइलर ( क्लैंग 8.0 और जीसी 9.2 ) उनके कार्यान्वयन के अनुसार क्या कहना चाहते हैं: https://godbolt.org/z/DQrq51


प्रमुख योगदान कारक पुन: आवंटन होने जा रहा है। vector को नए तत्वों के लिए जगह बनाना है।

इन 3 पापपीटों पर गौर कीजिए।

 //pushback
 std::vector<int> vec = {1};
 vec.push_back(2);
 vec.push_back(3);
 vec.push_back(4);
 vec.push_back(5);

 //insert
 std::vector<int> vec = {1};
 int arr[] = {2,3,4,5};
 vec.insert(std::end(vec), std::begin(arr), std::end(arr));


 //cosntruct
 std::vector<int> vec = {1,2,3,4,5};

पुशबैक और इंसर्ट वर्जन में vec.reserve(5) जोड़ने के बाद, चित्र में आने वाले vec.reserve(5) पुष्टि करने के लिए, हम नीचे दिए गए परिणाम प्राप्त करते हैं।


push_back एक एकल तत्व सम्मिलित करता है, इसलिए सबसे खराब स्थिति में आप कई वास्तविकताओं का सामना कर सकते हैं।

उदाहरण के लिए, उस मामले पर विचार करें, जहां प्रारंभिक क्षमता 2 है और प्रत्येक वास्तविककरण पर 2 के कारक से बढ़ता है। फिर

std::vector<int> vec = { 1 }; 
vec.push_back(2);             
vec.push_back(3);                 // need to reallocate, capacity is 4
vec.push_back(4);                   
vec.push_back(5);                  // need to reallocate, capacity is 8

आप निश्चित रूप से कॉल करके अनावश्यक वास्तविकताओं को रोक सकते हैं

vec.reserve(num_elements_to_push);

हालांकि, यदि आप किसी भी प्रकार से एक सरणी से सम्मिलित करते हैं, तो अधिक मुहावरेदार तरीका insert







insert