আমি কেন স্টাড :: একটি স্টাডি :: শেয়ারড_পিটার স্থানান্তর করব?


148

আমি কলং উত্স কোডটি সন্ধান করেছি এবং আমি এই স্নিপেটটি পেয়েছি:

void CompilerInstance::setInvocation(
    std::shared_ptr<CompilerInvocation> Value) {
  Invocation = std::move(Value);
}

কেন আমি চায় std::moveএকটি std::shared_ptr?

শেয়ারড রিসোর্সে মালিকানা স্থানান্তর করার কোনও পয়েন্ট আছে কি?

আমি কেবল এর পরিবর্তে এটি করব না কেন?

void CompilerInstance::setInvocation(
    std::shared_ptr<CompilerInvocation> Value) {
  Invocation = Value;
}

উত্তর:


137

আমি মনে করি যে অন্যান্য উত্তরগুলির পক্ষে যথেষ্ট পরিমাণে জোর দেওয়া হয়নি তা হ'ল গতির বিন্দু ।

std::shared_ptrরেফারেন্স গণনাটি পারমাণবিক । বৃদ্ধি বা রেফারেন্স গণনা হ্রাস প্রয়োজন পারমাণবিক বৃদ্ধি বা হ্রাস । এটি অ-পারমাণবিক বৃদ্ধি / হ্রাসের চেয়ে শতগুণ ধীর , উল্লেখ করা দরকার না যে আমরা যদি একই পরিমাণ বাড়ানো এবং হ্রাস করি তবে আমরা সঠিক সংখ্যাটি নিয়ে গতিতে প্রক্রিয়ায় এক টন সময় এবং সংস্থান নষ্ট করি।

shared_ptrএটি অনুলিপি করার পরিবর্তে সরানোর মাধ্যমে আমরা পারমাণবিক রেফারেন্স গণনাটি "চুরি" করি এবং আমরা অন্যটিকে বাতিল করি shared_ptr। "চুরি" রেফারেন্স গণনাটি পারমাণবিক নয় এবং এটি অনুলিপি করা shared_ptr(এবং পারমাণবিক রেফারেন্স বৃদ্ধি বা হ্রাস) এর চেয়ে শতগুণ দ্রুত faster

মনে রাখবেন যে এই কৌশলটি নিখুঁতভাবে অনুকূলকরণের জন্য ব্যবহৃত হয়েছে। এটিকে অনুলিপি করা (যেমন আপনি পরামর্শ দিয়েছেন) ঠিক তেমনি সূক্ষ্ম কার্যকারিতা অনুযায়ী।


5
এটা কি সত্যিই শতগুণ দ্রুত? আপনি কি এই জন্য মাপদণ্ড আছে?
xaviersjs

1
@xaviersjs যখন মূল্য সুযোগের বাইরে চলে যায় তখন অ্যাসাইনমেন্টটির জন্য একটি পারমাণবিক বৃদ্ধি প্রয়োজন হয় followed পারমাণবিক ক্রিয়াকলাপ শত শত ঘড়ির চক্র নিতে পারে। সুতরাং হ্যাঁ, এটি আসলে অনেক ধীর।
অ্যাডিসাক

2
@Adisak প্রথম আমি আনতে যোগ অপারেশন (শুনেছি যে en.wikipedia.org/wiki/Fetch-and-add ) একটি মৌলিক বৃদ্ধি চেয়ে বেশি চক্র শত শত সময় লাগতে পারে। আপনি কি তার জন্য একটি রেফারেন্স আছে?
xaviersjs

2
@xaviersjs: stackoverflow.com/a/16132551/4238087 রেজিস্টার অপারেশন কয়েক চক্র, 100 এর (100-300) পারমাণবিক তড়কা বিলের জন্য চক্র হয়ে পড়ে থাকে। যদিও মেট্রিকগুলি 2013 সালের, এটি এখনও বিশেষভাবে মাল্টি সকেট NUMA সিস্টেমের ক্ষেত্রে সত্য বলে মনে হচ্ছে।
রাশিয়ানফুল 21

1
কখনও কখনও আপনি ভাবেন যে আপনার কোডটিতে কোনও থ্রেডিং নেই ... তবে তারপরে কিছু রঞ্জক গ্রন্থাগার আসে এবং এটি আপনার জন্য নষ্ট করে দেয়। কনস্ট্যান্ট রেফারেন্স এবং স্ট্যান্ড :: ব্যবহারে ব্যবহার করা ভাল ... যদি এটি স্পষ্ট এবং স্পষ্ট হয় যে আপনি পারেন .... পয়েন্টার রেফারেন্স গণনার উপর নির্ভর করার চেয়ে।
এরিক অ্যারোনস্টি

