স্ট্যান্ড :: স্ট্রিংয়ের প্রসঙ্গে সংক্ষিপ্ত বিবরণ এসএসও এর অর্থ


155

ইন একটি সি ++ অপ্টিমাইজেশান এবং কোড শৈলী প্রশ্ন , বিভিন্ন উত্তর কপি নিখুঁত প্রেক্ষাপটে "লগইন SSO" হিসেবে অভিহিত করা std::string। সেই প্রসঙ্গে এসএসওর অর্থ কী?

স্পষ্টতই "একক সাইন অন" নয়। "ভাগ করা স্ট্রিং অপ্টিমাইজেশন", সম্ভবত?


57
এটি কেবল একইভাবে একটি সদৃশ যে "" 2 + 2 "" "200/50 এর ফলাফল কী" এর একটি সদৃশ। উত্তর একই। প্রশ্নটি সম্পূর্ণ আলাদা। "অনুলিপি হিসাবে বন্ধ করুন" ব্যবহার করার উদ্দেশ্য যখন একাধিক লোক একই * প্রশ্ন জিজ্ঞাসা করে। যখন std::stringকোনও ব্যক্তি "কীভাবে প্রয়োগ করা হয়" জিজ্ঞাসা করে , এবং অন্যটি "এসএসও মানে কী" জিজ্ঞাসা করে, আপনাকে তাদের একই প্রশ্ন হিসাবে বিবেচনা করার জন্য একেবারে পাগল হতে হবে
জাল্ফ

1
@ জাল্ফ: যদি একটি বিদ্যমান প্রশ্ন + এ থাকে যা এই প্রশ্নের ক্ষেত্রটি পুরোপুরি বিস্তৃত করে থাকে তবে আমি এটিকে একটি সদৃশ হিসাবে বিবেচনা করব (আমি বলছি না যে ওপি নিজেই এটি অনুসন্ধান করেছিল, কেবল যে এখানে কোনও উত্তরই এই ক্ষেত্রটিকে আবৃত করবে) ইতিমধ্যে কভার করা হয়েছে))
অলিভার চার্লসওয়ার্থ

47
আপনি কার্যকরভাবে ওপিকে বলছেন যে "আপনার প্রশ্নটি ভুল। তবে আপনার কী জিজ্ঞাসা করা উচিত ছিল তা জানতে আপনার উত্তরটি জানতে হবে"। লোককে এসও বন্ধ করার দুর্দান্ত উপায় এটি আপনার প্রয়োজনীয় তথ্য সন্ধান করা অযথা শক্ত করে তোলে। লোকেরা যদি প্রশ্ন না জিজ্ঞাসা করে (এবং সমাপ্তি কার্যকরভাবে "এই প্রশ্নটি জিজ্ঞাসা করা উচিত নয়") বলে থাকে, তবে ইতিমধ্যে উত্তর জানেন না এমন লোকদের পক্ষে এই প্রশ্নের উত্তর পাওয়ার কোনও সম্ভাব্য উপায় নেই
jalf

7
@ জালফ: মোটেও নয়। আইএমও, "ভোট বন্ধ করতে" "খারাপ প্রশ্ন" বোঝায় না। আমি তার জন্য ডাউনভোট ব্যবহার করি। আমি এটিকে একটি সদৃশ হিসাবে বিবেচনা করি যে সমস্ত অগণিত প্রশ্ন (i = i ++ ইত্যাদি) যার উত্তর "অপরিজ্ঞাত আচরণ" একে অপরের সদৃশ। অন্য একটি নোটে, কেন কেউ এই প্রশ্নের সদুত্তর নকল না করে উত্তর দিয়েছে?
অলিভার চার্লসওয়ার্থ

