Num ++ 'int num' এর জন্য পারমাণবিক হতে পারে?


153

সাধারণভাবে, (বা ) জন্য int num, রিড-মডিফাই-রাইটিং অপারেশন হিসাবে পারমাণবিক নয় । তবে আমি প্রায়শই সংকলকগুলি দেখতে পাই, উদাহরণস্বরূপ, জিসিসি , এর জন্য নিম্নলিখিত কোড তৈরি করুন ( এখানে চেষ্টা করুন ):num++++num

এখানে চিত্র বিবরণ লিখুন

যেহেতু লাইন 5, যা num++একটি নির্দেশের সাথে মিলে যায়, তাই আমরা কি সিদ্ধান্ত নিতে পারি যে এই ক্ষেত্রে num++ পারমাণবিক ?

এবং যদি তা হয়, তবে এর অর্থ কি এই যে উত্পন্ন num++উত্সাহটি ডেটা রেসের কোনও বিপদ ছাড়াই একযোগে (বহু-থ্রেডযুক্ত) পরিস্থিতিতে ব্যবহার করা যেতে পারে (যেমন আমাদের এটি তৈরি করার দরকার নেই, উদাহরণস্বরূপ, std::atomic<int>এবং সম্পর্কিত ব্যয় আরোপ করার দরকার নেই, কারণ এটি পরমাণু যাইহোক)?

হালনাগাদ

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


65
কে আপনাকে বলেছে যে addপারমাণবিক?
স্লভা

6
প্রদত্ত পারমাণবিক বৈশিষ্ট্যগুলির মধ্যে একটি হ'ল অপ্টিমাইজেশনের সময় নির্দিষ্ট ধরণের পুনর্নির্মাণ রোধ করা, না, আসল অপারেশনের পারমাণবিকতা নির্বিশেষে
জাগডস্পায়ার

19
আমি আরও উল্লেখ করতে চাই যে এটি যদি আপনার প্ল্যাটফর্মে পারমাণবিক হয় তবে এটি অন্য কোনও প্লাটফর্মে আসার কোনও গ্যারান্টি নেই। প্ল্যাটফর্মটি স্বাধীন হন এবং একটি ব্যবহার করে আপনার অভিপ্রায় প্রকাশ করুন std::atomic<int>
নাথানঅলিভার

8
সেই addনির্দেশটি কার্যকর করার সময় , অন্য একটি কোর এই কোরটির ক্যাশে থেকে সেই মেমরি ঠিকানাটি চুরি করতে এবং এটি সংশোধন করতে পারে। একটি x86 সিপিইউতে, addনির্দেশটি lockঅপারেশনের সময়কালের জন্য ক্যাশে লক করা দরকার হলে নির্দেশের একটি উপসর্গ প্রয়োজন।
ডেভিড শোয়ার্টজ

21
যেকোনও অপারেশন "পারমাণবিক" হওয়া সম্ভব । আপনাকে যা করতে হবে তা ভাগ্যবান এবং কখনই এমন কিছু সম্পাদন করতে হবে না যা প্রকাশ করবে এটি পারমাণবিক নয়। গ্যারান্টি হিসাবে পরমাণু কেবল মূল্যবান । প্রদত্ত যে আপনার সমাবেশ কোড এ খুঁজছেন, প্রশ্ন হচ্ছে সেই বিশেষ স্থাপত্য আপনি গ্যারান্টি প্রদান ঘটবে হয় এবং কম্পাইলার একটি গ্যারান্টি যে সমাবেশ স্তর বাস্তবায়ন তারা চয়ন করবেন প্রদান করে কিনা।
আমটন

উত্তর:


197

এটি সম্পূর্ণরূপে সি -++ কে ডেটা রেস হিসাবে সংজ্ঞায়িত করে যা অনির্ধারিত আচরণের কারণ করে, এমনকি যদি কোনও সংকলক কোড তৈরি করে যা কিছু টার্গেট মেশিনে আপনি যা প্রত্যাশা করেছিলেন তা করে। আপনি ব্যবহার করতে হবে std::atomicনির্ভরযোগ্য ফলাফলের জন্য, কিন্তু আপনার সাথে এটি ব্যবহার করতে পারেন memory_order_relaxedযদি আপনি পুনঃক্রমবিন্যাস যত্ন সম্পর্কে না। কিছু উদাহরণ কোড এবং asm আউটপুট ব্যবহার করে নীচে দেখুন fetch_add


তবে প্রথমে, সংসদ ভাষায় প্রশ্নের অংশ:

যেহেতু নাম ++ একটি নির্দেশ ( add dword [num], 1), তাই আমরা কি সিদ্ধান্ত নিতে পারি যে নাম ++ এই ক্ষেত্রে পারমাণবিক?

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

অন্যান্য সিপিইউগুলির স্মৃতি ক্রিয়াকলাপ লোড এবং স্টোরের মধ্যে বিশ্বব্যাপী দৃশ্যমান হতে পারে। অর্থাত add dword [num], 1একটি লুপে চলমান দুটি থ্রেড একে অপরের স্টোরগুলিতে প্রবেশ করবে। ( একটি সুন্দর চিত্রের জন্য @ মার্গারেটের উত্তর দেখুন )। দুইটি থ্রেডের প্রতিটি থেকে 40k ইনক্রিমেন্টের পরে, কাউন্টারটি কেবল রিয়েল মাল্টি-কোর x86 হার্ডওয়্যারে কেবল 60 ডলার (80 কে নয়) বাড়িয়েছে।


"অ্যাটমিক", গ্রীক শব্দ থেকে যার অর্থ অবিভাজ্য, এর অর্থ হ'ল কোনও পর্যবেক্ষক পৃথক পদক্ষেপ হিসাবে অপারেশনটি দেখতে পাচ্ছেন না। সমস্ত বিটের একযোগে শারীরিক / বৈদ্যুতিকভাবে তাত্ক্ষণিকভাবে ঘটে যাওয়া বোঝা বা স্টোরের জন্য এটি অর্জনের একমাত্র উপায়, তবে এটি ALU অপারেশনের পক্ষেও সম্ভব নয়। X86-এর পারমাণবিকতার বিষয়ে আমার উত্তরটিতে খাঁটি লোড এবং খাঁটি স্টোরগুলি সম্পর্কে আমি আরও অনেক বিশদে গিয়েছিলাম , যখন এই উত্তরটি পঠন-সংশোধন-লেখায় ফোকাস করে।

lockউপসর্গ সমগ্র অপারেশন সিস্টেমের মধ্যে সম্ভাব্য সব পর্যবেক্ষক থেকে সম্মান সঙ্গে পারমাণবিক করতে অনেক পাঠযোগ্য সংশোধন-রাইট (মেমরি গন্তব্য) নির্দেশাবলীর প্রয়োগ করা যেতে পারে (অন্যান্য কোর এবং, DMA ডিভাইস, না একটি ফুটিয়ে তোলা যায় সিপিইউ পিনের আপ লাগানো)। যে কারণে এটি বিদ্যমান। ( এই প্রশ্নোত্তরও দেখুন )

সুতরাং lock add dword [num], 1 হয় পারমাণবিক । একটি সিপিইউ কোর যে নির্দেশটি চালাচ্ছে সেটি ক্যাশ লাইনটিকে তার ব্যক্তিগত এল 1 ক্যাশে পরিবর্তিত অবস্থায় রেখে দেবে যখন লোডটি ক্যাশে থেকে ডেটা পড়বে ততক্ষণ স্টোর তার ফলাফলকে ক্যাশে ফেরত পাঠায় না। এটি MESI ক্যাশে কোহেরেন্সি প্রোটোকলের নিয়ম অনুযায়ী (বা মাল্টি-কোর এএমডি / দ্বারা ব্যবহৃত MOESI / MESIF সংস্করণগুলির নিয়ম অনুসারে) সিস্টেমের অন্য কোনও ক্যাশে লোড থেকে স্টোর পর্যন্ত যে কোনও সময়ে ক্যাশে লাইনের অনুলিপি থেকে বাধা দেয় ts যথাক্রমে ইন্টেল সিপিইউ)। সুতরাং, অন্যান্য কোর দ্বারা পরিচালিত হয় আগে বা পরে ঘটেছিল সময়কালে হয় না।

lockউপসর্গ ব্যতীত অন্য একটি কোর ক্যাশের লাইনের মালিকানা নিতে পারে এবং এটি আমাদের লোডের পরে কিন্তু আমাদের স্টোরের আগে সংশোধন করতে পারে, যাতে অন্য স্টোরটি আমাদের লোড এবং স্টোরের মধ্যে বিশ্বব্যাপী দৃশ্যমান হয়। আরও বেশ কয়েকটি উত্তর এটি ভুল পেয়েছে এবং দাবি করে যে lockআপনি একই ক্যাশে লাইনের বিরোধী অনুলিপিগুলি না পেলে। সুসংগত ক্যাশেযুক্ত সিস্টেমে এটি কখনই ঘটতে পারে না।

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

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


