Libc ++ এ শর্ট স্ট্রিং অপ্টিমাইজেশনের যান্ত্রিকগুলি কী কী?


106

এই উত্তরটি সংক্ষিপ্ত স্ট্রিং অপ্টিমাইজেশনের (এসএসও) একটি দুর্দান্ত উচ্চ-স্তরের ওভারভিউ দেয়। তবে আমি আরও বিশদে জানতে চাই যে এটি অনুশীলনে কীভাবে কাজ করে, বিশেষত libc ++ বাস্তবায়নে:

  • এসএসওর জন্য যোগ্যতার জন্য স্ট্রিংটি কত সংক্ষিপ্ত হতে হবে? এটি কি লক্ষ্য আর্কিটেকচারের উপর নির্ভর করে?

  • স্ট্রিং ডেটা অ্যাক্সেস করার সময় বাস্তবায়ন কীভাবে সংক্ষিপ্ত এবং দীর্ঘ স্ট্রিংয়ের মধ্যে পার্থক্য করে? এটি কি এতটা সহজ m_size <= 16বা এটি একটি পতাকা যা অন্য কোনও সদস্যের পরিবর্তকের অংশ? (আমি কল্পনা করি যে m_sizeবা এর কিছু অংশ স্ট্রিং ডেটা সঞ্চয় করার জন্যও ব্যবহৃত হতে পারে)।

আমি এই প্রশ্নটি বিশেষত লাইবসি ++ এর জন্য জিজ্ঞাসা করেছি কারণ আমি জানি যে এটি এসএসও ব্যবহার করে, এটি এমনকি লিবিসি ++ হোম পৃষ্ঠায় উল্লেখ করা হয়েছে ।

উত্সটি দেখার পরে এখানে কিছু পর্যবেক্ষণ রয়েছে :

libc ++ স্ট্রিং ক্লাসের জন্য দুটি সামান্য ভিন্ন মেমরি লেআউটগুলির সাথে সংকলন করা যায়, এটি _LIBCPP_ALTERNATE_STRING_LAYOUTপতাকা দ্বারা নিয়ন্ত্রিত হয় । উভয় বিন্যাসই ছোট-এন্ডিয়ান এবং বিগ-এন্ডিয়ান মেশিনগুলির মধ্যে পার্থক্য করে যা আমাদের মোট 4 টি ভিন্ন রূপ দেয়। আমি নিম্নলিখিত হিসাবে "স্বাভাবিক" লেআউট এবং লিটল-এন্ডিয়ান অনুমান করব।

আরও ধরে নিচ্ছি যে size_type4 বাইট এবং এটি value_type1 বাইট, এটি স্ট্রিংয়ের প্রথম 4 বাইট স্মৃতিতে দেখায়:

// short string: (s)ize and 3 bytes of char (d)ata
sssssss0;dddddddd;dddddddd;dddddddd
       ^- is_long = 0

// long string: (c)apacity
ccccccc1;cccccccc;cccccccc;cccccccc
       ^- is_long = 1

যেহেতু সংক্ষিপ্ত স্ট্রিংয়ের আকারটি উপরের 7 বিটে রয়েছে তাই এটি অ্যাক্সেস করার সময় স্থানান্তরিত হওয়া দরকার:

size_type __get_short_size() const {
    return __r_.first().__s.__size_ >> 1;
}

একইভাবে, দীর্ঘ স্ট্রিংয়ের সক্ষমতা অর্জনকারী এবং সেটটার বিটটি __long_maskপ্রায় কাজ করতে ব্যবহার করে is_long

আমি এখনও আমার প্রথম প্রশ্নের উত্তর খুঁজছি, অর্থাত্ __min_cap, বিভিন্ন আর্কিটেকচারের জন্য সংক্ষিপ্ত স্ট্রিংগুলির সক্ষমতা কী মূল্য গ্রহণ করবে ?

অন্যান্য স্ট্যান্ডার্ড গ্রন্থাগার বাস্তবায়ন

