স্ট্যান্ড :: স্ট্রিং ভিউ ঠিক কীভাবে কনস্ট স্ট্যান্ড :: স্ট্রিং ও এর চেয়ে দ্রুত?


221

std::string_viewএটি সি ++ 17 এ পরিণত করেছে এবং এর পরিবর্তে এটি ব্যবহার করার জন্য এটি ব্যাপকভাবে প্রস্তাবিত const std::string&

এর অন্যতম কারণ হ'ল পারফরম্যান্স।

প্যারামিটারের ধরণ হিসাবে ব্যবহৃত হওয়ার চেয়ে কেউ কীভাবে দ্রুত / ঠিক std::string_view হবে তা ব্যাখ্যা করতে পারে const std::string&? (আসুন ধরে নেওয়া যাক কলিতে কোনও কপি তৈরি করা হয়নি)


7
std::string_viewএটি (চর * শুরু, চর * শেষ) জুটির একটি বিমূর্ততা। আপনি এটি ব্যবহার করার সময় std::stringএকটি অপ্রয়োজনীয় অনুলিপি হবে।
QuestionC

আমার মতে প্রশ্নটি কোনটি দ্রুততর তা ঠিক নয়, তবে কখন সেগুলি ব্যবহার করব। যদি আমার স্ট্রিংয়ে কিছু হেরফের দরকার হয় এবং এটি স্থায়ী নয় এবং / অথবা মূল মানটি রাখে তবে স্ট্রিং_ভিউ সঠিক কারণ এটি আমার কাছে স্ট্রিংয়ের একটি অনুলিপি তৈরি করার দরকার নেই। তবে যদি স্ট্রিং ব্যবহার করে আমাকে কেবল স্ট্রিংয়ের জন্য কিছু পরীক্ষা করতে হবে :: উদাহরণস্বরূপ সন্ধান করুন, তবে উল্লেখটি আরও ভাল।
Therquitect

@ প্রশ্ন আপনি যখন std::stringএআইপি সীমাবদ্ধ রাখতে চান না তখন আপনি এটি ব্যবহার করেন (স্ট্রিং_ভিউ কাঁচা অ্যারে, ভেক্টরগুলি std::basic_string<>অ-ডিফল্ট বরাদ্দকারী ইত্যাদির সাথে গ্রহণ করতে পারে ইত্যাদি ইত্যাদি। ওহ, এবং অন্যান্য স্ট্রিং_ভিউগুলি স্পষ্টতই)
সেপ্টেম্বর

উত্তর:


213

std::string_view কয়েক ক্ষেত্রে দ্রুত হয়।

প্রথমত, std::string const&ডেটা a তে থাকা প্রয়োজন std::string, এবং কোনও কাঁচা সি অ্যারে নয়, সিআই char const*এপি দ্বারা ফেরত দেওয়া, std::vector<char>কিছু ডিসরিয়ালাইজেশন ইঞ্জিন দ্বারা উত্পাদিত ইত্যাদি ইত্যাদি এড়ানো বিন্যাস রূপান্তরটি বাইটগুলি অনুলিপি করা এড়ানো যায় এবং (যদি স্ট্রিংটি এর চেয়ে দীর্ঘ হয় তবে নির্দিষ্ট std::stringপ্রয়োগের জন্য এসবিও¹ ) একটি মেমরি বরাদ্দ এড়ানো হয়।

void foo( std::string_view bob ) {
  std::cout << bob << "\n";
}
int main(int argc, char const*const* argv) {
  foo( "This is a string long enough to avoid the std::string SBO" );
  if (argc > 1)
    foo( argv[1] );
}

ক্ষেত্রে কোনও বরাদ্দ দেওয়া হয় না string_view, তবে এটি যদি fooএকটি এর std::string const&পরিবর্তে নেওয়া হয় string_view

দ্বিতীয় সত্যই বড় কারণ হ'ল এটি অনুলিপি ছাড়াই সাবস্ট্রিংয়ের সাথে কাজ করার অনুমতি দেয়। মনে করুন আপনি একটি 2 গিগাবাইট জেসন স্ট্রিং (!) Ars পার্স করছেন ² আপনি যদি এটিতে পার্স করেন তবে এই std::stringজাতীয় প্রতিটি পার্স নোড যেখানে তারা কোনও নোডের নাম বা মান সঞ্চয় করে সেখানে 2 জিবি স্ট্রিং থেকে স্থানীয় নোডে মূল ডেটা অনুলিপি করে।