একটি uniprocessor মেশিন উপর, অথবা একটি একক থ্রেডেড প্রক্রিয়ায় , একটি একক RMW নির্দেশ আসলে হয় একটি ছাড়া পারমাণবিক lockউপসর্গ। ভাগ করা ভেরিয়েবল অ্যাক্সেস করার জন্য অন্য কোডের একমাত্র উপায় হ'ল সিপিইউ একটি প্রসঙ্গ সুইচ করা, যা কোনও নির্দেশের মাঝামাঝি ঘটতে পারে না। সুতরাং একটি সমতল dec dword [num]একক থ্রেডেড প্রোগ্রাম এবং এর সিগন্যাল হ্যান্ডলারগুলির মধ্যে বা একক-কোর মেশিনে চলমান বহু-থ্রেড প্রোগ্রামে সিঙ্ক্রোনাইজ করতে পারে। দেখুন অন্য প্রশ্নে আমার উত্তর দ্বিতীয়ার্ধে , এবং এটি অধীনে মন্তব্য, যেখানে আমি বিশদ আলোচনা করা এই ব্যাখ্যা।


সি ++ এ ফিরুন:

সংকলকটি না জানিয়ে num++এটি ব্যবহার করা সম্পূর্ণরূপে বোগাস যা আপনার প্রয়োজন একটি একক পঠন-পরিবর্তন-লিখন প্রয়োগের জন্য সংকলন করার:

;; Valid compiler output for num++
mov   eax, [num]
inc   eax
mov   [num], eax

এটি যদি আপনি numপরে এর মান ব্যবহার করেন তবে সম্ভবত : সংকলক বর্ধিত হওয়ার পরে এটি একটি রেজিস্টারে লাইভ রাখবে। সুতরাং আপনি num++নিজের থেকে কীভাবে সংকলন করে তা পরীক্ষা করে দেখুন, পার্শ্ববর্তী কোডটি পরিবর্তন করা এটিকে প্রভাবিত করতে পারে।

(যদি মানটির পরে প্রয়োজন না হয় তবে তা inc dword [num]পছন্দসই হয়; আধুনিক x86 সিপিইউগুলি তিনটি পৃথক নির্দেশনা ব্যবহার করার মতো কমপক্ষে দক্ষতার সাথে একটি মেমরি-গন্তব্য আরএমডাব্লু নির্দেশ পরিচালনা gcc -O3 -m32 -mtune=i586করবে Fun মজাদার ঘটনা: আসলে এটি নির্গত হবে , কারণ (পেন্টিয়াম) পি 5 এর সুপারক্যালার পাইপলাইনটি না একাধিক সাধারণ মাইক্রো-অপারেশনগুলির জটিল নির্দেশগুলি পি 6 এবং পরবর্তী মাইক্রোআরকিটেকচারগুলি ডিকোড করবেন না more আরও তথ্যের জন্য অ্যাগনার ফগের নির্দেশ সারণী / মাইক্রোআরকিটেকচার গাইড এবং দেখুন অনেকগুলি দরকারী লিঙ্কের জন্য ট্যাগ উইকি ট্যাগ করুন (ইন্টেলের x86 আইএসএ ম্যানুয়ালগুলি, যা পিডিএফ হিসাবে নির্বিঘ্নে উপলব্ধ)।


লক্ষ্য মেমরি মডেল (x86) কে সি ++ মেমরি মডেলের সাথে গুলিয়ে ফেলবেন না

সংকলন-সময় পুনর্নির্মাণ অনুমোদিত । আপনি স্টাড :: পারমাণবিক সাথে যা পান তার অন্য অংশটি কেবল অন্যnum++কোনও ক্রিয়াকলাপের পরেআপনারবিশ্বব্যাপী দৃশ্যমানহয় তা নিশ্চিত করার জন্য, সংকলন-সময় পুনর্নির্মাণের উপর নিয়ন্ত্রণ।

ক্লাসিক উদাহরণ: আরেকটি থ্রেড দেখার জন্য বাফারে কিছু ডেটা সংরক্ষণ করা, তারপরে একটি পতাকা সেট করা। যদিও x86 নিখরচায় লোড / রিলিজ স্টোর অর্জন করে, তবুও আপনাকে কম্পাইলারটি ব্যবহার করে পুনরায় অর্ডার না করতে বলতে হবে flag.store(1, std::memory_order_release);

আপনি আশা করতে পারেন যে এই কোডটি অন্য থ্রেডের সাথে সিঙ্ক্রোনাইজ করবে:

// flag is just a plain int global, not std::atomic<int>.
flag--;       // This isn't a real lock, but pretend it's somehow meaningful.
modify_a_data_structure(&foo);    // doesn't look at flag, and the compilers knows this.  (Assume it can see the function def).  Otherwise the usual don't-break-single-threaded-code rules come into play!
flag++;

তবে তা হবে না। flag++সংকলকটি ফাংশন কল জুড়ে সরানোর জন্য নিখরচায় (যদি এটি ফাংশনটির সাথে ইনলাইন করে থাকে বা জানে যে এটি তাকান না flag)। তারপরে এটি পুরোপুরি পরিবর্তনটি অপ্টিমাইজ করতে পারে, কারণ flagএটি এমনকি নয় volatile। (এবং না, সি ++ volatileস্ট্যান্ডার্ড :: পারমাণবিক। এসটিডি :: পারমাণবিকের জন্য কোনও কার্যকর বিকল্প নয়, এটি কম্পাইলারকে ধরে নিয়েছে যে মেমরির মানগুলি সংকীর্ণভাবে অনুরূপভাবে সংশোধন করা যেতে পারে volatile, তবে এর চেয়ে আরও অনেক কিছু রয়েছে Also এছাড়াও, volatile std::atomic<int> fooএটি নয় একই হিসাবে std::atomic<int> foo, যেমন @Richard Hodges সঙ্গে আলোচনা করেছেন।)

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


যেমনটি আমি উল্লেখ করেছি, x86 lockউপসর্গটি একটি সম্পূর্ণ মেমরি বাধা, তাই num.fetch_add(1, std::memory_order_relaxed);x86 এ একই কোডটি উত্পন্ন করে num++(ডিফল্টটি ক্রমযুক্ত ধারাবাহিকতা হয়) তবে এটি অন্যান্য আর্কিটেকচারে (যেমন এআরএম) আরও কার্যকর হতে পারে। এমনকি x86-এও রিল্যাক্সড আরও সংকলন-সময় পুনরায় অর্ডারিংয়ের অনুমতি দেয়।

std::atomicগ্লোবাল ভেরিয়েবলের উপর পরিচালিত কয়েকটি ফাংশনের জন্য এটি জিসিসি আসলে x86 এ করে ।

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

#include <atomic>
std::atomic<int> num;
void inc_relaxed() {
  num.fetch_add(1, std::memory_order_relaxed);
}

int load_num() { return num; }            // Even seq_cst loads are free on x86
void store_num(int val){ num = val; }
void store_num_release(int val){
  num.store(val, std::memory_order_release);
}
// Can the compiler collapse multiple atomic operations into one? No, it can't.

# g++ 6.2 -O3, targeting x86-64 System V calling convention. (First argument in edi/rdi)
inc_relaxed():
    lock add        DWORD PTR num[rip], 1      #### Even relaxed RMWs need a lock. There's no way to request just a single-instruction RMW with no lock, for synchronizing between a program and signal handler for example. :/ There is atomic_signal_fence for ordering, but nothing for RMW.
    ret
inc_seq_cst():
    lock add        DWORD PTR num[rip], 1
    ret
load_num():
    mov     eax, DWORD PTR num[rip]
    ret
store_num(int):
    mov     DWORD PTR num[rip], edi
    mfence                          ##### seq_cst stores need an mfence
    ret
store_num_release(int):
    mov     DWORD PTR num[rip], edi
    ret                             ##### Release and weaker doesn't.
store_num_relaxed(int):
    mov     DWORD PTR num[rip], edi
    ret

ক্রম-ধারাবাহিকতা স্টোরগুলির পরে কীভাবে এমএফএনসিইএন (সম্পূর্ণ বাধা) দরকার তা লক্ষ করুন। x86 সাধারণভাবে দৃ general়ভাবে অর্ডার করা হয় তবে স্টোরলড পুনরায় অর্ডার করার অনুমতি দেওয়া হয়। পাইপলাইনের আউট-অফ-অর্ডার সিপিইউতে ভাল পারফরম্যান্সের জন্য স্টোর বাফার থাকা অপরিহার্য। আইনটিতে জেফ প্রেসিংয়ের মেমোরি রির্ডারিং ক্যাচড রিয়েলর্ডিং রিয়েল হার্ডওয়ারে ঘটতে দেখানোর জন্য রিয়েল কোড সহ এমএফএনএনসিইএস ব্যবহার না করার পরিণতিগুলি দেখায় ।


পুনরায়: স্টাইল :: পারমাণবিক num++; num-=2;ক্রিয়াকলাপগুলিকে এক num--;নির্দেশে মার্জ করে সংকলকগণ সম্পর্কে @ রিচার্ড হজেসের উত্তরের মন্তব্যে আলোচনা :

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

বর্তমান সংকলকরা আসলে এটি (এখনও) করেন না, তবে তাদের অনুমতি দেওয়া হয়নি বলে নয়। সি ++ ডাব্লুজি 21 / P0062R1: সংকলকরা কখন পারমাণবিকতা অনুকূল করতে হবে? অনেক প্রোগ্রামারদের এমন প্রত্যাশা নিয়ে আলোচনা করা হয় যে সংকলকরা "আশ্চর্যজনক" অপ্টিমাইজেশান তৈরি করে না, এবং প্রোগ্রামারদের নিয়ন্ত্রণ দেওয়ার জন্য স্ট্যান্ডার্ড কী করতে পারে। N4455 এমন অনেকগুলি বিষয় নিয়ে আলোচনা করেছে যা এইগুলি সহ অনুকূলিত করা যায়। এটি উল্লেখ করে যে অন্তর্নিহিত এবং ধ্রুবক-প্রসারণ এমন কিছু জিনিস প্রবর্তন fetch_or(0)করতে পারে যা load()মূল উত্সটিতে কোনও স্পষ্টত অপ্রয়োজনীয় পারমাণবিক অপ্স না থাকলেও কেবলমাত্র (তবে এখনও শব্দার্থকে অর্জন ও প্রকাশ করতে পারে) রূপান্তর করতে সক্ষম হতে পারে ।

সংকলকগণ এটি না করার (এখনও) হ'ল: (১) জটিল জটিল কোডটি কেউ লেখেনি যে সংকলকটি নিরাপদে এটি করতে দেয় (কখনও এটি ভুল না করে), এবং (২) এটি সম্ভাব্যত ন্যূনতম নীতির লঙ্ঘন করে অবাক । লক-ফ্রি কোডটি প্রথম স্থানে সঠিকভাবে লেখার পক্ষে যথেষ্ট শক্ত। সুতরাং আপনার পারমাণবিক অস্ত্রের ব্যবহারে নৈমিত্তিক হবেন না: সেগুলি সস্তা নয় এবং বেশি অনুকূলিতকরণ করে না। অপ্রয়োজনীয় পারমাণবিক ক্রিয়াকলাপগুলি এড়ানো সর্বদা সহজ নয় std::shared_ptr<T>, যদিও এটির কোনও অ-পরমাণু সংস্করণ নেই (যদিও এখানে উত্তরগুলির মধ্যে একটি shared_ptr_unsynchronized<T>জিসিসি জন্য একটি সংজ্ঞায়নের একটি সহজ উপায় দেয় )।


এ ফেরা num++; num-=2;সংকলন যেমন যদি এটা ছিল num--কম্পাইলার: অনুমতি দেওয়া হয় এই কাজ করতে, যদি না numহয় volatile std::atomic<int>। যদি একটি পুনঃনির্মাণ করা সম্ভব হয় তবে বিস্মিত বিধি নিয়ম সংকলককে সংকলনের সময় সিদ্ধান্ত নিতে দেয় যে এটি সর্বদা সেভাবেই ঘটে। কোনও পর্যবেক্ষক অন্তর্বর্তী মানগুলি ( num++ফলাফল) দেখতে পাবে এমন কোনও গ্যারান্টি নেই ।

উদাহরণস্বরূপ, যদি এই ক্রিয়াকলাপগুলির মধ্যে বিশ্বব্যাপী কিছুই দৃশ্যমান না হয় এমন ক্রম উত্সের ক্রম প্রয়োজনীয়তার সাথে সামঞ্জস্যপূর্ণ (লক্ষ্য স্থিতি নয়, বিমূর্ত মেশিনের সি ++ নিয়ম অনুসারে), সংকলকটি / এর lock dec dword [num]পরিবর্তে একটি একক নির্গত করতে পারে ।lock inc dword [num]lock sub dword [num], 2

num++; num--অদৃশ্য হতে পারে না, কারণ এটি এখনও অন্য থ্রেডগুলির সাথে সম্পর্কের সাথে একটি সিঙ্ক্রোনাইজ করে numএবং এটি একটি অর্জন-লোড এবং একটি রিলিজ-স্টোর উভয়ই যা এই থ্রেডের অন্যান্য ক্রিয়াকলাপের পুনঃক্রমকে অস্বীকার করে। X86 এর জন্য, এটি কোনও lock add dword [num], 0(ie num += 0) এর পরিবর্তে কোনও MFENCE- এ সংকলন করতে সক্ষম হতে পারে ।

PR0062 এ আলোচিত হিসাবে , সংকলনের সময় অ-সংলগ্ন পারমাণবিক অপের আরও আক্রমণাত্মক সংযোজন খারাপ হতে পারে (উদাহরণস্বরূপ একটি অগ্রগতি কাউন্টার কেবল প্রতিটি পুনরাবৃত্তির পরিবর্তে একবারে আপডেট হয়), তবে এটি ডাউনসাইড ছাড়াই পারফরম্যান্সে সহায়তা করতে পারে (উদাহরণস্বরূপ এড়িয়ে যাওয়া) পারমাণবিক ইনক / ডিসি রেফ গণনা করা হয় যখন কোনওটির অনুলিপি shared_ptrতৈরি করা হয় এবং ধ্বংস হয়, যদি সংকলক প্রমাণ করতে পারে যে shared_ptrঅস্থায়ী পুরো জীবনকাল জন্য অন্য কোনও বস্তুর উপস্থিতি রয়েছে))

