एसएसडी का अर्थ एसटीडी के संदर्भ में: स्ट्रिंग


155

में एक सी ++ अनुकूलन और कोड शैली के बारे में सवाल , कई जवाब की प्रतियां के अनुकूलन के संदर्भ में "एसएसओ" कहा जाता है std::string। उस संदर्भ में SSO का क्या अर्थ है?

स्पष्ट रूप से "सिंगल साइन ऑन" नहीं। "साझा स्ट्रिंग अनुकूलन", शायद?


57
यह केवल उसी तरह से एक डुप्लिकेट है कि "2 + 2 क्या है" "200/50 का परिणाम क्या है" का डुप्लिकेट है। उत्तर वही है। सवाल बिल्कुल अलग है। "डुप्लिकेट के रूप में बंद करें" का उपयोग तब किया जाता है जब कई लोग एक ही * प्रश्न पूछते हैं। जब एक व्यक्ति "कैसे std::stringलागू होता है" पूछता है, और दूसरा पूछता है "एसएसओ का क्या मतलब है", तो आपको उन्हें एक ही सवाल पर विचार करने के लिए बिल्कुल पागल होना होगा
जालिम

1
@ जैलफ: यदि कोई मौजूदा क्यू + ए है जो वास्तव में इस प्रश्न के दायरे को शामिल करता है, तो मैं इसे एक डुप्लिकेट मानता हूं (मैं यह नहीं कह रहा हूं कि ओपी को खुद इस के लिए खोज करनी चाहिए, बस यह है कि यहां कोई भी उत्तर जमीन को कवर करेगा। पहले से ही कवर किया गया।)
ओलिवर चार्ल्सवर्थ

47
आप प्रभावी रूप से ओपी को बता रहे हैं कि "आपका प्रश्न गलत है। लेकिन आपको यह जानने के लिए उत्तर की आवश्यकता है कि आपको क्या पूछना चाहिए था"। एसओ को लोगों को बंद करने का अच्छा तरीका। यह आपके लिए आवश्यक जानकारी को खोजने के लिए अनावश्यक रूप से कठिन भी बनाता है। यदि लोग सवाल नहीं पूछते (और समापन प्रभावी रूप से कह रहा है "यह सवाल नहीं पूछा जाना चाहिए था"), तो इस सवाल का जवाब पाने के लिए उन लोगों के लिए कोई संभव तरीका नहीं होगा जो पहले से ही जवाब नहीं जानते हैं
jalf

7
@ जैलफ: बिल्कुल नहीं। IMO, "वोट टू क्लोज़" का अर्थ "खराब प्रश्न" नहीं है। मैं उस के लिए downvotes का उपयोग करें। मैं इसे इस अर्थ में डुप्लिकेट मानता हूं कि सभी असंख्य प्रश्न (i = i ++, आदि) जिनका उत्तर "अपरिभाषित व्यवहार" है, एक दूसरे के डुप्लिकेट हैं। एक अलग नोट पर, किसी ने इस सवाल का जवाब क्यों नहीं दिया है अगर यह डुप्लिकेट नहीं है?
ओलिवर चार्ल्सवर्थ

5
@ जैलफ: मैं ओली से सहमत हूं, सवाल कोई डुप्लिकेट नहीं है, लेकिन इसका जवाब होगा, इसलिए एक और सवाल पर रीडायरेक्ट करना जहां पहले से ही उत्तर उपयुक्त हैं। डुप्लिकेट के रूप में बंद किए गए प्रश्न गायब नहीं होते हैं, इसके बजाय वे दूसरे प्रश्न की ओर संकेत के रूप में कार्य करते हैं जहां उत्तर देता है। एसएसओ की तलाश करने वाला अगला व्यक्ति यहां समाप्त होगा, पुनर्निर्देशन का पालन करेगा, और उसका उत्तर ढूंढेगा।
मैथ्यू एम।

जवाबों:


212

पृष्ठभूमि / अवलोकन

स्वचालित चर ("स्टैक से", जो कि आप बिना कॉल किए malloc/ बनाये हुए newहैं) पर परिचालन आम तौर पर मुफ्त स्टोर ("हीप", जो कि उपयोग किए जाने वाले चर होते हैं new) को शामिल करने की तुलना में बहुत तेजी से होता है । हालाँकि, स्वचालित सरणियों का आकार संकलन समय पर निर्धारित किया जाता है, लेकिन मुक्त स्टोर से सरणियों का आकार नहीं है। इसके अलावा, स्टैक का आकार सीमित है (आमतौर पर कुछ MiB), जबकि फ्री स्टोर केवल आपके सिस्टम की मेमोरी द्वारा सीमित है।