পরিবর্তে, আপনি যদি এটির জন্য পার্স std::string_viewকরেন, নোডগুলি মূল ডেটাটিকে উল্লেখ করে। এটি পার্সিংয়ের সময় কয়েক মিলিয়ন বরাদ্দ এবং অর্ধেক মেমরির প্রয়োজনীয়তা সংরক্ষণ করতে পারে।

আপনি যে স্পিডআপ পেতে পারেন তা কেবল হাস্যকর।

এটি একটি চরম ঘটনা, তবে অন্যান্য "সাবস্ট্রিং পান এবং এটির সাথে কাজ করুন" কেসগুলি এর সাথে শালীন স্পিডআপও তৈরি করতে পারে string_view

সিদ্ধান্তের একটি গুরুত্বপূর্ণ অংশ হ'ল আপনি যা ব্যবহার করে হারাবেন std::string_view। এটি খুব বেশি নয়, তবে এটি কিছু।

আপনি নিখুঁত নাল সমাপ্তি হারাবেন, এবং এটি প্রায় এটিই। সুতরাং যদি একই স্ট্রিংটি 3 টি ফাংশনে পাস করা হয় যার মধ্যে সবগুলিই নাল টার্মিনেটরের প্রয়োজন, std::stringএকবারে রূপান্তর করা বুদ্ধিমান হতে পারে। সুতরাং যদি আপনার কোডটি নাল টার্মিনেটরের প্রয়োজন হিসাবে পরিচিত হয় এবং আপনি সি-স্টাইলের টানযুক্ত বাফার বা এর মতো স্ট্রিংগুলি আশা করেন না তবে সম্ভবত এটি গ্রহণ করুন std::string const&। নইলে ক std::string_view

যদি std::string_viewএকটি পতাকা থাকে যা যদি জানিয়েছিল যে এটি বাতিল হয়ে গেছে (বা কোনও অনুরাগী কিছু) এটি ব্যবহারের শেষ কারণটিও সরিয়ে ফেলবে std::string const&

সেখানে একটি মামলা যেখানে একটি গ্রহণ std::stringসঙ্গে const&করা হয় অনুকূল একটি ওভার std::string_view। কল করার পরে যদি আপনার অনির্দিষ্টকালের জন্য স্ট্রিংয়ের একটি অনুলিপি গ্রহণের প্রয়োজন হয় তবে বাই-মান নেওয়া কার্যকর efficient আপনি হয় এসবিও কেসে থাকবেন (এবং কোনও বরাদ্দ নেই, এটির সদৃশ করার জন্য কয়েকটি চরিত্রের অনুলিপি) বা আপনি স্থানীয়ভাবে হ্যাপ-বরাদ্দ হওয়া বাফার স্থানান্তরিত করতে সক্ষম হবেন std::string। দুটি ওভারলোড থাকা std::string&&এবং std::string_viewএটি দ্রুত হতে পারে তবে কেবলমাত্র সামান্য এবং এর ফলে পরিমিত কোড ফুটে উঠবে (যা আপনাকে সমস্ত গতির ব্যয় করতে পারে)।


¹ ছোট বাফার অপ্টিমাইজেশন

প্রকৃত ব্যবহারের ক্ষেত্রে।


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

এসবিও অদ্ভুত লাগছে। আমি সর্বদা এসএসও শুনেছি (ছোট স্ট্রিং অপ্টিমাইজেশন)
4:44

@ ফিউ শিওর; স্ট্রিংগুলি কেবলমাত্র আপনি কৌশলটি ব্যবহার করেন না।
ইয়াক্ক - অ্যাডাম নেভ্রামামন্ট

@ ফুকলভ এসএসও কেবল এসবিওর একটি নির্দিষ্ট ক্ষেত্রে, যা ছোট বাফার অপ্টিমাইজেশনের জন্য দাঁড়িয়েছে । বিকল্প পদগুলি হ'ল ছোট ডেটা অপ্ট। , ছোট অবজেক্ট অপ্ট। বা ছোট আকারের অপ্ট।
ড্যানিয়েল ল্যাঙ্গার