এমনকি num++; num--একত্রিত হওয়া কোনও লক প্রয়োগের ন্যায্যতার ক্ষতি করতে পারে যখন একটি থ্রেড এখনই আনলক হয় এবং পুনরায় লক হয়। এটি প্রকৃতপক্ষে কখনই প্রকাশিত হয় না, এমনকি হার্ডওয়্যার আরবিট্রেশন প্রক্রিয়াগুলি অন্য থ্রেডটিকে সেই সময়ে লকটি ধরার সুযোগ দেয় না।


বর্তমান জিসিসি .2.২ এবং ক্ল্যাং ৩.৯ সহ, আপনি এখনও সর্বাধিক স্পষ্টতই অপটিমাইজযোগ্য ক্ষেত্রে পৃথক lockএড ক্রিয়াকলাপগুলি memory_order_relaxedপান। ( গডবোল্ট সংকলক এক্সপ্লোরার যাতে আপনি দেখতে পারেন যে সর্বশেষতম সংস্করণগুলি আলাদা কিনা if)

void multiple_ops_relaxed(std::atomic<unsigned int>& num) {
  num.fetch_add( 1, std::memory_order_relaxed);
  num.fetch_add(-1, std::memory_order_relaxed);
  num.fetch_add( 6, std::memory_order_relaxed);
  num.fetch_add(-5, std::memory_order_relaxed);
  //num.fetch_add(-1, std::memory_order_relaxed);
}

multiple_ops_relaxed(std::atomic<unsigned int>&):
    lock add        DWORD PTR [rdi], 1
    lock sub        DWORD PTR [rdi], 1
    lock add        DWORD PTR [rdi], 6
    lock sub        DWORD PTR [rdi], 5
    ret

1
"আরও দক্ষ ব্যবহার করা হয় [পৃথক নির্দেশাবলী ব্যবহার] ... কিন্তু আধুনিক x86 সিপিইউ আবার দক্ষতার হিসাবে অন্তত RMW অপারেশন হ্যান্ডেল" - এটা এখনও ক্ষেত্রে যেখানে আপডেট মান একই ফাংশন পরবর্তী ব্যবহার করা হবে আরও কার্যকরী এবং এটি সংরক্ষণ করার জন্য কম্পাইলারের জন্য একটি নিখরচায় নিবন্ধ রয়েছে (এবং অবশ্যই ভেরিয়েবলটি অস্থির হিসাবে চিহ্নিত নয়)। এর অর্থ হ'ল এটি খুব সম্ভবত যে সংকলকটি কোনও একক নির্দেশনা উত্পন্ন করে বা অপারেশনের জন্য একাধিক উত্পন্ন করে কিনা তা কেবল ফাংশনের বাকী কোডের উপর নির্ভর করে, কেবলমাত্র একক রেখায় নয় not
পেরিটা ব্রেটাটা

@ পেরিয়াটাব্রেটা: হ্যাঁ, ভাল কথা। Asm এ আপনি mov eax, 1 xadd [num], eaxপোস্ট-ইনক্রিমেন্ট বাস্তবায়নের জন্য (কোনও লক প্রিফিক্স সহ) ব্যবহার করতে পারেন তবে সংকলকরা num++এটি করেন না।
পিটার কর্ডেস

3
@ ডেভিডসি। র্যাঙ্কিন: আপনি যদি করতে চান এমন কোনও সম্পাদনা থাকে তবে নির্দ্বিধায়। যদিও আমি এই সিডাব্লু বানাতে চাই না। এটি এখনও আমার কাজ (এবং আমার গণ্ডগোল: পি)। আমি আমার চূড়ান্ত [ফ্রিসবি] খেলার পরে কিছুটা পরিষ্কার করব :)
পিটার কর্ডেস

1
যদি সম্প্রদায় উইকি না হয় তবে উপযুক্ত ট্যাগ উইকের লিঙ্ক হতে পারে। (উভয় x86 এবং পারমাণবিক ট্যাগ?)। এসও-তে জেনেরিক অনুসন্ধানের মাধ্যমে আশাবাদী প্রত্যাবর্তনের চেয়ে এটি অতিরিক্ত সংযোগের মতো মূল্যবান (যদি আমি আরও ভালভাবে জানতাম যে এটির ক্ষেত্রে এটি কোথায় ফিট করা উচিত তবে আমি এটি করতাম the উইকি লিঙ্কেজ)
ডেভিড সি র্যাঙ্কিন