5
@ জালফ: আমি অলির সাথে একমত, প্রশ্নটি সদৃশ নয়, তবে উত্তরটি হবে, সুতরাং উত্তরটি ইতিমধ্যে যথাযথ বলে মনে করা হচ্ছে এমন প্রশ্নে পুনর্নির্দেশ করা। সদৃশ হিসাবে বন্ধ করা প্রশ্নগুলি অদৃশ্য হয়ে যায় না, পরিবর্তে তারা উত্তরটি যেখানে রাখে অন্য প্রশ্নের দিকে পয়েন্টার হিসাবে কাজ করে। এসএসও-র সন্ধানকারী পরবর্তী ব্যক্তিটি এখানেই শেষ হবে, পুনর্নির্দেশটি অনুসরণ করবে এবং তার উত্তর খুঁজে পাবে।
ম্যাথিউ এম।

উত্তর:


212

পটভূমি / ওভারভিউ

স্বয়ংক্রিয় ভেরিয়েবল উপর অপারেশনের (যা ভেরিয়েবল যে আপনাকে কল ছাড়া তৈরি হল "স্ট্যাক থেকে" malloc/ new) দ্বারা সাধারণত অনেক দ্রুত বিনামূল্যে দোকান ( "গাদা", যা ভেরিয়েবল যে ব্যবহার করে তৈরি করা হয় জড়িত তুলনায় new)। তবে সংকলনের সময় স্বয়ংক্রিয় অ্যারেগুলির আকার নির্দিষ্ট করা হলেও ফ্রি স্টোর থেকে অ্যারের আকার হয় না। তদুপরি, স্ট্যাকের আকার সীমাবদ্ধ (সাধারণত কয়েকটি এমআইবি), তবে ফ্রি স্টোরটি কেবলমাত্র আপনার সিস্টেমের স্মৃতি দ্বারা সীমাবদ্ধ।

এসএসও হ'ল সংক্ষিপ্ত / ছোট স্ট্রিং অপটিমাইজেশন। একটি 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;
};

একটি -৪-বিট সিস্টেমের জন্য, এর অর্থ সাধারণত std::stringস্ট্রিং প্রতি 'ওভারহেড' এর 24 বাইট, এবং এসএসও বাফারের জন্য আরও 16 টি (প্যাডিং প্রয়োজনীয়তার কারণে 20 এর পরিবর্তে এখানে 16 বেছে নেওয়া হয়েছে) means আমার সরল উদাহরণ হিসাবে যেমন, এই তিনটি ডেটা সদস্যের সাথে স্থানীয় অক্ষরের স্থানীয় অ্যারে সঞ্চয় করা কোনও অর্থবোধ করবে না। যদি m_size <= 16, তবে আমি সমস্ত ডেটা willুকিয়ে দেব 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

যখন বেশিরভাগ বিকাশকারী স্ট্যান্ড :: রেখাচিত্রমালা রেফারেন্স ব্যবহার করে স্ট্রিং পাস করেন তখন কি এসএসও সত্যই ব্যবহারিক?
গুপ্ত

1
অনুলিপি কম খরচে তৈরি করার বাইরে এসএসওর দুটি সুবিধা রয়েছে। প্রথমটি হ'ল যদি আপনার স্ট্রিং আকারটি ছোট বাফার আকারে ফিট করে তবে আপনাকে প্রাথমিক নির্মাণের জন্য বরাদ্দ দেওয়ার দরকার নেই। দ্বিতীয়টি হ'ল যখন কোনও ফাংশন একটি গ্রহণ করে std::string const &, তখন ডেটা পাওয়া একটি একক মেমরির ইন্ডিरेশন হয়, কারণ তথ্যটি রেফারেন্সের লোকেশনটিতে সংরক্ষণ করা হয়। যদি কোনও ছোট স্ট্রিং অপ্টিমাইজেশন না থাকে তবে ডেটা অ্যাক্সেসের জন্য দুটি মেমরি ইন্ডায়ার্কেশন প্রয়োজন হয় (প্রথমে স্ট্রিংয়ের রেফারেন্সটি লোড করতে এবং এর বিষয়বস্তু পড়তে হবে, তারপরে দ্বিতীয় স্ট্রিংয়ে ডেটা পয়েন্টারের সামগ্রীগুলি পড়তে হবে)।
ডেভিড স্টোন