59

স্ট্রিং_ভিউ কর্মক্ষমতা উন্নত করার একটি উপায় হ'ল এটি সহজেই উপসর্গ এবং প্রত্যয়গুলি সরিয়ে ফেলার অনুমতি দেয়। হুডের নীচে, স্ট্রিং_ভিউ কেবল কিছু স্ট্রিং বাফারে একটি পয়েন্টারে উপসর্গের আকার যুক্ত করতে পারে বা বাইট কাউন্টার থেকে প্রত্যয় আকারটি বিয়োগ করতে পারে, এটি সাধারণত দ্রুত হয়। std :: স্ট্রিংকে অন্যদিকে সাবস্ট্রাস্টের মতো কিছু করার সময় তার বাইটগুলি অনুলিপি করতে হবে (এইভাবে আপনি একটি নতুন স্ট্রিং পেয়েছেন যা এর বাফারের মালিক, তবে অনেক ক্ষেত্রে আপনি অনুলিপি ছাড়াই মূল স্ট্রিংয়ের অংশ পেতে চান)। উদাহরণ:

std::string str{"foobar"};
auto bar = str.substr(3);
assert(bar == "bar");

স্ট্যান্ড :: স্ট্রিং_ভিউ সহ:

std::string str{"foobar"};
std::string_view bar{str.c_str(), str.size()};
bar.remove_prefix(3);
assert(bar == "bar");

হালনাগাদ:

কিছু বাস্তব সংখ্যা যুক্ত করার জন্য আমি খুব সাধারণ একটি বেঞ্চমার্ক লিখেছিলাম। আমি দুর্দান্ত গুগল বেনমার্ক লাইব্রেরি ব্যবহার করেছি । বেঞ্চমার্কযুক্ত ফাংশনগুলি হ'ল:

string remove_prefix(const string &str) {
  return str.substr(3);
}
string_view remove_prefix(string_view str) {
  str.remove_prefix(3);
  return str;
}
static void BM_remove_prefix_string(benchmark::State& state) {                
  std::string example{"asfaghdfgsghasfasg3423rfgasdg"};
  while (state.KeepRunning()) {
    auto res = remove_prefix(example);
    // auto res = remove_prefix(string_view(example)); for string_view
    if (res != "aghdfgsghasfasg3423rfgasdg") {
      throw std::runtime_error("bad op");
    }
  }
}
// BM_remove_prefix_string_view is similar, I skipped it to keep the post short

ফলাফল

(x86_64 লিনাক্স, জিসিসি 6.2, " -O3 -DNDEBUG"):

Benchmark                             Time           CPU Iterations
-------------------------------------------------------------------
BM_remove_prefix_string              90 ns         90 ns    7740626
BM_remove_prefix_string_view          6 ns          6 ns  120468514

2
আপনি প্রকৃত মানদণ্ড সরবরাহ করেছেন তা দুর্দান্ত। এটি প্রকৃতপক্ষে প্রাসঙ্গিক ব্যবহারের ক্ষেত্রে কী অর্জন করা যায় তা দেখায়।
ড্যানিয়েল কামিল কোজার

1
@ ড্যানিয়েলকামিলকোজার মতামতের জন্য ধন্যবাদ আমি বেনমার্কগুলি মূল্যবান বলেও মনে করি, কখনও কখনও তারা সবকিছু পরিবর্তন করে।
পাভেল ডেভিডভ

47

দুটি প্রধান কারণ রয়েছে:

  • string_view বিদ্যমান বাফারের একটি টুকরো এটি মেমরি বরাদ্দের প্রয়োজন হয় না
  • string_view রেফারেন্স দ্বারা নয়, মান দ্বারা পাস করা হয়

স্লাইস থাকার সুবিধা একাধিক:

  • আপনি এটি কোনও নতুন বাফার বরাদ্দের সাথে char const*বা char[]ছাড়াই ব্যবহার করতে পারেন
  • আপনি বরাদ্দ ছাড়াই বিদ্যমান বাফারে একাধিক স্লাইস এবং সাবস্ক্রাইস নিতে পারেন
  • সাবস্ট্রিংটি হ'ল (1), ও (এন) নয়
  • ...