এই উত্তরটিstd::string অন্যান্য স্ট্যান্ডার্ড গ্রন্থাগার বাস্তবায়নে মেমরি লেআউটগুলির একটি সুন্দর ওভারভিউ দেয় ।


libc ++, হচ্ছে ওপেন সোর্স, আপনি তার জানতে পারেন stringহেডার এখানে , আমি এটা আউট মুহূর্তে :) খুঁজে দেখছি
Matthieu এম


@ ম্যাথিউ এম: আমি আগেও দেখেছি, দুর্ভাগ্যক্রমে এটি একটি খুব বড় ফাইল, এটি যাচাইয়ের জন্য সহায়তার জন্য ধন্যবাদ।
ভালারডোহারিস

@ অলি: আমি প্রায় গুগল করে এই নিয়ে হোঁচট খেয়েছি। তবে এই ব্লগ পোস্টটি স্পষ্টতই বলেছে যে এটি কেবলমাত্র এসএসওর একটি চিত্রণ এবং অনুশীলনে ব্যবহৃত হবে এমন কোনও উচ্চতর অনুকূলিতকরণ বৈকল্পিক নয়।
ভালারডোহারিস

উত্তর:


121

Libc ++ সমস্ত আর্কিটেকচারে 3 টি শব্দ রাখতে basic_stringডিজাইন করা হয়েছে sizeof, যেখানে sizeof(word) == sizeof(void*)। আপনি দীর্ঘ / সংক্ষিপ্ত পতাকাটি এবং সংক্ষিপ্ত আকারে আকারের ক্ষেত্রটি সঠিকভাবে বিচ্ছিন্ন করেছেন।

বিভিন্ন আর্কিটেকচারের জন্য স্বল্প স্ট্রিংয়ের সক্ষমতা __মিন_ক্যাপের কী মান হবে?

সংক্ষিপ্ত আকারে, এখানে কাজ করার জন্য 3 টি শব্দ রয়েছে:

  • 1 বিট দীর্ঘ / সংক্ষিপ্ত পতাকার কাছে যায়।
  • 7 বিট আকারে যায়।
  • ধরে নিচ্ছি char, 1 বাইট ট্রেলিং নালটিতে যায় (libc ++ সর্বদা ডেটার পিছনে একটি ট্রিলিং নাল সংরক্ষণ করবে)।

এটি একটি সংক্ষিপ্ত স্ট্রিং (অর্থ capacity()বরাদ্দ ছাড়াই বৃহত্তম ) সংরক্ষণের জন্য 3 শব্দ বিয়োগ 2 বাইট ছেড়ে যায় ।

একটি 32 বিট মেশিনে, 10 অক্ষর সংক্ষিপ্ত স্ট্রিংয়ের সাথে খাপ খায়। আকারের (স্ট্রিং) 12 হয়।

একটি 64 বিট মেশিনে, 22 টি অক্ষর সংক্ষিপ্ত স্ট্রিংয়ের সাথে খাপ খায়। আকারের (স্ট্রিং) 24 হয়।

sizeof(string)অভ্যন্তরীণ বাফারটিকে যতটা সম্ভব বড় করার সময় একটি প্রধান নকশার লক্ষ্য হ্রাস করা ছিল । যুক্তি হ'ল গতি সঞ্চার এবং গতি অ্যাসাইনমেন্ট। sizeofমুভ নির্মাণের সময় বা সরানো কার্যের সময় আপনাকে যত বেশি বৃহত্তর শব্দ সরাতে হবে।

ডেটা পয়েন্টার, আকার এবং ক্ষমতা সঞ্চয় করতে দীর্ঘ ফর্মটির সর্বনিম্ন 3 টি শব্দ প্রয়োজন needs সুতরাং আমি সেই একই 3 টি শব্দের মধ্যে সংক্ষিপ্ত রূপটি সীমাবদ্ধ করেছি। এটি প্রস্তাবিত হয়েছে যে একটি 4 শব্দ আকারের আরও ভাল পারফরম্যান্স থাকতে পারে। আমি সেই নকশা পছন্দটি পরীক্ষা করিনি।