1
সর্বদা হিসাবে - দুর্দান্ত উত্তর! সংহতি এবং পারমাণবিকতার মধ্যে ভাল পার্থক্য (যেখানে অন্য কেউ এটি ভুল পেয়েছে)
লিওর

39

... এবং এখন আসুন অনুকূলিতকরণ সক্ষম করুন:

f():
        rep ret

ঠিক আছে, আসুন এটি একটি সুযোগ দিন:

void f(int& num)
{
  num = 0;
  num++;
  --num;
  num += 6;
  num -=5;
  --num;
}

ফলাফল:

f(int&):
        mov     DWORD PTR [rdi], 0
        ret

অন্য পর্যবেক্ষণের থ্রেড (এমনকি ক্যাশে সিঙ্ক্রোনাইজেশন বিলম্বগুলি উপেক্ষা করেও) পৃথক পরিবর্তনগুলি পর্যবেক্ষণ করার কোনও সুযোগ নেই।

তুলনা করা:

#include <atomic>

void f(std::atomic<int>& num)
{
  num = 0;
  num++;
  --num;
  num += 6;
  num -=5;
  --num;
}

ফলাফল যেখানে:

f(std::atomic<int>&):
        mov     DWORD PTR [rdi], 0
        mfence
        lock add        DWORD PTR [rdi], 1
        lock sub        DWORD PTR [rdi], 1
        lock add        DWORD PTR [rdi], 6
        lock sub        DWORD PTR [rdi], 5
        lock sub        DWORD PTR [rdi], 1
        ret

এখন, প্রতিটি পরিবর্তনটি হ'ল: -

  1. অন্য থ্রেডে পর্যবেক্ষণযোগ্য এবং
  2. অন্যান্য থ্রেডে ঘটছে অনুরূপ পরিবর্তন সম্মান।

পারমাণবিকতা কেবলমাত্র নির্দেশের স্তরে নয়, এটি প্রসেসর থেকে শুরু করে ক্যাশগুলির মাধ্যমে, মেমরি এবং পিছনে পুরো পাইপলাইন জড়িত।

আরও তথ্য

এর আপডেটগুলির অপ্টিমাইজেশনের প্রভাব সম্পর্কে std::atomic

সি ++ স্ট্যান্ডার্ডের 'যদি' নিয়ম রয়েছে, যার মাধ্যমে সংকলকটির কোডটি পুনরায় অর্ডার করা জায়েজ এবং এমনকি কোডটি পুনর্লিখন করে তবে শর্ত হয় যে ফলাফলটি ঠিক একই পর্যবেক্ষণযোগ্য প্রভাব (পার্শ্ব-প্রতিক্রিয়া সহ) রয়েছে যেমন এটি কেবল আপনার কার্যকর করা হয়েছিল কোড।

বিধি-বিধানটি রক্ষণশীল, বিশেষত পরমাণুর সাথে জড়িত।

বিবেচনা:

void incdec(int& num) {
    ++num;
    --num;
}

যেহেতু আন্তঃ থ্রেড সিকোয়েন্সিংকে প্রভাবিত করে এমন কোনও মিটেক্স লক, অ্যাটমিক্স বা অন্য কোনও নির্মাণ নেই, তাই আমি যুক্তি দিয়ে বলতে পারি যে সংকলকটি এই ফাংশনটিকে একটি এনওপি হিসাবে পুনরায় লিখতে মুক্ত, যেমন:

void incdec(int&) {
    // nada
}

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

তবে এটি একটি ভিন্ন বলের খেলা:

void incdec(std::atomic<int>& num) {
    ++num;
    --num;
}

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

এখানে একটি ডেমো রয়েছে:

#include <thread>
#include <atomic>

int main()
{
    for (int iter = 0 ; iter < 20 ; ++iter)
    {
        std::atomic<int> num = { 0 };
        std::thread t1([&] {
            for (int i = 0 ; i < 10000000 ; ++i)
            {
                ++num;
                --num;
            }
        });
        std::thread t2([&] {
            for (int i = 0 ; i < 10000000 ; ++i)
            {
                num = 100;
            }
        });
        
        t2.join();
        t1.join();
        std::cout << num << std::endl;
    }
}

নমুনা আউটপুট:

99
99
99
99
99
100
99
99
100
100
100
100
99
99
100
99
99
100
100
99

5
এই ব্যাখ্যা করতে এটি ব্যর্থ হয় add dword [rdi], 1হয় না পারমাণবিক (ছাড়া lockউপসর্গ)। লোডটি পারমাণবিক, এবং স্টোরটি পারমাণবিক, তবে কিছুই লোড এবং স্টোরের মধ্যে ডেটা পরিবর্তন করতে অন্য থ্রেডকে থামায় না। সুতরাং স্টোরটি অন্য থ্রেড দ্বারা তৈরি একটি পরিবর্তনের পদক্ষেপ নিতে পারে। Jfdube.wordpress.com/2011/11/30/ বোঝার -টমিক-অপারেশন দেখুন । এছাড়াও, জেফ প্রেসিং-এর লক-ফ্রি নিবন্ধগুলি খুব ভাল , এবং তিনি সেই অন্তর্ভুক্ত নিবন্ধে বেসিক আরএমডাব্লু সমস্যা উল্লেখ করেছেন।
পিটার কর্ডস

3
এখানে সত্যই যা চলছে তা হ'ল যে কেউ এই অপ্টিমাইজেশনটি জিসিটিতে প্রয়োগ করেনি, কারণ এটি প্রায় অকেজো এবং সম্ভবত সহায়তার চেয়ে আরও বিপজ্জনক হবে। (পুঁজি অন্তত আশ্চর্য। হয়তো কেউ হয় একটি অস্থায়ী রাষ্ট্র কখনও কখনও দৃশ্যমান হতে আশা, এবং পরিসংখ্যান probabilty ঠিক মনে হয়। অথবা তারা করছে হার্ডওয়্যার ঘড়ি-পয়েন্ট ব্যবহার পরিমার্জন উপর ছিন্ন করা।) সাবধানে crafted করা লক-বিনামূল্যে কোড প্রয়োজন, সুতরাং অনুকূলিতকরণ কিছুই হবে না। কোডারকে সতর্ক করার জন্য এটির সন্ধান এবং একটি সতর্কতা মুদ্রণ করা কার্যকর হতে পারে যে তাদের কোডটির অর্থ তারা কী ভাবেন তা বোঝাতে পারে না!
পিটার কর্ডেস

2
সংকলকরা এটি বাস্তবায়ন না করার সম্ভবত কারণ (অন্তত বিস্ময়ের নীতি এবং আরও কিছু)। বাস্তব পর্যবেক্ষণে এটি অনুশীলন করা সম্ভব হবে। তবে, সি ++ মেমরি অর্ডার করার নিয়মগুলি কোনও গ্যারান্টি সম্পর্কে কিছুই বলে না যে একটি থ্রেডের বোঝা সি ++ অ্যাবস্ট্রাক্ট মেশিনে অন্য থ্রেডের অপের সাথে "সমানভাবে" মিশে যায়। আমি এখনও মনে করি এটি আইনী হবে, তবে প্রোগ্রামার-বৈরী।
পিটার কর্ডস

2
চিন্তার পরীক্ষা: সমবায় মাল্টি-টাস্কিং সিস্টেমে সি ++ বাস্তবায়ন বিবেচনা করুন। এটি স্টাড :: থ্রেড কার্যকর করে যেখানে ফলন পয়েন্টগুলি সন্নিবেশ করানো হয় যেখানে অচলাবস্থা এড়াতে প্রয়োজন, তবে প্রতিটি নির্দেশের মধ্যে নয়। আমি অনুমান করি আপনি যুক্তি দিতেন যে সি ++ স্ট্যান্ডার্ডের কোনও কিছুর জন্য num++এবং এর মধ্যে একটি ফলন পয়েন্ট দরকার num--আপনি যদি স্ট্যান্ডার্ডের এমন একটি বিভাগ খুঁজে পান যার জন্য এটির প্রয়োজন হয় তবে এটি এটি নিষ্পত্তি করবে। আমি দৃ sure়ভাবে নিশ্চিত যে এটির জন্য কেবল কোনও পর্যবেক্ষকই কোনও ভুল পুনরায় অর্ডারিং দেখতে পাবেন না, যার ফলন প্রয়োজন হয় না। সুতরাং আমি মনে করি এটি কেবলমাত্র বাস্তবায়নের সমস্যা issue
পিটার কর্ডস

5
চূড়ান্ততার স্বার্থে, আমি স্টাডি আলোচনা মেলিংয়ের তালিকায় জিজ্ঞাসা করেছি। এই প্রশ্নটি 2 টি কাগজপত্রে পরিণত হয়েছে যা পিটারের সাথে উভয়ই সম্মত বলে মনে হয়েছে এবং এমন উদ্বেগের বিষয়ে আমার যে উদ্বেগ রয়েছে তার সমাধান করুন: wg21.link/p0062 এবং wg21.link/n4455 অ্যান্ডির প্রতি আমার ধন্যবাদ যারা এই বিষয়গুলি আমার নজরে এনেছে।
রিচার্ড হোজেস

38

অনেক জটিলতা ছাড়াই একটি নির্দেশনা add DWORD PTR [rbp-4], 1খুব সিআইএসসি-স্টাইল।