34

এসএসও হ'ল "ছোট স্ট্রিং অপটিমাইজেশন" এর সংক্ষেপণ, একটি কৌশল যেখানে ছোট স্ট্রিংগুলি পৃথকভাবে বরাদ্দকৃত বাফার ব্যবহার না করে স্ট্রিং ক্লাসের শরীরে এম্বেড করা থাকে।


15

অন্যান্য উত্তর দ্বারা ইতিমধ্যে ব্যাখ্যা করা হয়েছে, এসএসওর অর্থ ছোট / শর্ট স্ট্রিং অপটিমাইজেশন । এই অপ্টিমাইজেশনের পিছনে অনুপ্রেরণা হ'ল অনস্বীকার্য প্রমাণ যা সাধারণভাবে অ্যাপ্লিকেশনগুলি দীর্ঘতর স্ট্রিংয়ের চেয়ে অনেক বেশি সংক্ষিপ্ত স্ট্রিং পরিচালনা করে।

ডেভিড স্টোন তার উপরের উত্তরে যেমনটি ব্যাখ্যা করেছেন , std::stringশ্রেণি একটি নির্দিষ্ট দৈর্ঘ্য পর্যন্ত সামগ্রী সংরক্ষণের জন্য একটি অভ্যন্তরীণ বাফার ব্যবহার করে এবং এটি মেমরিকে গতিশীলভাবে বরাদ্দ দেওয়ার প্রয়োজনকে সরিয়ে দেয়। এটি কোডটিকে আরও কার্যকর এবং দ্রুত করে তোলে ।

এই অন্যান্য সম্পর্কিত উত্তর পরিষ্কারভাবে দেখায় যে অভ্যন্তরীণ বাফারের আকার std::stringবাস্তবায়নের উপর নির্ভর করে , যা প্ল্যাটফর্ম থেকে প্ল্যাটফর্মে পরিবর্তিত হয় (নীচে বেঞ্চমার্ক ফলাফল দেখুন)।

benchmarks

এখানে একটি ছোট প্রোগ্রাম যা একই দৈর্ঘ্যের সাথে প্রচুর স্ট্রিংয়ের অনুলিপি অপারেশনকে বেনমার্ক করে। এটি দৈর্ঘ্য = 1 দিয়ে 10 মিলিয়ন স্ট্রিংগুলি অনুলিপি করার জন্য সময় মুদ্রণ শুরু করে Then এরপরে এটি দৈর্ঘ্যের স্ট্রিংগুলির সাথে পুনরাবৃত্তি করে = 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সেগুলি কনসোলে প্রদর্শিত হবে।

আমি আমার ম্যাকবুক এবং উবুন্টু মেশিনগুলি থেকে আউটপুট দিয়ে চার্ট তৈরি করেছি। নোট করুন যে দৈর্ঘ্য যখন একটি নির্দিষ্ট বিন্দুতে পৌঁছে তখন স্ট্রিংগুলি অনুলিপি করার জন্য একটি বিশাল জাম্প রয়েছে jump সেই মুহুর্তে যখন স্ট্রিংগুলি অভ্যন্তরীণ বাফারে আর ফিট করে না এবং মেমরি বরাদ্দ ব্যবহার করতে হয়।

এছাড়াও লক্ষ করুন যে লিনাক্স মেশিনে, স্ট্রিংটির দৈর্ঘ্য 16 এ পৌঁছলে লাফটি ঘটে।

উবুন্টু উবুন্টুতে এসএসও বেঞ্চমার্ক

ম্যাকবুক প্রো ম্যাকবুক প্রোতে এসএসও বেঞ্চমার্ক

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.