ইন একটি সি ++ অপ্টিমাইজেশান এবং কোড শৈলী প্রশ্ন , বিভিন্ন উত্তর কপি নিখুঁত প্রেক্ষাপটে "লগইন SSO" হিসেবে অভিহিত করা std::string
। সেই প্রসঙ্গে এসএসওর অর্থ কী?
স্পষ্টতই "একক সাইন অন" নয়। "ভাগ করা স্ট্রিং অপ্টিমাইজেশন", সম্ভবত?
ইন একটি সি ++ অপ্টিমাইজেশান এবং কোড শৈলী প্রশ্ন , বিভিন্ন উত্তর কপি নিখুঁত প্রেক্ষাপটে "লগইন SSO" হিসেবে অভিহিত করা std::string
। সেই প্রসঙ্গে এসএসওর অর্থ কী?
স্পষ্টতই "একক সাইন অন" নয়। "ভাগ করা স্ট্রিং অপ্টিমাইজেশন", সম্ভবত?
উত্তর:
স্বয়ংক্রিয় ভেরিয়েবল উপর অপারেশনের (যা ভেরিয়েবল যে আপনাকে কল ছাড়া তৈরি হল "স্ট্যাক থেকে" 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;
};
};
আমি ধরে নিই যে বেশিরভাগ বাস্তবায়নগুলি আরও দেখতে এরকম দেখাচ্ছে।
std::string const &
, তখন ডেটা পাওয়া একটি একক মেমরির ইন্ডিरेশন হয়, কারণ তথ্যটি রেফারেন্সের লোকেশনটিতে সংরক্ষণ করা হয়। যদি কোনও ছোট স্ট্রিং অপ্টিমাইজেশন না থাকে তবে ডেটা অ্যাক্সেসের জন্য দুটি মেমরি ইন্ডায়ার্কেশন প্রয়োজন হয় (প্রথমে স্ট্রিংয়ের রেফারেন্সটি লোড করতে এবং এর বিষয়বস্তু পড়তে হবে, তারপরে দ্বিতীয় স্ট্রিংয়ে ডেটা পয়েন্টারের সামগ্রীগুলি পড়তে হবে)।
এসএসও হ'ল "ছোট স্ট্রিং অপটিমাইজেশন" এর সংক্ষেপণ, একটি কৌশল যেখানে ছোট স্ট্রিংগুলি পৃথকভাবে বরাদ্দকৃত বাফার ব্যবহার না করে স্ট্রিং ক্লাসের শরীরে এম্বেড করা থাকে।
অন্যান্য উত্তর দ্বারা ইতিমধ্যে ব্যাখ্যা করা হয়েছে, এসএসওর অর্থ ছোট / শর্ট স্ট্রিং অপটিমাইজেশন । এই অপ্টিমাইজেশনের পিছনে অনুপ্রেরণা হ'ল অনস্বীকার্য প্রমাণ যা সাধারণভাবে অ্যাপ্লিকেশনগুলি দীর্ঘতর স্ট্রিংয়ের চেয়ে অনেক বেশি সংক্ষিপ্ত স্ট্রিং পরিচালনা করে।
ডেভিড স্টোন তার উপরের উত্তরে যেমনটি ব্যাখ্যা করেছেন , std::string
শ্রেণি একটি নির্দিষ্ট দৈর্ঘ্য পর্যন্ত সামগ্রী সংরক্ষণের জন্য একটি অভ্যন্তরীণ বাফার ব্যবহার করে এবং এটি মেমরিকে গতিশীলভাবে বরাদ্দ দেওয়ার প্রয়োজনকে সরিয়ে দেয়। এটি কোডটিকে আরও কার্যকর এবং দ্রুত করে তোলে ।
এই অন্যান্য সম্পর্কিত উত্তর পরিষ্কারভাবে দেখায় যে অভ্যন্তরীণ বাফারের আকার std::string
বাস্তবায়নের উপর নির্ভর করে , যা প্ল্যাটফর্ম থেকে প্ল্যাটফর্মে পরিবর্তিত হয় (নীচে বেঞ্চমার্ক ফলাফল দেখুন)।
এখানে একটি ছোট প্রোগ্রাম যা একই দৈর্ঘ্যের সাথে প্রচুর স্ট্রিংয়ের অনুলিপি অপারেশনকে বেনমার্ক করে। এটি দৈর্ঘ্য = 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 এ পৌঁছলে লাফটি ঘটে।
std::string
কোনও ব্যক্তি "কীভাবে প্রয়োগ করা হয়" জিজ্ঞাসা করে , এবং অন্যটি "এসএসও মানে কী" জিজ্ঞাসা করে, আপনাকে তাদের একই প্রশ্ন হিসাবে বিবেচনা করার জন্য একেবারে পাগল হতে হবে