আরও ভাল এবং আরও বেশি ধারাবাহিক পারফরম্যান্স over


মান দ্বারা পাস করার রেফারেন্স দ্বারা পাস করারও সুবিধা আছে, কারণ aliasing।

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

অন্যদিকে, string_viewমান দ্বারা পাস করার সময় , সংকলক স্থিতিশীলভাবে নির্ধারণ করতে পারে যে কোনও অন্য কোড এখন স্ট্যাকের (বা রেজিস্টারগুলিতে) দৈর্ঘ্য এবং ডেটা পয়েন্টারগুলিকে সংশোধন করতে পারে না। ফলস্বরূপ, এটি ফাংশন কলগুলিতে তাদের "ক্যাশে" করতে পারে।


36

একটি জিনিস এটি করতে পারে তা হল std::stringনাল টার্মিনেটেড স্ট্রিং থেকে অন্তর্নিহিত রূপান্তরের ক্ষেত্রে কোনও অবজেক্ট তৈরি করা এড়ানো :

void foo(const std::string& s);

...

foo("hello, world!"); // std::string object created, possible dynamic allocation.
char msg[] = "good morning!";
foo(msg); // std::string object created, possible dynamic allocation.

12
এটি বলার অপেক্ষা রাখে না যে স্ট্রিং-ভিউয়ের চেয়ে স্ট্রিং-ভিউয়ের সাথে const std::string str{"goodbye!"}; foo(str);সম্ভবত আর কোনও দ্রুত হবে না এবং
মার্টিন বোনার

1
এটি কোনও string_viewপয়েন্টারের বিপরীতে দুটি পয়েন্টার অনুলিপি করতে হবে বলে ধীর হবে না const string&?
বালকি

9

std::string_viewমূলত a এর চারপাশে কেবল একটি মোড়ক const char*। এবং পাস করার const char*অর্থ হ'ল পাসিং const string*(বা const string&) এর সাথে তুলনা করে সিস্টেমে আরও কম পয়েন্টার থাকবে , কারণ string*এরকম কিছু বোঝায়:

string* -> char* -> char[]
           |   string    |

স্পষ্টতই কনস্ট আর্গুমেন্টগুলি পাস করার উদ্দেশ্যে প্রথম পয়েন্টারটি অতিরিক্ত প্রয়োজন।

পিএস এর মধ্যে std::string_viewএবং এর মধ্যে const char*একটি বৈষম্যমূলক পার্থক্য হ'ল স্ট্রিং_ভিউগুলি নাল-টার্মিনেট করতে হবে না (তাদের বিল্ট-ইন আকার রয়েছে) এবং এটি এলোমেলোভাবে জায়গায় দীর্ঘতর স্ট্রিং বিভক্ত করতে দেয় allows


4
ডাউনভোটসের কী আছে? std::string_viewগুলি কেবল অভিনব const char*, পিরিয়ড। জিসিসি তাদের এগুলি প্রয়োগ করে:class basic_string_view {const _CharT* _M_str; size_t _M_len;}
n.caillou

4
কেবলমাত্র 65 কে রেপ পান (আপনার বর্তমান 65 থেকে) এবং এটিই স্বীকৃত উত্তর (কার্গো-
কাল্ট

7
@mlvljr কেউ পাস std::string const*। এবং সেই চিত্রটি বোধগম্য। @ এনসিএলউ: আপনার নিজের মন্তব্যের উত্তরটির চেয়ে ইতিমধ্যে আরও নির্ভুল। এটি string_view"অভিনব char const*" এর চেয়েও বেশি করে তোলে - এটি সত্যই সুস্পষ্ট।
sehe

@ তবে আমি হতে পারি যে কেউই নেই, সমস্যা নেই (অর্থাত্ কনস্ট্রিং স্ট্রিংয়ের জন্য কোনও পয়েন্টার (বা রেফারেন্স) দিচ্ছেন কেন, কেন নয়?) :) :)
এমএলভিএলজেআর

2
@ তবে আপনি বুঝতে পারেন যে এটি একটি অপ্টিমাইজেশন বা সম্পাদন দৃষ্টিকোণ থেকে std::string const*এবং std::string const&একইরকম, তাই না?
n.caillou
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.