SSO लघु / लघु स्ट्रिंग अनुकूलन है। एक std::stringआम तौर पर मुक्त दुकान ( "ढेर"), जो इसी तरह के प्रदर्शन विशेषताओं देता है के रूप में यदि आप कॉल के लिए गए थे के सूचक के रूप स्ट्रिंग संग्रहीत करता है new char [size]। यह बहुत बड़े स्ट्रिंग्स के लिए स्टैक ओवरफ्लो को रोकता है, लेकिन यह धीमा हो सकता है, खासकर कॉपी ऑपरेशन के साथ। अनुकूलन के रूप में, std::stringएक छोटे स्वचालित सरणी बनाने के कई कार्यान्वयन , कुछ इस तरह char [20]। यदि आपके पास एक स्ट्रिंग है जो 20 वर्ण या उससे छोटा है (इस उदाहरण को देखते हुए, वास्तविक आकार भिन्न होता है), यह सीधे उस सरणी में संग्रहीत करता है। यह newबिल्कुल भी कॉल करने से बचता है , जो चीजों को थोड़ा बढ़ा देता है।

संपादित करें:

मैं इस उत्तर की इतनी अधिक लोकप्रियता की उम्मीद नहीं कर रहा था, लेकिन चूंकि यह है, मुझे अधिक यथार्थवादी कार्यान्वयन देने दें, इस चेतावनी के साथ कि मैंने वास्तव में एसएसओ के किसी भी कार्यान्वयन को "जंगली में" कभी नहीं पढ़ा है।

कार्यान्वयन का विवरण

न्यूनतम पर, std::stringनिम्न जानकारी संग्रहीत करने की आवश्यकता है:

  • आकार
  • क्षमता
  • डेटा का स्थान

आकार std::string::size_typeको एक संकेतक के रूप में या अंत तक संग्रहीत किया जा सकता है । अंतर केवल इतना है कि क्या आप उपयोगकर्ता को कॉल करने पर दो पॉइंटर्स को घटाना चाहते हैं sizeया size_typeजब उपयोगकर्ता कॉल करता है, तो एक पॉइंटर जोड़ देता है end। क्षमता को किसी भी तरह से संग्रहीत किया जा सकता है।

आप जो उपयोग नहीं करते हैं उसके लिए आप भुगतान नहीं करते हैं।

सबसे पहले, ऊपर बताए गए के आधार पर अनुभवहीन कार्यान्वयन पर विचार करें:

class string {
public:
    // all 83 member functions
private:
    std::unique_ptr<char[]> m_data;
    size_type m_size;
    size_type m_capacity;
    std::array<char, 16> m_sso;
};

64-बिट सिस्टम के लिए, आमतौर पर इसका मतलब है कि std::stringप्रति स्ट्रिंग 'ओवरहेड' के 24 बाइट्स हैं, और एसएसओ बफर के लिए एक और 16 (पैडिंग आवश्यकताओं के कारण 20 के बजाय यहां चुना गया 16)। यह वास्तव में उन तीन डेटा सदस्यों और पात्रों के एक स्थानीय सरणी को संग्रहीत करने के लिए समझ में नहीं आएगा, जैसा कि मेरे सरलीकृत उदाहरण में। अगर m_size <= 16, तो मैं सारा डेटा अंदर डाल दूंगा m_sso, इसलिए मुझे पहले से ही क्षमता पता है और मुझे डेटा के लिए पॉइंटर की जरूरत नहीं है। तो m_size > 16, तो मैं की जरूरत नहीं है m_sso। पूरी तरह से कोई ओवरलैप नहीं है जहां मुझे उन सभी की आवश्यकता है। एक होशियार समाधान जो कोई जगह नहीं बर्बाद करता है, उसे कुछ अधिक दिखाई देगा (अप्रकाशित, उदाहरण के उद्देश्य केवल):

class string {
public:
    // all 83 member functions
private:
    size_type m_size;
    union {
        class {
            // This is probably better designed as an array-like class
            std::unique_ptr<char[]> m_data;
            size_type m_capacity;
        } m_large;
        std::array<char, sizeof(m_large)> m_small;
    };
};

मुझे लगता है कि अधिकांश क्रियान्वयन इस तरह दिखते हैं।


7
यहाँ कुछ वास्तविक कार्यान्वयन की एक अच्छी व्याख्या है: stackoverflow.com/a/28003328/203044
BillT

क्या SSO वास्तव में व्यावहारिक है जब अधिकांश डेवलपर्स std :: string को const reference का उपयोग करते हैं?
गुप्ता

1
SSO को नकल को सस्ता बनाने से परे दो लाभ हैं। पहला यह है कि यदि आपका स्ट्रिंग आकार छोटे बफर आकार में फिट बैठता है, तो आपको प्रारंभिक निर्माण पर आवंटित करने की आवश्यकता नहीं है। दूसरा यह है कि जब कोई फ़ंक्शन स्वीकार करता है std::string const &, तो डेटा पर प्राप्त करना एक एकल मेमोरी अप्रत्यक्ष है, क्योंकि डेटा को संदर्भ के स्थान पर संग्रहीत किया जाता है। यदि कोई छोटा स्ट्रिंग अनुकूलन नहीं था, तो डेटा तक पहुंचने के लिए दो मेमोरी इनडायरेक्शन की आवश्यकता होगी (पहले स्ट्रिंग के संदर्भ को लोड करने और इसकी सामग्री को पढ़ने के लिए, फिर स्ट्रिंग में डेटा पॉइंटर की सामग्री को पढ़ने के लिए दूसरा)।
डेविड स्टोन