123

ব্যবহার করে moveআপনি বাড়তি এড়াতে পারবেন এবং তারপরে তাত্ক্ষণিকভাবে হ্রাস পাবে, শেয়ারের সংখ্যা। এটি আপনাকে ব্যবহারের গণনায় কিছু ব্যয়বহুল পারমাণবিক অপারেশন সংরক্ষণ করতে পারে।


1
এটি কি অকালীন অপটিমাইজেশন নয়?
ওয়াইএসসি

11
@YSC না, যদি কেউ এটি সেখানে রাখে তবে এটি সত্যই পরীক্ষা করে।
অরেঞ্জডোগ

19
@ ওয়াইএসসি অকাল অপটিমাইজেশন খারাপ যদি এটি কোড পড়তে বা বজায় রাখা শক্ত করে তোলে। এটি একটিও হয় না, কমপক্ষে আইএমও করে।
অ্যাঞ্জিউ আর 10

17
প্রকৃতপক্ষে. এটি অকাল অপটিমাইজেশন নয়। পরিবর্তে এই ফাংশনটি লেখার বুদ্ধিমান উপায়।
অরবিটে লাইটনেস রেস

60

সরান কার্যকলাপ (পদক্ষেপ কন্সট্রাকটর মত) জন্য std::shared_ptrহয় সস্তা তারা মূলত হয়, "চুরি পয়েন্টার" (গন্তব্যে উৎস থেকে আরও ভালো হবে, পুরো রাষ্ট্র নিয়ন্ত্রণ ব্লক গন্তব্যে উৎস থেকে "চুরি" হয়, রেফারেন্স গণনা তথ্য সহ) ।

পরিবর্তে কপি উপর অপারেশন std::shared_ptrডাকা পারমাণবিক (ঠিক না অর্থাত রেফারেন্স গণনা বৃদ্ধি ++RefCountএকটি পূর্ণসংখ্যা উপর RefCountতথ্য সদস্য, কিন্তু যেমন কলিং InterlockedIncrementWindows এ), যা বেশি ব্যয়বহুল মাত্র পয়েন্টার / রাজ্য চুরি হয়।

সুতরাং, বিশদে বিশদ হিসাবে এই মামলার রেফ গণনা গতিশীলতা বিশ্লেষণ:

// shared_ptr<CompilerInvocation> sp;
compilerInstance.setInvocation(sp);

আপনি যদি spমান দ্বারা পাস করেন এবং তারপরে পদ্ধতির ভিতরে একটি অনুলিপি নেন CompilerInstance::setInvocation, আপনার কাছে রয়েছে:

  1. পদ্ধতিতে প্রবেশ করার সময়, shared_ptrপ্যারামিটারটি অনুলিপি নির্মিত হয়: রেফ কাউন্ট অ্যাটমিক ইনক্রিমেন্ট
  2. পদ্ধতির শরীরে ভিতরে, আপনি প্যারামিটারটি ডেটা মেম্বারে অনুলিপি করেন shared_ptr: রেফ কাউন্ট অ্যাটমিক ইনক্রিমেন্ট
  3. পদ্ধতিটি থেকে বেরিয়ে আসার সময়, shared_ptrপ্যারামিটারটি ধ্বংস হয়: রেফ কাউন্ট পারমাণবিক হ্রাস

মোট তিনটি পারমাণবিক ক্রিয়াকলাপের জন্য আপনার কাছে দুটি পরমাণু বৃদ্ধি এবং একটি পারমাণবিক হ্রাস রয়েছে ।

পরিবর্তে, যদি আপনি shared_ptrমান অনুসারে প্যারামিটারটি পাস করেন এবং তারপরে std::moveপদ্ধতির অভ্যন্তরে (যথাযথভাবে ক্ল্যাংয়ের কোডে করা হয়ে থাকে) আপনার কাছে রয়েছে:

  1. পদ্ধতিতে প্রবেশ করার সময়, shared_ptrপ্যারামিটারটি অনুলিপি নির্মিত হয়: রেফ কাউন্ট অ্যাটমিক ইনক্রিমেন্ট
  2. পদ্ধতি লাশ ভিতরে, আপনি তথ্য সদস্য মধ্যে পরামিতি: সুত্র গণনা করে না পরিবর্তন! আপনি কেবল পয়েন্টার / রাষ্ট্র চুরি করছেন: কোনও ব্যয়বহুল পারমাণবিক রেফ কাউন্ট অপারেশন জড়িত নয়।std::moveshared_ptr
  3. পদ্ধতিটি থেকে বেরিয়ে যাওয়ার সময়, shared_ptrপ্যারামিটারটি ধ্বংস হয়; তবে যেহেতু আপনি দ্বিতীয় পদক্ষেপে চলে এসেছেন, তাই ধ্বংসকারী কিছুই নেই, কারণ shared_ptrপ্যারামিটারটি আর কোনও কিছুর দিকে ইশারা করছে না। আবার, এক্ষেত্রে কোনও পারমাণবিক হ্রাস ঘটে না।

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