এটি তিনটি অপারেশন সম্পাদন করে: মেমরি থেকে অপারেন্ড লোড করুন, এটি বৃদ্ধি করুন, অপারেন্ডটিকে মেমোরিতে ফিরিয়ে রাখুন।
এই ক্রিয়াকলাপগুলির সময়, সিপিইউ দুটিবার বাস অর্জন এবং ছেড়ে দেয়, অন্য কোনও এজেন্টের মধ্যে এটিও অর্জন করতে পারে এবং এটি পারমাণবিকতা লঙ্ঘন করে।

AGENT 1          AGENT 2

load X              
inc C
                 load X
                 inc C
                 store X
store X

এক্স শুধুমাত্র একবার বৃদ্ধি করা হয়।


7
@ লিওহিনসর এটি হওয়ার জন্য, প্রতিটি মেমোরি চিপের নিজস্ব গাণিতিক যুক্তি ইউনিট (এএলইউ) প্রয়োজন হবে। এটা তোলে বস্তুত, করতে হবে প্রতিটি মেমরি চিপ ছিল একটি প্রসেসর।
রিচার্ড হোজেস

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

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

প্রশ্নের মন্তব্যটিকে সি ++ অংশে সম্বোধন সহ একটি পূর্ণ উত্তরে পরিণত করেছে।
পিটার কর্ডেস

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

11

অ্যাড নির্দেশিকাটি পারমাণবিক নয় । এটি মেমরিটির উল্লেখ করে এবং দুটি প্রসেসরের কোরের সেই মেমরির স্থানীয় স্থানীয় ক্যাশে থাকতে পারে।

আইআইআরসি অ্যাড নির্দেশের পারমাণবিক রূপকে লক এক্সএডিডি বলে


3
lock xaddC ++ std :: পারমাণবিক প্রয়োগ করে fetch_add, পুরানো মানটি ফিরিয়ে দেয়। আপনার যদি এটির প্রয়োজন না হয় তবে সংকলকটি একটি lockপূর্বের সাথে সাধারণ মেমরি গন্তব্য নির্দেশাবলী ব্যবহার করবে । lock addবা lock inc
পিটার কর্ডেস

1
add [mem], 1কোনও ক্যাশে ছাড়াই কোনও এসএমপি মেশিনে এখনও পারমাণবিক হবে না, অন্য উত্তরে আমার মন্তব্য দেখুন।
পিটার কর্ডেস

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

10

যেহেতু লাইন 5 লাইনটি নম্ব ++ এর সাথে অনুরূপ, সেগুলিই একটি নির্দেশনা, তাই আমরা কী সিদ্ধান্ত নিতে পারি যে নাম ++ এই ক্ষেত্রে পারমাণবিক?

"বিপরীত প্রকৌশল" উত্পন্ন সমাবেশের উপর ভিত্তি করে সিদ্ধান্তগুলি আঁকা বিপজ্জনক। উদাহরণস্বরূপ, আপনি নিজের কোডটি অপ্টিমাইজেশান অক্ষম করে সংকলন করেছেন বলে মনে হয়, অন্যথায় সংকলকটি বিন্যাস না করে সরাসরি সেই পরিবর্তনশীলটিকে 1 বা এটিকে লোড করে ফেলেত operator++। উত্পন্ন সমাবেশটি অপ্টিমাইজেশন পতাকা, টার্গেট সিপিইউ ইত্যাদির উপর ভিত্তি করে উল্লেখযোগ্যভাবে পরিবর্তন করতে পারে বলে আপনার উপসংহারটি বালির উপর ভিত্তি করে।

এছাড়াও, আপনার ধারণা যে একটি সমাবেশ নির্দেশের অর্থ একটি অপারেশনটিও পারমাণবিক হয় তাও ভুল। এটি addমাল্টি-সিপিইউ সিস্টেমে এমনকি এক্স x আর্কিটেকচারেও পারমাণবিক হবে না।


9

এমনকি যদি আপনার সংকলক সর্বদা এটি পারমাণবিক অপারেশন হিসাবে নির্গত করে, numঅন্য যে কোনও থ্রেড থেকে একই সাথে অ্যাক্সেস করা সি ++ 11 এবং সি ++ 14 স্ট্যান্ডার্ড অনুযায়ী ডেটা রেস গঠন করে এবং প্রোগ্রামটির অপরিবর্তিত আচরণ থাকতে পারে।

তবে এটি তার চেয়েও খারাপ। প্রথমত, যেমনটি উল্লেখ করা হয়েছে, কম্পাইলার দ্বারা যখন কোনও ভেরিয়েবল বাড়ানো হয় তখন নির্দেশটি অনুকূলিতকরণ স্তরের উপর নির্ভর করতে পারে। দ্বিতীয়ত, সংকলক যদি পারমাণবিক না হয় তবে অন্য মেমরির অ্যাক্সেসগুলি চারদিকে পুনঃক্রম করতে পারে++numnum

int main()
{
  std::unique_ptr<std::vector<int>> vec;
  int ready = 0;
  std::thread t{[&]
    {
       while (!ready);
       // use "vec" here
    });
  vec.reset(new std::vector<int>());
  ++ready;
  t.join();
}

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

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

সর্বশেষে, এমনকি যদি আপনি বহনযোগ্যতা যত্ন সম্পর্কে না, এবং আপনার কম্পাইলার জাদুর সুন্দর ছিল, CPU- র ব্যবহার করছেন একটি superscalar CISC ধরনের খুব সম্ভবত এবং মাইক্রো-অপস, পুনর্বিন্যাস এবং / অনুমানজনিতভাবে বা তাদের চালানো মধ্যে নির্দেশাবলী ভেঙে ফেলব LOCKসেকেন্ডে সর্বাধিক ক্রিয়াকলাপকে সর্বাধিকীকরণের জন্য প্রিফিক্স বা মেমরি বেড়ার মতো (ইন্টেলের উপর) প্রিমিটিভগুলি সিঙ্ক্রোনাইজ করার মাধ্যমে সীমাবদ্ধ ।

দীর্ঘ গল্প সংক্ষিপ্ত করতে থ্রেড-সেফ প্রোগ্রামিংয়ের প্রাকৃতিক দায়িত্ব হ'ল:

  1. আপনার দায়িত্ব হ'ল কোডটি লিখুন যা ভাষার নিয়মের অধীনে (এবং বিশেষত ভাষা স্ট্যান্ডার্ড মেমরির মডেল) এর সাথে সু-সংজ্ঞায়িত আচরণ করে।
  2. আপনার সংকলকের দায়িত্ব হ'ল মেশিন কোড তৈরি করা যা লক্ষ্য স্থাপত্যের মেমরির মডেলের অধীনে একই সু-সংজ্ঞায়িত (পর্যবেক্ষণযোগ্য) আচরণ করে।
  3. আপনার সিপিইউর কর্তব্য এই কোডটি কার্যকর করা যাতে পর্যবেক্ষণ করা আচরণটি তার নিজস্ব আর্কিটেকচারের মেমরি মডেলের সাথে সামঞ্জস্য হয়।

যদি আপনি এটি নিজের উপায়ে করতে চান তবে এটি কিছু ক্ষেত্রে কেবল কার্যকর হতে পারে তবে বুঝতে হবে যে ওয়্যারেন্টিটি বাতিল, এবং কোনও অনাকাঙ্ক্ষিত ফলাফলের জন্য আপনি সম্পূর্ণ দায়বদ্ধ থাকবেন । :-)

পিএস: সঠিকভাবে লিখিত উদাহরণ:

int main()
{
  std::unique_ptr<std::vector<int>> vec;
  std::atomic<int> ready{0}; // NOTE the use of the std::atomic template
  std::thread t{[&]
    {
       while (!ready);
       // use "vec" here
    });
  vec.reset(new std::vector<int>());
  ++ready;
  t.join();
}

এটি নিরাপদ কারণ:

  1. readyভাষার নিয়ম অনুসারে চেকগুলি অপ্টিমাইজ করা যায় না।
  2. যা ++ready ঘটেছিল তার আগে - যা চেক readyশূন্য নয় এবং অন্যান্য ক্রিয়াকলাপগুলি এই ক্রিয়াকলাপগুলির চারপাশে পুনরায় সাজানো যায় না। এটি কারণ ++readyএবং চেকটি ধারাবাহিকভাবে সামঞ্জস্যপূর্ণ , যা সি ++ মেমরির মডেলটিতে বর্ণিত আরেকটি শব্দ এবং এটি এই নির্দিষ্ট পুনর্নির্মাণকে নিষেধ করে। সুতরাং সংকলক অবশ্যই নির্দেশাবলীর পুনঃক্রম করতে হবে না এবং সিপিইউকে অবশ্যই বলতে হবে যে এটি অবশ্যই vecবর্ধনের পরে লেখার জন্য স্থগিত করা উচিত নয় ready। ভাষার মান অনুসারে পরমাণু সম্পর্কিত সবচেয়ে শক্তিশালী গ্যারান্টি হল ধারাবাহিকভাবে সামঞ্জস্যপূর্ণ । কম (এবং তাত্ত্বিকভাবে সস্তা) গ্যারান্টি পাওয়া যায় যেমন অন্যান্য পদ্ধতির মাধ্যমেstd::atomic<T>, তবে এগুলি অবশ্যই বিশেষজ্ঞদের জন্য, এবং সংকলক বিকাশকারীরা খুব বেশি অনুকূলিত নাও হতে পারেন, কারণ এগুলি খুব কমই ব্যবহৃত হয়।