_লিবিসিপিপি_আবিআইএলটিআরএনআরআরটিআরসি_ইলআউট

একটি কনফিগারেশন পতাকা রয়েছে _LIBCPP_ABI_ALTERNATE_STRING_LAYOUTযা ডেটা সদস্যদের এমনভাবে সাজিয়ে তোলে যে "দীর্ঘ বিন্যাস" এর থেকে পরিবর্তিত হয়:

struct __long
{
    size_type __cap_;
    size_type __size_;
    pointer   __data_;
};

প্রতি:

struct __long
{
    pointer   __data_;
    size_type __size_;
    size_type __cap_;
};

এই পরিবর্তনের অনুপ্রেরণা এই বিশ্বাসটি যে __data_ভালতর সারিবদ্ধ হওয়ার কারণে প্রথমে রাখার কিছু কার্যকারিতা সুবিধা থাকবে। পারফরম্যান্স সুবিধার পরিমাপ করার চেষ্টা করা হয়েছিল, এবং এটি পরিমাপ করা কঠিন ছিল difficult এটি কার্য সম্পাদনকে আরও খারাপ করবে না এবং এটি এটি আরও ভাল করে তুলতে পারে।

পতাকাটি যত্ন সহকারে ব্যবহার করা উচিত। এটি ভিন্ন এবিআই, এবং যদি দুর্ঘটনাক্রমে একটি libc ++ এর std::stringসাথে পৃথক সেটিংয়ের সংকলন মিশ্রিত হয় তবে _LIBCPP_ABI_ALTERNATE_STRING_LAYOUTরান সময়ের ত্রুটি তৈরি হবে।

আমি প্রস্তাব দিচ্ছি যে এই পতাকাটি কেবলমাত্র libc ++ এর বিক্রেতা দ্বারা পরিবর্তন করা উচিত।


17
নিশ্চিত নয় যে লিবাসি ++ এবং ফেসবুক ফলির মধ্যে লাইসেন্সের সামঞ্জস্যতা রয়েছে তবে এফবিস্ট্রিং মাপের বাকী ক্ষমতায় পরিবর্তন করে অতিরিক্ত চর (অর্থাত্ 23) সংরক্ষণ করতে সক্ষম করে , যাতে এটি 23 টি সংক্ষিপ্ত স্ট্রিংয়ের জন্য নাল টার্মিনেটর হিসাবে ডাবল ডিউটি ​​করতে পারে ।
TemplateRex

20
@ টেম্পলেটআরেক্স: এটি চালাক। তবে যদি libc ++ অবলম্বন করে তবে এর স্টাড :: স্ট্রিং সম্পর্কে আমি পছন্দ করি এমন অন্য একটি বৈশিষ্ট্যটি ছেড়ে দিতে লাইবসি ++ প্রয়োজন হবে: একটি ডিফল্ট নির্ধারিত stringসমস্ত 0 বিট। এটি ডিফল্ট নির্মাণকে সুপার দক্ষ করে তোলে। এবং আপনি যদি নিয়মগুলি বেন্ড করতে ইচ্ছুক হন তবে কখনও কখনও বিনামূল্যেও। উদাহরণস্বরূপ আপনি callocমেমরি করতে পারেন এবং কেবল এটি ডিফল্ট নির্মিত স্ট্রিংয়ে পূর্ণ বলে ঘোষণা করতে পারেন।
হাওয়ার্ড হিন্যান্ট

6
আহ, 0-থিম সত্যিই দুর্দান্ত! বিটিডাব্লু, এফবিস্ট্রিংয়ে দুটি পতাকা বিট রয়েছে, যা সংক্ষিপ্ত, মধ্যবর্তী এবং বড় স্ট্রিংগুলি নির্দেশ করে। এটি 23 অক্ষর পর্যন্ত স্ট্রিংয়ের জন্য এসএসও ব্যবহার করে এবং তারপরে 254 অক্ষর পর্যন্ত স্ট্রিংয়ের জন্য ম্যালোক-এড মেমরি অঞ্চল ব্যবহার করে এবং তারপরে তারা সিওডাব্লু করে (সি ++ 11 এ আর আইনি নয়, আমি জানি)।
TemplateRex