1
লক্ষ্য করার মতো বিষয়: কেন তারা কেবল কনস্টের্ট রেফারেন্স দিয়ে পাস করে না এবং পুরো স্টাড :: পদক্ষেপের পদক্ষেপ এড়িয়ে যায় কেন? কারণ পাস-বাই-মান আপনাকে সরাসরি একটি কাঁচা পয়েন্টারে পাস করতে দেয় এবং সেখানে একটি শেয়ারড_পিটার তৈরি হবে।
জোসেফ আয়ারল্যান্ড ২

@ জোসেফ আইরল্যান্ড কারণ আপনি কোনও সংবিধানের রেফারেন্স সরাতে পারবেন না
ব্রুনো

2
@ জোসেফ আইরল্যান্ড কারণ আপনি যদি এটি হিসাবে কল করেন compilerInstance.setInvocation(std::move(sp));তবে কোনও বৃদ্ধি হবে না । আপনি shared_ptr<>&&যখন প্রয়োজন হয় না তখন কেন ডুপ্লিকেট লাগে এমন একটি ওভারলোড যুক্ত করে আপনি একই আচরণ পেতে পারেন ।
ratchet freak

2
@ ব্রুনোফেরিরা আমি আমার নিজের প্রশ্নের উত্তর দিচ্ছিলাম। আপনার এটি সরানোর দরকার হবে না কারণ এটি একটি রেফারেন্স, কেবল এটি অনুলিপি করুন। এখনও দুজনের পরিবর্তে কেবল একটি অনুলিপি। কারণ তারা যে না, কারণ এটা অকারণে নবনির্মিত shared_ptrs, যেমন থেকে কপি হবে setInvocation(new CompilerInvocation), র্যাচিট উল্লেখ করা হয়েছে, অথবা setInvocation(std::move(sp))। দুঃখিত, যদি আমার প্রথম মন্তব্যটি অস্পষ্ট ছিল, আমি আসলে এটি দুর্ঘটনার দ্বারা পোস্ট করেছি, আমার লেখার আগে, এবং আমি কেবল এটি ছেড়ে দেওয়ার সিদ্ধান্ত নিয়েছিলাম
জোসেফ আয়ারল্যান্ড

22

একটি অনুলিপি shared_ptrকরার সাথে এর অভ্যন্তরীণ স্থিতি অবজেক্ট পয়েন্টারটি অনুলিপি করা এবং রেফারেন্স গণনা পরিবর্তন করা। এটি স্থানান্তরিত করার জন্য কেবলমাত্র অভ্যন্তরীণ রেফারেন্স কাউন্টারে পয়েন্টার এবং স্বত্বযুক্ত বস্তুটি অদলবদল জড়িত, তাই এটি দ্রুত।


16

এই পরিস্থিতিতে স্ট্যান্ড :: পদক্ষেপ ব্যবহারের দুটি কারণ রয়েছে। বেশিরভাগ প্রতিক্রিয়া গতির ইস্যুটিকে সম্বোধন করেছিল, তবে কোডের অভিপ্রায়টি আরও স্পষ্টভাবে দেখানোর গুরুত্বপূর্ণ বিষয়টিকে উপেক্ষা করেছে।

একটি স্টাডি :: শেয়ার্ড_পিটারের জন্য, স্টাড :: পদক্ষেপটি নির্বিঘ্নে পয়েন্টটির মালিকানার স্থানান্তরকে বোঝায়, যখন একটি সাধারণ অনুলিপি অপারেশন অতিরিক্ত মালিককে যুক্ত করে। অবশ্যই, যদি মূল মালিক পরবর্তী সময়ে তাদের মালিকানা ছেড়ে দেয় (যেমন তাদের স্টাড :: শেয়ার্ড_পিটারকে ধ্বংস করার অনুমতি দিয়ে), তবে মালিকানা স্থানান্তর সম্পন্ন হয়েছে।

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


ঠিক আমি যা খুঁজছি অন্যান্য উত্তর কীভাবে এই গুরুত্বপূর্ণ শব্দার্থক পার্থক্যটিকে উপেক্ষা করে তা অবাক করে দিয়েছে। স্মার্ট পয়েন্টারগুলি হ'ল মালিকানা সম্পর্কে।
কিওয়ারুইপ

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