1
সংকলকটি যদি সমস্ত ব্যবহার দেখতে না পেত ready, তবে সম্ভবত এটি while (!ready);আরও ভাল কিছুতে সংকলিত হবে if(!ready) { while(true); }। উত্সাহিত: স্ট্যান্ড :: পারমাণবিকের একটি মূল অংশ যেকোনও সময় অ্যাসিক্রোনাস পরিবর্তন অনুমান করার জন্য শব্দার্থকে পরিবর্তন করছে। এটি সাধারণত ইউবি হওয়ার কারণে কম্পাইলাররা লোডগুলি উত্তোলন করতে এবং লুপের বাইরে স্টোরগুলি ডোবার অনুমতি দেয়।
পিটার কর্ডেস

9

একটি একক কোর এক্স 86 মেশিন, একটি addনির্দেশ সাধারণত CPU- র অন্যান্য কোড থেকে সম্মান সঙ্গে পারমাণবিক হতে হবে 1 । একটি বাধা কোনও একক নির্দেশকে মাঝখানে ভাগ করতে পারে না।

একক কোরের মধ্যে ক্রমবর্ধমান নির্দেশাবলীর মায়া রক্ষা করার জন্য আউট-অফ-অর্ডার এক্সিকিউশন প্রয়োজন, সুতরাং একই সিপিইউতে চলমান কোনও নির্দেশ অ্যাডের আগে বা সম্পূর্ণরূপে ঘটবে।

আধুনিক x86 সিস্টেমগুলি মাল্টি-কোর, তাই ইউনিকপ্রসেসর বিশেষ ক্ষেত্রে প্রযোজ্য না।

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

(এই, যদি আপনি C ++ করছি লেখা আপনি সাহায্য না যদিও। কম্পাইলার প্রয়োজন একটি বিকল্প না থাকে num++একটি মেমরি গন্তব্যস্থল যোগ করতে কম্পাইল বা xadd করার ছাড়া একটি lockউপসর্গ। তারা লোড করতে চয়ন করতে পারেন numএকটি রেজিস্টার এবং দোকান মধ্যে একটি পৃথক নির্দেশাবলী সহ বর্ধিত ফলাফল, এবং আপনি যদি ফলাফলটি ব্যবহার করেন তবে সম্ভবত এটি করবে)


পাদটীকা 1: lockউপসর্গটি মূল 8086 তেও বিদ্যমান ছিল কারণ I / O ডিভাইসগুলি সিপিইউয়ের সাথে একযোগে কাজ করে; সিঙ্গল-কোর সিস্টেমের ড্রাইভারদের lock addডিভাইস এটিকেও সংশোধন করতে পারে, বা ডিএমএ অ্যাক্সেসের ক্ষেত্রে ডিটেমি মেমোরির ক্ষেত্রে পারমাণবিকভাবে মূল্য বৃদ্ধি করতে হবে।


এটি এমনকি সাধারণভাবে পারমাণবিক নয়: অন্য একটি থ্রেড একই সময়ে একই পরিবর্তনশীল আপডেট করতে পারে এবং কেবলমাত্র একটি আপডেট নেওয়া হবে।
ফুজ

1
একটি বহু-কোর সিস্টেম বিবেচনা করুন। অবশ্যই, একটি মূলের মধ্যেই নির্দেশটি পারমাণবিক, তবে এটি পুরো সিস্টেমের ক্ষেত্রে পারমাণবিক নয়।
ফুজ

1
@FUZxxl: আমার উত্তরের চতুর্থ এবং পঞ্চম শব্দগুলি কী ছিল?
সুপারক্যাট

1
@ সুপের্যাট আপনার উত্তরটি অত্যন্ত বিভ্রান্তিমূলক কারণ এটি আজকাল বিরল ক্ষেত্রে কেবল একটি মাত্র কোরকে বিবেচনা করে এবং ওপিকে সুরক্ষার একটি মিথ্যা ধারণা দেয়। এজন্য আমি মাল্টি-কোর কেসটিও বিবেচনা করতে মন্তব্য করেছি।
ফুজ

1
@FUZxxl: আমি পাঠকদের পক্ষে সম্ভাব্য বিভ্রান্তি দূর করার জন্য একটি সম্পাদনা করেছি যারা খেয়াল করেনি যে এটি সাধারণ আধুনিক মাল্টিকোর সিপিইউ সম্পর্কে কথা বলছে না। (এবং এমন কিছু স্টাফ সম্পর্কে আরও সুনির্দিষ্ট হন যা সুপারক্যাট নিশ্চিত ছিল না)। বিটিডাব্লু, এই উত্তরের সমস্ত কিছু ইতিমধ্যে আমার মধ্যে রয়েছে, যেখানে প্লে-প্ল্যাটফর্মগুলি যেখানে পঠন-সংশোধন-লিখনকে পরমাণু "ফ্রি" জন্য বিরল তা সম্পর্কে শেষ বাক্যটি বাদে।
পিটার কর্ডেস

7

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

গ্রাহকের ডেস্কটপে যখন ডুয়াল প্রসেসর (যেমন ডুয়াল-সকেট পেন্টিয়াম প্রো) পাওয়া বিরল ছিল, তখন আমি কার্যকরভাবে এটি একটি একক কোর মেশিনে লক উপসর্গ এড়াতে এবং কর্মক্ষমতা উন্নত করতে ব্যবহার করেছিলাম।

আজ, এটি কেবলমাত্র একাধিক থ্রেডের বিরুদ্ধে সহায়তা করবে যা সমস্ত একই সিপিইউ অ্যাফিনিটিতে সেট করা ছিল, তাই আপনি যে থ্রেডগুলি নিয়ে উদ্বিগ্ন তা কেবল সময়ের স্লাইসের মেয়াদ শেষ হয়ে একই সিপিইউতে অন্য থ্রেড চালানোর মাধ্যমে কার্যকর হবে core এটা বাস্তববাদী নয়।

আধুনিক x86 / x64 প্রসেসরের সাহায্যে একক নির্দেশনাটি কয়েকটি মাইক্রো অপে বিভক্ত হয় এবং ততক্ষণে মেমরি পড়ার এবং লেখার বাফার হয়। সুতরাং বিভিন্ন সিপিইউতে চলমান বিভিন্ন থ্রেডগুলি কেবল এটি অ-পারমাণবিক হিসাবে দেখবে না তবে এটি মেমরি থেকে কী পড়বে এবং অন্যান্য থ্রেডগুলি কীভাবে সেই সময়টিতে পড়েছে তা নিয়ে অসামঞ্জস্যপূর্ণ ফলাফল দেখতে পাবেন: বুদ্ধি পুনরুদ্ধার করতে আপনাকে মেমরি বেড়া যুক্ত করতে হবে আচরণ।


1
ব্যাঘাত এখনও তাই তারা, না বিভক্ত RMW অপারেশন করবেন না এখনো সংকেত হ্যান্ডলার সঙ্গে একটি একক থ্রেড সুসংগত যে একই থ্রেড চালানো। অবশ্যই, এটি কেবল তখনই কার্যকর হয় যদি asm একটি একক নির্দেশনা ব্যবহার করে, আলাদা লোড / সংশোধন / স্টোর না করে। সি ++ 11 এই হার্ডওয়্যার কার্যকারিতাটি প্রকাশ করতে পারে, তবে এটি হয় না (সম্ভবত এটি ইউনিকপ্রসেসর কার্নেলগুলিতে ব্যাবহারকারী হ্যান্ডলারের সাথে সিঙ্ক্রোনাইজ করার জন্য কেবল কার্যকর ছিল, সংকেত হ্যান্ডলারের সাথে ব্যবহারকারীর জায়গায় নয়)। এছাড়াও আর্কিটেকচারে মেমরি-গন্তব্য নির্দেশাবলী পাঠ-সংশোধন / লিখতে হয় না। তবুও, এটি কেবলমাত্র নন- x86 on এর একটি শিথিল পারমাণবিক আরএমডাব্লুয়ের মতো সংকলন করতে পারে
পিটার

যদিও আমার মনে আছে, সুপার ক্যালাররা উপস্থিত না হওয়া পর্যন্ত লক উপসর্গটি ব্যবহার করা অযৌক্তিকভাবে ব্যয়বহুল ছিল না। সুতরাং কোনও 486-তে গুরুত্বপূর্ণ কোডটি ধীর করে দেওয়ার বিষয়টি লক্ষ্য করার কোনও কারণ নেই, যদিও সেই প্রোগ্রামটির প্রয়োজন হয়নি।
জেডিগোগস

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

1
@ পিটারকর্ডস ওকে। অবশ্যই, কোনও ডিএমএ / ডিভাইস পর্যবেক্ষকও ধরে নিচ্ছেন - মন্তব্য ক্ষেত্রের মধ্যে এটিরও অন্তর্ভুক্ত নয়। ধন্যবাদ জডিডুগোসকে দুর্দান্ত সংযোজনের জন্য (উত্তর হিসাবে ভাল মতামত)। সত্যই আলোচনা শেষ।
লিও হেইনসর

