में एक सी ++ अनुकूलन और कोड शैली के बारे में सवाल , कई जवाब की प्रतियां के अनुकूलन के संदर्भ में "एसएसओ" कहा जाता है std::string
। उस संदर्भ में SSO का क्या अर्थ है?
स्पष्ट रूप से "सिंगल साइन ऑन" नहीं। "साझा स्ट्रिंग अनुकूलन", शायद?
में एक सी ++ अनुकूलन और कोड शैली के बारे में सवाल , कई जवाब की प्रतियां के अनुकूलन के संदर्भ में "एसएसओ" कहा जाता है std::string
। उस संदर्भ में SSO का क्या अर्थ है?
स्पष्ट रूप से "सिंगल साइन ऑन" नहीं। "साझा स्ट्रिंग अनुकूलन", शायद?
जवाबों:
स्वचालित चर ("स्टैक से", जो कि आप बिना कॉल किए 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;
};
};
मुझे लगता है कि अधिकांश क्रियान्वयन इस तरह दिखते हैं।
std::string const &
, तो डेटा पर प्राप्त करना एक एकल मेमोरी अप्रत्यक्ष है, क्योंकि डेटा को संदर्भ के स्थान पर संग्रहीत किया जाता है। यदि कोई छोटा स्ट्रिंग अनुकूलन नहीं था, तो डेटा तक पहुंचने के लिए दो मेमोरी इनडायरेक्शन की आवश्यकता होगी (पहले स्ट्रिंग के संदर्भ को लोड करने और इसकी सामग्री को पढ़ने के लिए, फिर स्ट्रिंग में डेटा पॉइंटर की सामग्री को पढ़ने के लिए दूसरा)।
SSO "स्माल स्ट्रिंग स्ट्रिंग ऑप्टिमाइज़ेशन" का संक्षिप्त नाम है, एक ऐसी तकनीक है जहाँ एक अलग से आवंटित बफर का उपयोग करने के बजाय स्ट्रिंग वर्ग के शरीर में छोटे तार एम्बेडेड होते हैं।
जैसा कि पहले से ही अन्य उत्तरों द्वारा समझाया गया है, 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 प्लेटफ़ॉर्म कार्यान्वयन पर निर्भर करता है।
std::string
लागू होता है" पूछता है, और दूसरा पूछता है "एसएसओ का क्या मतलब है", तो आपको उन्हें एक ही सवाल पर विचार करने के लिए बिल्कुल पागल होना होगा