কেন আকার এবং ক্ষমতা সঞ্চয় করা যায় না intযাতে ক্লাসটি কেবলমাত্র 16 বাইটে 64-বিট আর্কিটেকচারে প্যাক করা যায়?
ফুকলিভ

@ লুভানহফ্যাক: আমি 2 জিবি-র চেয়ে বড় স্ট্রিংগুলিকে 64-বিটের মধ্যে দিয়ে দিতে চেয়েছিলাম। ব্যয় স্বীকার করা হয় আরও বড় sizeof। তবে একই সাথে অভ্যন্তরীণ বাফারটি char14 থেকে 22 পর্যন্ত চলে যায় যা বেশ ভাল উপকার।
হাওয়ার্ড হিন্যান্ট

21

Libc ++, বাস্তবায়ন একটু জটিল, আমি তার বিকল্প নকশা উপেক্ষা এবং একটি সামান্য endian কম্পিউটার অনুমান করা হবে:

template <...>
class basic_string {
/* many many things */

    struct __long
    {
        size_type __cap_;
        size_type __size_;
        pointer   __data_;
    };

    enum {__short_mask = 0x01};
    enum {__long_mask  = 0x1ul};

    enum {__min_cap = (sizeof(__long) - 1)/sizeof(value_type) > 2 ?
                      (sizeof(__long) - 1)/sizeof(value_type) : 2};

    struct __short
    {
        union
        {
            unsigned char __size_;
            value_type __lx;
        };
        value_type __data_[__min_cap];
    };

    union __ulx{__long __lx; __short __lxx;};

    enum {__n_words = sizeof(__ulx) / sizeof(size_type)};

    struct __raw
    {
        size_type __words[__n_words];
    };

    struct __rep
    {
        union
        {
            __long  __l;
            __short __s;
            __raw   __r;
        };
    };

    __compressed_pair<__rep, allocator_type> __r_;
}; // basic_string

নোট: __compressed_pairমূলত একজোড়া জন্য অপ্টিমাইজ করা হয় খালি বেজ অপ্টিমাইজেশান , ওরফে template <T1, T2> struct __compressed_pair: T1, T2 {};; সমস্ত উদ্দেশ্য এবং উদ্দেশ্যে আপনি এটিকে একটি নিয়মিত জুটি বিবেচনা করতে পারেন। এর গুরুত্ব সবেমাত্র উঠে আসে কারণ std::allocatorরাষ্ট্রহীন এবং এইভাবে খালি।

ঠিক আছে, এটি বরং কাঁচা, সুতরাং যাক যান্ত্রিকতা পরীক্ষা করা যাক! অভ্যন্তরীণভাবে, অনেক ফাংশন কল করবে __get_pointer()যা নিজে কল __is_longকরে স্ট্রিংটি ব্যবহার করছে __longবা __shortউপস্থাপন করছে তা নির্ধারণ করতে :

bool __is_long() const _NOEXCEPT
    { return bool(__r_.first().__s.__size_ & __short_mask); }

// __r_.first() -> __rep const&
//     .__s     -> __short const&
//     .__size_ -> unsigned char

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


এই বিস্তারিত উত্তরের জন্য আপনাকে ধন্যবাদ! আমি যে একমাত্র টুকু মিস করছি তা হ'ল __min_capবিভিন্ন আর্কিটেকচারের জন্য যা মূল্যায়ন করবে, আমি নিশ্চিত নই যে কী sizeof()ফিরে আসবে এবং কীভাবে এটি এলিয়াসিং দ্বারা প্রভাবিত হয়।
ভালারডোহারিস

4
@ ভালারডোহারের এটি বাস্তবায়নের সংজ্ঞা দেওয়া হয়েছে। সাধারণত, আপনি 3 * the size of one pointerএই ক্ষেত্রে আশা করতে পারেন , যা একটি 32 বিট খিলানের 12 টি এবং একটি 64 বিটের খিলানটিতে 24 হবে ts
জাস্টিন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.