3
@ লিও: একটি মূল বিষয় যা উল্লেখ করা হয়নি: আউট-অফ-অর্ডার সিপিইউগুলি অভ্যন্তরীণভাবে পুনঃক্রম করতে থাকে তবে স্বর্ণের নিয়মটি হ'ল যে কোনও একক কোরের জন্য তারা এক সাথে এক সাথে চলমান নির্দেশাবলীর মায়া রক্ষা করে। (এবং এটিতে এমন বাধা অন্তর্ভুক্ত করে যা প্রসঙ্গের সুইচগুলিকে ট্রিগার করে)। মানগুলি বৈদ্যুতিনভাবে মেমোরিতে বিন্যস্ত হয়ে সঞ্চারিত হতে পারে, তবে একক মূল যা সমস্ত কিছু চালিত হয় তা মায়ু সংরক্ষণের জন্য, এটি নিজেই করা সমস্ত পুনঃক্রমের উপর নজর রাখে। a = 1; b = a;আপনার সঞ্চিত 1 টি সঠিকভাবে লোড করার জন্য asm সমতুল্যের জন্য আপনার কোনও মেমরি বাধা লাগবে না ।
পিটার কর্ডেস

4

নং https://www.youtube.com/watch?v=31g0YE61PLQ (এটি "অফিস" থেকে "না" দৃশ্যের কেবল একটি লিঙ্ক)

আপনি কি সম্মত হন যে এটি প্রোগ্রামের একটি সম্ভাব্য আউটপুট হবে:

নমুনা আউটপুট:

100
100
100
100
100
100
100
100
100
100
100
100
100
100
100
100
100
100
100
100

যদি তাই হয়, তবে সংকলকটি প্রোগ্রামটির জন্য একমাত্র সম্ভাব্য আউটপুট তৈরি করতে নির্দ্বিধায়, যেকোন উপায়ে সংকলকটি চায়। অর্থাত্ একটি প্রধান () যা কেবলমাত্র 100 এর বাইরে রাখে।

এটি "হিসাবে যদি" ​​নিয়ম।

এবং আউটপুট নির্বিশেষে আপনি থ্রেড সিঙ্ক্রোনাইজেশন মনে করতে পারেন একই ভাবে - যদি থ্রেড একটি করে num++; num--;এবং থ্রেড বি সার্চ numবারবার, তারপর একটি সম্ভাব্য বৈধ interleaving যে থ্রেড B এর মধ্যে কখনোই সার্চ num++এবং num--। যেহেতু ইন্টারলিভিং বৈধ, তাই সংকলকটি কেবল এটি তৈরি করতে বিনামূল্যে সম্ভাব্য ইন্টারলিভিং । এবং কেবল incr / decr সম্পূর্ণরূপে সরান।

এখানে কিছু আকর্ষণীয় প্রভাব রয়েছে:

while (working())
    progress++;  // atomic, global

(উদাহরণস্বরূপ, অন্য কোনও থ্রেডের উপর ভিত্তি করে একটি অগ্রগতি বার ইউআই আপডেট করুন progress)

সংকলক কি এটিকে রূপান্তর করতে পারে:

int local = 0;
while (working())
    local++;

progress += local;

সম্ভবত যে বৈধ। তবে সম্ভবত প্রোগ্রামার যা আশা করেছিল তা নয় :-(

কমিটি এখনও এই স্টাফ উপর কাজ করছে। বর্তমানে এটি "কাজ করে" কারণ সংকলকরা পরমাণুকে খুব বেশি অনুকূলিত করে না। তবে তা বদলে যাচ্ছে।

এমনকি যদি progressএটি অস্থিতিশীলও ছিল তবে এটি কার্যকর হবে:

int local = 0;
while (working())
    local++;

while (local--)
    progress++;

: - /


এই উত্তরটি কেবল রিচার্ড এবং আমি চিন্তা করেই যে পার্শ্ব-প্রশ্নের উত্তর দিচ্ছিলাম বলে মনে হচ্ছে। আমরা অবশেষে এটা সমাধান হয়েছে: আউট যে হ্যাঁ পালাক্রমে, C ++ স্ট্যান্ডার্ডের নেই অ- উপর অপারেশনের মার্জ অনুমতি volatileযখন এটি অন্য কোন নিয়ম ভঙ্গ করে না, পারমাণবিক বস্তু। দুটি মান-আলোচনার নথিতে এটি ঠিক আলোচনা করা হয় ( রিচার্ডের মন্তব্যে লিঙ্কগুলি ), একই ধরণের অগ্রগতি-পাল্টা উদাহরণ ব্যবহার করে using সুতরাং এটি কার্যকর মানের কার্যকর সমস্যা যতক্ষণ না সি ++ এটির প্রতিরোধের উপায়গুলিকে মানক করে না izes
পিটার কর্ডেস

হ্যাঁ, আমার "না" সত্যিই যুক্তির পুরো লাইনের জবাব। যদি প্রশ্নটি কেবল "কয়েকটি সংকলক / প্রয়োগের ক্ষেত্রে পার্শ্ববর্তী +++ পারমাণবিক হতে পারে", উত্তরটি অবশ্যই নিশ্চিত। উদাহরণস্বরূপ, একটি সংকলক lockপ্রতিটি ক্রিয়াকলাপে যুক্ত করার সিদ্ধান্ত নিতে পারে । বা কিছু সংকলক + ইউনিকপ্রসেসর সংমিশ্রণ যেখানে উভয়ই পুনরায় অর্ডারিং করেনি (যেমন "শুভ ওল 'দিন") সবকিছুই পারমাণবিক। তবে এর মানে কী? আপনি সত্যিই এটির উপর নির্ভর করতে পারবেন না। আপনি যদি না জানেন তবে আপনি যে সিস্টেমটি লিখছেন। (তারপরেও আরও ভাল হবে যে পারমাণবিক <<> সেই সিস্টেমে কোনও অতিরিক্ত বিকল্প যুক্ত করা যায় না So সুতরাং আপনার এখনও স্ট্যান্ডার্ড কোডটি লেখা উচিত ...)
টনি

1
মনে রাখবেন যে And just remove the incr/decr entirely.এটি ঠিক নয়। এটি এখনও একটি অধিগ্রহণ এবং রিলিজ অপারেশন চলছে num। X86, num++;num--শুধু MFENCE কিছুই না কম্পাইল পারে, কিন্তু স্পষ্টভাবে। (যদি না সংকলকের পুরো প্রোগ্রাম বিশ্লেষণটি প্রমাণ করতে পারে না যে কিছুই সেই সংখ্যার পরিবর্তনের সাথে কোনও কিছু সিঙ্ক্রোনাইজ করে না এবং এর আগে কিছু স্টোরের পরে লোড হওয়ার পরেও বিলম্ব হয় কিনা তা বিবেচ্য নয়)) উদাহরণস্বরূপ যদি এটি একটি আনলক এবং পুনরায় কাজ করে -লোক-ডান-দূরে ব্যবহারের ক্ষেত্রে, আপনার এখনও দুটি পৃথক সমালোচনা বিভাগ রয়েছে (সম্ভবত মো_ রিল্যাক্সযুক্ত ব্যবহার করা হচ্ছে), একটিও বড় নয়।
পিটার কর্ডেস

@ পিটারকর্ডস হ্যাঁ, একমত হয়েছেন।
টনি

2

হ্যাঁ কিন্তু...

পারমাণবিক আপনি যা বলতে চেয়েছিলেন তা নয়। আপনি সম্ভবত ভুল জিনিস জিজ্ঞাসা করছেন।

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

এটা থ্রেড-নিরাপদ?

এটি একটি পৃথক প্রশ্ন এবং একটি নির্দিষ্ট "না!" দিয়ে উত্তর দেওয়ার কমপক্ষে দুটি ভাল কারণ রয়েছে !

প্রথমত, সম্ভাবনা রয়েছে যে অন্য কোনও কোরের সেই ক্যাশে লাইনের একটি অনুলিপি এল 1 এ থাকতে পারে (এল 2 এবং উপরের দিকে সাধারণত ভাগ করা হয়, তবে এল 1 সাধারণত প্রতি-কোর হয়!) এবং একই সাথে মানটি সংশোধন করে। অবশ্যই এটি পরমাণুগতভাবেও ঘটে, তবে এখন আপনার দুটি "সঠিক" (সঠিকভাবে, পরমাণুগতভাবে, সংশোধিত) মান আছে - কোনটি এখন সত্যিকারের সঠিক?
অবশ্যই সিপিইউ এটি কোনওভাবে বাছাই করবে। তবে ফলাফলটি আপনার প্রত্যাশা মতো নাও হতে পারে।

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

আপনার গ্যারান্টি কার্যকর করার সম্ভাবনা রয়েছে যে মেমরি-ভিত্তিতে ঘটে যাওয়া সবকিছুই কিছু গ্যারান্টিযুক্ত, সু-সংজ্ঞায়িত অর্ডারে উপলব্ধ হয়ে যায় যেখানে আপনার "গ্যারান্টির আগে" ঘটেছিল। এই ক্রমটি "রিল্যাক্সড" (পড়ুন: মোটেও কিছুই নয়) বা আপনার প্রয়োজন মতো কঠোর হতে পারে।

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