34

SSO "स्माल स्ट्रिंग स्ट्रिंग ऑप्टिमाइज़ेशन" का संक्षिप्त नाम है, एक ऐसी तकनीक है जहाँ एक अलग से आवंटित बफर का उपयोग करने के बजाय स्ट्रिंग वर्ग के शरीर में छोटे तार एम्बेडेड होते हैं।


15

जैसा कि पहले से ही अन्य उत्तरों द्वारा समझाया गया है, SSO का अर्थ है स्माल / शॉर्ट स्ट्रिंग ऑप्टिमाइज़ेशन । इस अनुकूलन के पीछे प्रेरणा निर्विवाद सबूत है कि सामान्य रूप से अनुप्रयोग लंबे तार की तुलना में बहुत अधिक छोटे तारों को संभालते हैं।

जैसा कि ऊपर दिए गए अपने उत्तर में डेविड स्टोन ने बताया है , std::stringवर्ग किसी दिए गए लंबाई तक सामग्री को संग्रहीत करने के लिए एक आंतरिक बफर का उपयोग करता है, और यह स्मृति को गतिशील रूप से आवंटित करने की आवश्यकता को समाप्त करता है। यह कोड को अधिक कुशल और तेज बनाता है

यह अन्य संबंधित उत्तर स्पष्ट रूप से दिखाता है कि आंतरिक बफर का आकार std::stringकार्यान्वयन पर निर्भर करता है , जो प्लेटफ़ॉर्म से प्लेटफ़ॉर्म पर भिन्न होता है (नीचे बेंचमार्क परिणाम देखें)।

मानक

यहां एक छोटा सा कार्यक्रम है जो एक ही लंबाई के साथ बहुत सारे तार के कॉपी ऑपरेशन को बेंचमार्क करता है। यह लंबाई = 1. के साथ 10 मिलियन स्ट्रिंग्स को कॉपी करने के लिए समय प्रिंट करना शुरू कर देता है। फिर यह लंबाई के स्ट्रिंग्स के साथ दोहराता है = 2. यह तब तक चलता रहता है जब तक कि लंबाई 50 नहीं हो जाती।

#include <string>
#include <iostream>
#include <vector>
#include <chrono>

static const char CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static const int ARRAY_SIZE = sizeof(CHARS) - 1;

static const int BENCHMARK_SIZE = 10000000;
static const int MAX_STRING_LENGTH = 50;

using time_point = std::chrono::high_resolution_clock::time_point;

void benchmark(std::vector<std::string>& list) {
    std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

    // force a copy of each string in the loop iteration
    for (const auto s : list) {
        std::cout << s;
    }

    std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();
    const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
    std::cerr << list[0].length() << ',' << duration << '\n';
}

void addRandomString(std::vector<std::string>& list, const int length) {
    std::string s(length, 0);
    for (int i = 0; i < length; ++i) {
        s[i] = CHARS[rand() % ARRAY_SIZE];
    }
    list.push_back(s);
}

int main() {
    std::cerr << "length,time\n";

    for (int length = 1; length <= MAX_STRING_LENGTH; length++) {
        std::vector<std::string> list;
        for (int i = 0; i < BENCHMARK_SIZE; i++) {
            addRandomString(list, length);
        }
        benchmark(list);
    }

    return 0;
}

यदि आप इस प्रोग्राम को चलाना चाहते हैं, तो आपको इसे ऐसे करना चाहिए ./a.out > /dev/nullताकि स्ट्रिंग्स को प्रिंट करने का समय गिना न जाए। जो संख्याएं मायने रखती हैं stderr, वे मुद्रित हैं , इसलिए वे कंसोल में दिखाई देंगे।

मैंने अपने मैकबुक और उबंटू मशीनों से आउटपुट के साथ चार्ट बनाए हैं। ध्यान दें कि स्ट्रिंग्स को कॉपी करने के लिए समय में भारी उछाल है जब लंबाई किसी दिए गए बिंदु तक पहुंचती है। यही वह क्षण है जब तार आंतरिक बफर में फिट नहीं होते हैं और मेमोरी आवंटन का उपयोग करना पड़ता है।

यह भी ध्यान दें कि linux मशीन पर, जंप तब होता है जब स्ट्रिंग की लंबाई 16 तक पहुँच जाती है। मैकबुक पर, जंप तब होता है जब लंबाई 23 तक पहुँच जाती है। यह पुष्टि करता है कि SSO प्लेटफ़ॉर्म कार्यान्वयन पर निर्भर करता है।

उबंटू Ubuntu पर SSO बेंचमार्क

मैकबुक प्रो मैकबुक प्रो पर एसएसओ बेंचमार्क

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.