2
লোড এবং স্টোর পৃথকভাবে প্রতিটি পারমাণবিক, তবে সামগ্রিকভাবে পুরো রিড-মডিফাই-রাইটিং অপারেশন অবশ্যই পারমাণবিক নয় । ক্যাচগুলি সুসংগত, সুতরাং একই লাইনের বিবাদী অনুলিপিগুলি কখনই ধরে রাখতে পারে না ( এনউইকিপিডিয়া . org / উইকি / এমইএসআই_প্রোটোকল )। অন্য কোরটিতে কেবল পঠনযোগ্য অনুলিপি থাকতে পারে না যখন এই কোরটি সংশোধিত অবস্থায় রয়েছে। এটি অ-পারমাণবিক করে তোলে তা হ'ল আরএমডাব্লু করছেন মূলটি লোড এবং স্টোরের মধ্যে ক্যাশে লাইনের মালিকানা হারাতে পারে।
পিটার কর্ডস

2
এছাড়াও, না, পুরো ক্যাশে লাইনগুলি সর্বদা পারমাণবিকভাবে স্থানান্তরিত হয় না। এই উত্তরটি দেখুন , যেখানে এটি পরীক্ষামূলকভাবে প্রমাণিত হয়েছে যে একটি বহু-সকেট ওপ্টরন 16B এসএসইকে হাইপারট্রান্সপোর্টের সাথে 8 বি অংশে ক্যাশে লাইনগুলি স্থানান্তর করে অ-পারমাণবিক স্টোর করে, যদিও তারা একই ধরণের সিঙ্গল-সকেটের সিপিইউগুলির জন্য পারমাণবিক হয় (কারণ লোড / স্টোর হার্ডওয়ারের L1 ক্যাশে 16 বি পাথ রয়েছে)। x86 কেবল 8 বি পর্যন্ত পৃথক লোড বা স্টোরের জন্য পারমাণবিকতার গ্যারান্টি দেয়।
পিটার কর্ডস

সংকলকটিতে প্রান্তিককরণ ছেড়ে যাওয়ার অর্থ এই নয় যে মেমরিটি 4-বাইট সীমানায় প্রান্তিক করা হবে। সংযোজকগুলির প্রান্তিককরণের সীমানা পরিবর্তন করতে বিকল্প বা প্রাগমাস থাকতে পারে। উদাহরণস্বরূপ, নেটওয়ার্ক স্ট্রিমে দৃly়-প্যাকড ডেটা অপারেট করার জন্য এটি দরকারী।
দিমিত্রি রুবানোভিচ

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

1
@ ড্যামন, প্রশ্নের যে উদাহরণটি দেওয়া হয়েছে তাতে কোনও কাঠামোর কথা উল্লেখ করা হয়নি, তবে এটি কেবল এমন পরিস্থিতিতে সংকীর্ণ হয় না যেখানে পূর্ণসংখ্যা স্ট্রাক্টের অংশ নয়। পিওডিগুলিতে স্পষ্টতই পিওটি আকার থাকতে পারে এবং পিওটি সারিবদ্ধ করা যায় না। সিনট্যাক্স উদাহরণগুলির জন্য এই উত্তরটি একবার দেখুন: stackoverflow.com/a/11772340/1219722 । সুতরাং এটি খুব কমই একটি "পরিশ্রমী" কারণ এইভাবে ঘোষিত পিওডিগুলি নেটওয়ার্কিং কোডে রিয়েল-লাইফ কোডে বেশ কিছুটা ব্যবহৃত হয়।
দিমিত্রি রুবানোভিচ

2

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

নোট করুন যে পরমাণু (ব্যবহার করে lock নির্দেশের উপসর্গটি ) x86 এর তুলনায় তুলনামূলকভাবে ভারী (তবে এই প্রাসঙ্গিক উত্তরটি দেখুন ) তবে এটি এখনও একটি মুটেক্সের চেয়ে কম উল্লেখযোগ্য, যা এই ব্যবহারের ক্ষেত্রে খুব উপযুক্ত নয়।

সংকলন করার সময় নিম্নলিখিত ফলাফলগুলি ঝাঁকুনি ++ 3.8 থেকে নেওয়া হয় -Os

রেফারেন্স দ্বারা একটি int বৃদ্ধি, "নিয়মিত" উপায়:

void inc(int& x)
{
    ++x;
}

এটি সংকলন করে:

inc(int&):
    incl    (%rdi)
    retq

রেফারেন্সের মাধ্যমে পারমাণবিক উপায়ে পাস করা ইনট বাড়ানো:

#include <atomic>

void inc(std::atomic<int>& x)
{
    ++x;
}

এই উদাহরণটি, যা নিয়মিত পদ্ধতির চেয়ে বেশি জটিল নয়, কেবল নির্দেশে lockউপসর্গ যুক্ত হয় incl- তবে সাবধানতা, যেমন আগে বলা হয়েছে এটি সস্তা নয় । সমাবেশটি সংক্ষিপ্ত বলে মনে হচ্ছে এর অর্থ এটি দ্রুত নয়।

inc(std::atomic<int>&):
    lock            incl    (%rdi)
    retq

-2

আপনার সংকলক যখন বর্ধিতকরণের জন্য কেবলমাত্র একটি একক নির্দেশনা ব্যবহার করে এবং আপনার মেশিনটি একক থ্রেডযুক্ত তখন আপনার কোডটি নিরাপদ। ^^


-3

অ-x86 মেশিনে একই কোডটি সংকলনের চেষ্টা করুন এবং আপনি খুব শীঘ্রই খুব বিধানসভা ফলাফল দেখতে পাবেন।

কারণ num++ হাজির পারমাণবিক হতে কারণ র সাহায্যে x86 মেশিনে একটি 32 বিট পূর্ণসংখ্যা বৃদ্ধিশীল হয়, আসলে, পারমাণবিক (কোন মেমরি আহরণ অভিমানী সঞ্চালিত) হয়। তবে এটি সি ++ স্ট্যান্ডার্ড দ্বারা গ্যারান্টিযুক্ত নয়, বা এটি এমন কোনও মেশিনেও ঘটতে পারে না যা x86 নির্দেশিকা সেট ব্যবহার করে না। সুতরাং এই কোডটি রেস শর্ত থেকে ক্রস প্ল্যাটফর্ম নিরাপদ নয়।

আপনার একটি দৃ guarantee় গ্যারান্টিও নেই যে এই কোডটি রেস কন্ডিশন থেকে এমনকি একটি x86 আর্কিটেকচারেও নিরাপদ, কারণ x86 বিশেষভাবে এটি করার নির্দেশ না দিলে মেমরিতে লোড এবং স্টোর সেট আপ করে না। সুতরাং যদি একাধিক থ্রেড একই সাথে এই পরিবর্তনশীলটি আপডেট করার চেষ্টা করে তবে সেগুলি ক্যাশে (পুরানো) মানগুলি বাড়িয়ে দেয়

তারপরে, এর কারণটি std::atomic<int>এবং এরকম কারণগুলি হ'ল আপনি যখন এমন একটি আর্কিটেকচারের সাথে কাজ করছেন যেখানে বুনিয়াদি গণনার পারমাণবিকতা গ্যারান্টিযুক্ত নয়, আপনার কাছে এমন একটি ব্যবস্থা রয়েছে যা সংকলককে পারমাণবিক কোড তৈরি করতে বাধ্য করবে।


"কারণ x86 মেশিনে, 32-বিট পূর্ণসংখ্যার বর্ধন করা আসলে, পারমাণবিক is" আপনি কি দলিলের লিঙ্ক সরবরাহ করতে পারেন যা প্রমাণ করে?
স্লাভা

8
এটি x86 তেও পারমাণবিক নয়। এটি একক-কোর-নিরাপদ, তবে যদি একাধিক কোর থাকে (এবং সেখানে থাকে) এটি একেবারেই পারমাণবিক নয়।
বর্ধিত

X86 addআসলে পারমাণবিক গ্যারান্টিযুক্ত? রেজিস্টার ইনক্রিমেন্টগুলি যদি পারমাণবিক হয় তবে আমি অবাক হব না, তবে এটি খুব কমই কার্যকর; নিবন্ধটিকে বর্ধিতকরণের জন্য অন্য থ্রেডে দৃশ্যমান করে তুলতে এটি মেমোরিতে থাকা দরকার, যা এটি লোড এবং সংরক্ষণের জন্য অতিরিক্ত নির্দেশাবলীর প্রয়োজন হবে, পারমাণবিকতা অপসারণ করে। আমার বোধগম্যতা হ'ল এই কারণেই lockনির্দেশের জন্য উপসর্গ উপস্থিত রয়েছে; কেবলমাত্র কার্যকর পারমাণবিকটি addঅবজ্ঞাত মেমরির ক্ষেত্রে প্রযোজ্য, এবং lockঅপারেশনের সময়কালের জন্য ক্যাশে লাইনটি লক করা আছে তা নিশ্চিত করতে উপসর্গ ব্যবহার করে
শ্যাডোএ্যাঞ্জার

@ স্লাভা @ হারল্ড @ শ্যাডো রেঞ্জার আমি উত্তরটি আপডেট করেছি। addপারমাণবিক, তবে আমি স্পষ্ট করে দিয়েছি যে এটি কোডটি রেস-শর্তটি নিরাপদ বলে বোঝায় না, কারণ পরিবর্তনগুলি এখনই বিশ্বব্যাপী দৃশ্যমান হয় না।
জিরেমা

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