সি ++ 11 এ কীভাবে স্টোরলড বাধা অর্জন করবেন?


13

আমি পোর্টেবল কোড লিখতে চাই (ইন্টেল, এআরএম, পাওয়ারপিসি ...) যা ক্লাসিক সমস্যার বৈকল্পিক সমাধান করে:

Initially: X=Y=0

Thread A:
  X=1
  if(!Y){ do something }
Thread B:
  Y=1
  if(!X){ do something }

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

আমি সচেতন, যে আমি memory_order_seq_cstপরমাণু storeগুলি এবং loadগুলি দ্বারা নিম্নলিখিত হিসাবে লক্ষ্য অর্জন করতে পারেন :

std::atomic<int> x{0},y{0};
void thread_a(){
  x.store(1);
  if(!y.load()) foo();
}
void thread_b(){
  y.store(1);
  if(!x.load()) bar();
}

যা লক্ষ্য অর্জন করে, কারণ
{x.store(1), y.store(1), y.load(), x.load()}ইভেন্টগুলিতে কিছু একক মোট অর্ডার থাকতে হবে, যা অবশ্যই প্রোগ্রামের আদেশ "প্রান্ত" এর সাথে একমত হতে হবে:

  • x.store(1) "TO তে আগে" y.load()
  • y.store(1) "TO তে আগে" x.load()

এবং যদি foo()বলা হয়, তবে আমাদের অতিরিক্ত প্রান্ত রয়েছে:

  • y.load() "আগে মান পড়ে" y.store(1)

এবং যদি bar()বলা হয়, তবে আমাদের অতিরিক্ত প্রান্ত রয়েছে:

  • x.load() "আগে মান পড়ে" x.store(1)

এবং এই সমস্ত প্রান্তগুলি একত্রিত হয়ে একটি চক্র গঠন করবে:

x.store(1)"TO এর আগে" y.load()" " এর আগে " " মান পড়ার আগে " y.store(1)" " " TO "এর আগে" x.load()"আগে মান পড়ার আগে" "হয়x.store(true)

যা আদেশের কোনও চক্র নেই তা লঙ্ঘন করে।

আমি ইচ্ছাকৃতভাবে স্ট্যান্ডার্ড শর্তগুলির বিপরীতে "TO এর আগে" এবং "পূর্বে মূল্য পড়ি" মানহীন শর্তাদি ব্যবহার করি happens-before, কারণ এই ধারগুলি সত্যই happens-beforeসম্পর্ককে বোঝায় যে আমার ধারনার যথার্থতা সম্পর্কে আমি মতামত জানাতে চাই , এককভাবে একত্রিত হতে পারে গ্রাফ এবং এই জাতীয় সংযুক্ত গ্রাফের চক্র নিষিদ্ধ। আমি এটি সম্পর্কে নিশ্চিত নই। আমি যা জানি তা এই কোডটি ইন্টেল জিসিসি এবং কলং এবং এআরএম জিসিসিতে সঠিক বাধা তৈরি করে


এখন, আমার আসল সমস্যাটি আরও জটিল, কারণ "এক্স" এর উপর আমার কোনও নিয়ন্ত্রণ নেই - এটি কিছু ম্যাক্রো, টেম্পলেট ইত্যাদির আড়ালে লুকিয়ে রয়েছে এবং এটি সম্ভবত দুর্বল হতে পারে might seq_cst

আমি জানি না "এক্স" একটি একক পরিবর্তনশীল, বা অন্য কোনও ধারণা (যেমন একটি হালকা ওজনের সেমোফোর বা মিটেক্স)। কেবলমাত্র আমি জানি যে আমার কাছে দুটি ম্যাক্রো রয়েছে set()এবং check()এমন একটি আর থ্রেড "" পরে " check()ফিরে আসে । (এটা হয় যে পরিচিত এবং থ্রেড-নিরাপদ ও ডেটা-জাতি UB তৈরি করতে পারবেন না।)trueset()setcheck

সুতরাং ধারণাগতভাবে set()কিছুটা "এক্স = 1" এর check()মতো এবং এটি "এক্স" এর মতো, তবে আমার কোনও জড়িত পরমাণুর সাথে সরাসরি কোনও অ্যাক্সেস নেই।

void thread_a(){
  set();
  if(!y.load()) foo();
}
void thread_b(){
  y.store(1);
  if(!check()) bar();
}

আমি উদ্বিগ্ন, এটি set()অভ্যন্তরীণভাবে x.store(1,std::memory_order_release)এবং / অথবা check()হতে পারে অভ্যন্তরীণভাবে প্রয়োগ করা হতে পারে x.load(std::memory_order_acquire)। বা অনুমানকভাবে একটি std::mutexযে থ্রেড আনলক করা হচ্ছে এবং অন্যটি try_lockআইএন; আইএসও স্ট্যান্ডার্ডে std::mutexকেবল অর্ডার অর্ডার এবং রিলিজ করার গ্যারান্টি রয়েছে, seq_cst নয়।

এই যদি হয় তাহলে, তারপর check()'s যদি শরীর করতে পারার আগে "পুনর্বিন্যস্তভাবে" করা y.store(true)( দেখুন অ্যালেক্স এর উত্তর তারা কোথায় প্রকট যে এই পাওয়ারপিসি ঘটে )।
এটি সত্যিই খারাপ হবে, কারণ এখন ইভেন্টগুলির এই ক্রমটি সম্ভব:

  • thread_b()প্রথমে x( 0) এর পুরানো মান লোড করে
  • thread_a() সহ সবকিছু কার্যকর করে foo()
  • thread_b() সহ সবকিছু কার্যকর করে bar()

সুতরাং, উভয় foo()এবং কল bar()পেয়েছিলাম, যা আমি এড়ানো ছিল। এটি রোধ করার জন্য আমার বিকল্পগুলি কী কী?


বিকল্প ক

স্টোর-লোড বাধা জোর করার চেষ্টা করুন। এটি, বাস্তবে, এটি অর্জন করা যায় std::atomic_thread_fence(std::memory_order_seq_cst);- অ্যালেক্সের ব্যাখ্যা অনুযায়ী আলাদাভাবে উত্তর দেওয়া সমস্ত পরীক্ষিত সংকলক একটি সম্পূর্ণ বেড়া নির্গত করে:

  • x86_64: MFENCE CE
  • পাওয়ারপিসি: hwsync
  • ইটানুইম: এমএফ
  • এআরএমভি 7 / এআরএমভি 8: ডিএমবি ইএস
  • মিপস 64: সিঙ্ক

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

void thread_a(){
  set();
  std::atomic_thread_fence(std::memory_order_seq_cst)
  if(!y.load()) foo();
}
void thread_b(){
  y.store(true);
  std::atomic_thread_fence(std::memory_order_seq_cst)
  if(!check()) bar();
}

বিকল্প বি

ওয়াইডে রিড-মডিফাই-রাইট মেমরি_অর্ডার_এককি_রেল অপারেশন ব্যবহার করে সিঙ্ক্রোনাইজেশন অর্জন করতে আমাদের ওয়াইয়ের ওপরে নিয়ন্ত্রণ ব্যবহার করুন:

void thread_a(){
  set();
  if(!y.fetch_add(0,std::memory_order_acq_rel)) foo();
}
void thread_b(){
  y.exchange(1,std::memory_order_acq_rel);
  if(!check()) bar();
}

এখানে ধারণাটি হ'ল একক পারমাণবিক ( y) এ অ্যাক্সেসগুলি অবশ্যই একটি একক আদেশ হতে হবে যার উপর সমস্ত পর্যবেক্ষক সম্মত হন, তাই হয় fetch_addহয় আগে exchangeবা বিপরীত।

যদি fetch_addএর আগে হয় exchangeতবে "রিলিজ" অংশটি fetch_add"অর্জন" অংশটির সাথে সিঙ্ক্রোনাইজ করে exchangeএবং এইভাবে এর সমস্ত পার্শ্ব প্রতিক্রিয়াগুলি set()কোড এক্সিকিউটিভের কাছে দৃশ্যমান হতে হয় check(), তাই bar()বলা হবে না।

তা না হলে, exchangeআগে fetch_add, তারপর fetch_addদেখতে হবে 1এবং কল না foo()। সুতরাং, উভয়কেই কল করা অসম্ভব foo()এবং bar()। এই যুক্তি কি সঠিক?


বিকল্প সি

"প্রান্ত" প্রবর্তন করতে ডামি অ্যাটমিক্স ব্যবহার করুন যা দুর্যোগ রোধ করে। নিম্নলিখিত পদ্ধতির বিবেচনা করুন:

void thread_a(){
  std::atomic<int> dummy1{};
  set();
  dummy1.store(13);
  if(!y.load()) foo();
}
void thread_b(){
  std::atomic<int> dummy2{};
  y.store(1);
  dummy2.load();
  if(!check()) bar();
}

আপনি যদি মনে করেন যে সমস্যাটি এখানে atomicস্থানীয়, তবে তাদেরকে বিশ্বব্যাপী সরিয়ে নিয়ে যাওয়ার কল্পনা করুন, নিম্নলিখিত যুক্তিতে এটি আমার কাছে তাত্পর্যপূর্ণ বলে মনে হয় না এবং আমি ইচ্ছাকৃতভাবে কোডটি এমনভাবে লিখেছি যাতে এটি কতটা মজাদার exp এবং ডামি 2 সম্পূর্ণ পৃথক।

কেন পৃথিবীতে এটি কাজ করতে পারে? ওয়েল, অবশ্যই কিছু একক সামগ্রিক অর্ডার থাকতে হবে {dummy1.store(13), y.load(), y.store(1), dummy2.load()}যার ক্রমটি প্রোগ্রামের আদেশের সাথে "প্রান্ত" এর সাথে সামঞ্জস্যপূর্ণ থাকতে হবে:

  • dummy1.store(13) "TO তে আগে" y.load()
  • y.store(1) "TO তে আগে" dummy2.load()

(একটি seq_cst স্টোর + লোড আশা করি স্টোরলড সহ একটি সম্পূর্ণ মেমরি বাধা সমতুল্য সি ++ সমৃদ্ধ, যেমন তারা এমনকি আআআর্ক including৪ সহ সত্যিকারের আইএসএতে asm করে তবে যেখানে কোনও পৃথক বাধা নির্দেশের প্রয়োজন নেই))

এখন, আমাদের দুটি বিষয় বিবেচনা করতে হবে: হয় y.store(1)হয় y.load()মোট ক্রমের আগে বা পরে।

যদি y.store(1)এর আগে হয় y.load()তবে foo()ডাকা হবে না এবং আমরা নিরাপদ।

যদি y.load()আগে হয় y.store(1), তবে এটি ইতিমধ্যে প্রোগ্রাম ক্রমে আমাদের দুটি প্রান্তের সাথে একত্রিত করে, আমরা এটি হ্রাস করি:

  • dummy1.store(13) "TO তে আগে" dummy2.load()

এখন, এটি dummy1.store(13)একটি রিলিজ অপারেশন, যা এর প্রভাবগুলি প্রকাশ করে set()এবং dummy2.load()এটি একটি অধিগ্রহণ অপারেশন, সুতরাং check()এর প্রভাবগুলি দেখতে হবে set()এবং এভাবে bar()ডাকা হবে না এবং আমরা নিরাপদ are

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

মনে রাখবেন আমরা কেবল কেস যেখানে সম্পর্কে উদ্বিগ্ন হন dumm1.storeহয় পরিচিত (অন্যান্য যুক্তি মাধ্যমে) আগে হতে হবে dummy2.loadseq_cst মোট যাতে। সুতরাং যদি তারা একই ভেরিয়েবলটি অ্যাক্সেস করে থাকত তবে লোডটি সঞ্চিত মানটি দেখতে পেত এবং এর সাথে সিঙ্ক্রোনাইজ করত।

(বাস্তবায়নের জন্য মেমোরি-বাধা / পুনর্নির্ধারণের যুক্তি যেখানে পারমাণবিক লোড এবং স্টোরগুলি কমপক্ষে 1-উপায় মেমরি বাধা (এবং seq_cst ক্রিয়াকলাপগুলি পুনরায় অর্ডার করতে পারে না: যেমন একটি seq_cst স্টোর একটি seq_cst লোড পাস করতে পারে না) যে কোনও লোড / দোকানে পর dummy2.loadস্পষ্টভাবে অন্যান্য থ্রেড কাছে দৃশ্যমান হয়ে পরে y.store । আর একইভাবে অন্যান্য থ্রেড জন্য, ... সামনে y.load।)


আপনি https://godbolt.org/z/u3dTa8 এ আমার বিকল্প এ, বি, সি প্রয়োগের সাথে খেলতে পারেন


1
সি ++ মেমরির মডেলটির স্টোরলড পুনঃক্রমের কোনও ধারণা নেই, কেবল সিনক্রোনাইজ-উইথ এবং ঘটে-এর আগে। (এবং সত্যিকারের হার্ডওয়্যার হিসাবে অ্যাসমিকের তুলনায় অ-পারমাণবিক অবজেক্টগুলিতে ডেটা রেসের বিষয়ে ইউবি)) সমস্ত বাস্তব বাস্তবায়নের বিষয়ে আমি অবগত, std::atomic_thread_fence(std::memory_order_seq_cst)একটি সম্পূর্ণ বাধা সংকলন করে তবে পুরো ধারণাটি বাস্তবায়নের বিশদ হিসাবে আপনি পাবেন না স্ট্যান্ডার্ডে এটির কোনও উল্লেখ। (CPU- র মেমরি মডেল সাধারণত হয় কি reorerings অনুক্রমিক দৃঢ়তা আপেক্ষিক অনুমতি দেওয়া হয় পদ সংজ্ঞায়িত যেমন এক্স 86 হল SeQ-CST + একটি দোকান বাফার W / ফরওয়ার্ডিং।)
পিটার Cordes

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

1
আপনি compare_exchange_*কোনও পারমাণবিক স্তরে তার মান পরিবর্তন না করে কোনও আরএমডাব্লু অপারেশন করতে পারেন (কেবলমাত্র প্রত্যাশিত এবং একই মানটিতে নতুন সেট করুন)।
এমপিএটার

1
@ ফ্যারানর এবং কিউব্লেক: atomic<bool>রয়েছে exchangeএবং আছে compare_exchange_weak। পরবর্তীটি সিএএস (সত্য, সত্য) বা মিথ্যা, মিথ্যা দ্বারা একটি ডামি আরএমডাব্লু করতে ব্যবহার করা যেতে পারে। এটি হয় ব্যর্থ হয় বা পরমাণুর সাথে মানটি নিজের সাথে প্রতিস্থাপন করে। (X86-64 asm এ, সেই কৌশলটি lock cmpxchg16bআপনি কীভাবে গ্যারান্টেড-পারমাণবিক 16-বাইট লোড করবেন; অকার্যকর তবে আলাদা লক নেওয়ার চেয়ে কম খারাপ bad)
পিটার কর্ডেস

1
@ পিটারকর্ডস হ্যাঁ আমি জানি এটি ঘটতে পারে foo()এবং bar()নাও ডাকা হবে না। আমি কোডের অনেকগুলি "রিয়েল ওয়ার্ল্ড" উপাদানগুলিতে আনতে চাইনি, "আপনার মনে হয় আপনার এক্স সমস্যা আছে তবে আপনার ওয়াই" সমস্যা রয়েছে বলে মনে করেন এ ধরণের প্রতিক্রিয়া। কিন্তু, যদি এক সত্যিই জানতে পটভূমি তলা কি দরকার: set()সত্যিই some_mutex_exit(), check()হয় try_enter_some_mutex(), yহয় "কিছু ওয়েটার হয়", foo()"প্রস্থান কেউ নিদ্রাভঙ্গ আপ ছাড়া" হয়, bar()"wakup জন্য অপেক্ষা" হয় ... কিন্তু, আমি অস্বীকার এই নকশাটি এখানে আলোচনা করুন - আমি এটি সত্যিই পরিবর্তন করতে পারি না।
কিবোলেক

উত্তর:


5

বিকল্প এ এবং বি বৈধ সমাধান।

  • বিকল্প এ: সিক-সিএসটি বেড়াটি কী অনুবাদ করে তা সত্যিই আসে না, সি ++ স্ট্যান্ডার্ড এটি সরবরাহ করে কি গ্যারান্টি দেয় তা স্পষ্টভাবে সংজ্ঞায়িত করে। আমি তাদের এই পোস্টে রেখেছি : কখন একটি মেমরি_অর্ডার_সেক_সিএসটি বেড়া কার্যকর হয়?
  • বিকল্প বি: হ্যাঁ, আপনার যুক্তিটি সঠিক। কিছু বস্তুর সমস্ত পরিবর্তনের একক মোট অর্ডার (পরিবর্তনের ক্রম) থাকে, সুতরাং আপনি থ্রেডগুলি সিঙ্ক্রোনাইজ করতে এবং সমস্ত পার্শ্ব-প্রতিক্রিয়াটির দৃশ্যমানতা নিশ্চিত করতে এটি ব্যবহার করতে পারেন।

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

হালনাগাদ

বিকল্প একটি:
আমি ধরে নিয়েছি set()এবং check()কিছু পারমাণবিক মান নিয়ে কাজ করি। তারপরে আমাদের নিম্নোক্ত পরিস্থিতি রয়েছে (-> সিক্যুয়েন্সড-এর আগে বোঝায় ):

  • set()-> fence1(seq_cst)->y.load()
  • y.store(true)-> fence2(seq_cst)->check()

সুতরাং আমরা নিম্নলিখিত বিধি প্রয়োগ করতে পারি:

পারমাণবিক অপারেশন জন্য একটি এবং বি একটি পারমাণবিক বস্তুর উপর এম , যেখানে একজন মডিফাই এম এবং বি এর মান লাগে, যদি আছে memory_order_seq_cstবেড়া এক্স এবং ওয়াই যেমন যে একজন সামনে সিকোয়েন্স হয় এক্স , ওয়াই আগে সিকোয়েন্স করা হয় বি , এবং এক্স পূর্বে ওয়াই মধ্যে এস , তারপরে বি এর পরিবর্তনের ক্রমে A এর প্রভাবগুলি বা এম এর পরে সংশোধন করে ves

অর্থাৎ, হয় check()সঞ্চিত সেই মানটি দেখায় set, বা y.load()লিখিত মানটি y.store()(অপারেশন yএমনকি ব্যবহার করতে পারে memory_order_relaxed) দেখে।

অপশন সি: সি ++ 17 মান যুক্তরাষ্ট্র [32.4.3, p1347]:

সমস্ত ক্ষতিগ্রস্থ অবস্থানগুলির জন্য "পূর্বে ঘটে" অর্ডার এবং সংশোধন আদেশগুলির সাথে সামঞ্জস্য রেখে সমস্ত ক্রিয়াকলাপে একক মোট অর্ডার এস থাকবে memory_order_seq_cst[...]

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

বিশেষত, দুটি পৃথক বস্তুর উপর দুটি সিক-সিএসটি ক্রিয়াকলাপ সম্পর্কের আগে একটি ঘটনা প্রতিষ্ঠার জন্য ব্যবহার করা যায় না, যদিও অপারেশন সম্পূর্ণরূপে এস-এ অর্ডার করা হয় আপনি যদি পৃথক বস্তুর উপর ক্রিয়াকলাপ অর্ডার করতে চান তবে আপনাকে সেক-সিএসটি উল্লেখ করতে হবে -বিঘ্ন (অপশন এ দেখুন)।


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

ধন্যবাদ @ মপিটার আপনি কী দয়া করে অপশন এ সম্পর্কে বিস্তারিত জানাতে পারেন আপনার উত্তরের তিনটি বুলেটগুলির মধ্যে কোনটি এখানে প্রয়োগ হয়? আইআইইউসি যদি এর y.load()প্রভাব না দেখায় y.store(1)তবে আমরা বিধি থেকে প্রমাণ করতে পারি যে atomic_thread_fenceথ্রেড_এ এর থ্রেড_এ এর এস atomic_thread_fencein আমি যা দেখছি না তা হ'ল এ থেকে কীভাবে এই সিদ্ধান্তে পৌঁছানো যায় যে set()পার্শ্ব প্রতিক্রিয়াগুলি দৃশ্যমান check()
qbolec

1
@ কিবোলেট: আমি আমার উত্তরটি অপশন এ সম্পর্কিত আরও বিশদ সহ আপডেট করেছি
এমপিএটার

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

1
ভাল, যে অভিমানী setএবং checkনিরাপদে সমান্তরাল মৃত্যুদন্ড কার্যকর করা যেতে পারে, আমি সম্ভবত, বিকল্প ক সঙ্গে যেতে হবে, বিশেষ করে যদি এই কর্মক্ষমতা সমালোচনামূলক, যেহেতু এটি ভাগ পরিবর্তনশীল উপর বিবাদের এড়াতে y
এমপিএটার

1

প্রথম উদাহরণে, y.load()0 পড়ার y.load()আগে এমনটি বোঝায় না y.store(1)

এটি ইঙ্গিত দেয় যে এটি একক মোট অর্ডারে প্রথমে এই নিয়মের জন্য ধন্যবাদ যে একটি seq_cst লোড মোট অর্ডারে সর্বশেষ seq_cst স্টোরের মান দেয়, বা এমন কিছু অ-সিক_সিএসটি স্টোরের মূল্য দেয় যা এর আগে ঘটে না এটি (যা এই ক্ষেত্রে বিদ্যমান নেই)। সুতরাং যদি মোট ক্রমের y.store(1)তুলনায় আগেরটি ছিল y.load(), y.load()ফিরে আসত 1।

প্রমাণটি এখনও সঠিক কারণ একক মোট অর্ডারে একটি চক্র নেই।

কীভাবে এই সমাধান?

std::atomic<int> x2{0},y{0};

void thread_a(){
  set();
  x2.store(1);
  if(!y.load()) foo();
}

void thread_b(){
  y.store(1);
  if(!x2.load()) bar();
}

ওপি'র সমস্যাটি হ'ল "এক্স" এর উপর আমার কোনও নিয়ন্ত্রণ নেই - এটি র‌্যাপার ম্যাক্রো বা কোনও কিছুর পিছনে রয়েছে এবং এটি সিক-সিএসটি স্টোর / লোড নাও থাকতে পারে। আমি আরও ভাল হাইলাইট করতে প্রশ্ন আপডেট।
পিটার কর্ডেস

@ পিটারকর্ডস ধারণাটি ছিল যে একটি "এক্স" তৈরি করা উচিত যা তার নিয়ন্ত্রণ করতে পারে। এটি পরিষ্কার করার জন্য আমি আমার উত্তরে এর নাম পরিবর্তন করে "x2" রাখব। আমি নিশ্চিত যে আমি কিছু প্রয়োজনীয়তা নিখোঁজ করছি, তবে কেবলমাত্র প্রয়োজনীয়তাটি foo () এবং বার () উভয়কে বলা হয় না তা নিশ্চিত করা হয় তবে এটি এটি সন্তুষ্ট করে।
টোমেক সিজ্জাকা

তবে if(false) foo();তবে আমি মনে করি যে ওপি তাও চায় না: পি ইন্টারেস্টিং পয়েন্ট তবে আমি মনে করি যে ওপি শর্তযুক্ত কলগুলি তাদের নির্দিষ্ট করা শর্তের ভিত্তিতে করা হোক!
পিটার কর্ডেস

1
হাই @ টোমেকজাজকা, নতুন সমাধানের প্রস্তাব দেওয়ার জন্য সময় দেওয়ার জন্য ধন্যবাদ। এটি আমার বিশেষ ক্ষেত্রে কার্যকর হবে না, কারণ এটির গুরুত্বপূর্ণ পার্শ্ব-প্রতিক্রিয়াগুলি বাদ দেয় check()(বাস্তব-বিশ্বের অর্থের জন্য আমার প্রশ্নে আমার মন্তব্য দেখুন set,check,foo,bar)। আমি মনে করি এটি if(!x2.load()){ if(check())x2.store(0); else bar(); }পরিবর্তে কাজ করতে পারে ।
qbolec

1

@ এমপিটার ব্যাখ্যা করল কেন বিকল্প এ এবং বি নিরাপদ।

বাস্তব বাস্তবায়নের অনুশীলনে, আমি মনে করি অপশন এ এর ​​কেবল std::atomic_thread_fence(std::memory_order_seq_cst)থ্রেড এ প্রয়োজন, বি নয় B.

অনুশীলনে seq-cst স্টোরগুলিতে একটি সম্পূর্ণ মেমরি বাধা অন্তর্ভুক্ত থাকে, বা AArch64 এ অন্তত পরে অর্জন বা seq_cst লোডগুলির সাথে পুনরায় অর্ডার করতে পারে না ( ক্যাশে থেকে পড়ার stlrআগে ক্রিয়াকলাপ -রিলিজ স্টোর বাফার থেকে ড্রেন ldarকরতে হয়)।

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

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

(অবশ্যই সুরক্ষার আনুষ্ঠানিক গ্যারান্টির জন্য, আমাদের অর্জন / রিলিজ সেট () -> চেক () একটি seq_cst সিঙ্ক্রোনাইজ-উইন্ডে উন্নীত করার জন্য আমাদের উভয়ের মধ্যে একটি বেড়া প্রয়োজন I এছাড়াও একটি শিথিল সেটের জন্যও কাজ করবে বলে আমি মনে করি, তবে একটি রিলাক্সড চেকটি অন্য থ্রেডের পিওভি থেকে বারের সাথে পুনঃক্রম করতে পারে))


আমি মনে করি অপশন সি রিয়েল সমস্যা হল এটি কিছু প্রকল্পিত পর্যবেক্ষক যে নির্ভর করে পারে সুসংগত-সঙ্গে yএবং ডামি অপারেশন। এবং এইভাবে আমরা বাধা-ভিত্তিক আইএসএর জন্য asm তৈরি করার সময় সংকলকটি সেই ক্রমটি সংরক্ষণ করে রাখার প্রত্যাশা করি।

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

অপশন সি কাজ করার জন্য, কিছু আইএসও সি ++ নিয়ম লঙ্ঘন করার জন্য আমাদের dummy1.store(13);/ y.load()/ set();(থ্রেড বি দ্বারা প্রদর্শিত) এর মতো একটি অর্ডারিং দরকার

থ্রেড এই বিবৃতি চলমান আচরণ করে গেছে যেন set() (সিকোয়েন্স কারণ আগে) প্রথম মৃত্যুদন্ড কার্যকর। এটি ঠিক আছে, রানটাইম মেমরি ক্রম এবং / অথবা ক্রিয়াকলাপের ক্রম সংক্রমণের কাজ এখনও করতে পারে।

দুটি seq_cst অপ্স d1=13এবং yসিকোয়েন্সড বিফোর (প্রোগ্রাম ক্রম) এর সাথে সামঞ্জস্যপূর্ণ। set()seq_cst অপ্সের জন্য প্রয়োজনীয় টু-অস্তিত্বের বৈশ্বিক অর্ডারে অংশ নেয় না কারণ এটি seq_cst নয়।

থ্রেড বি সুসংগত-সঙ্গে না dummy1.store তাই কোনও ঘটে-পূর্বে প্রয়োজন setআপেক্ষিক d1=13প্রযোজ্য , যদিও নিয়োগ মুক্তি অপারেশন।

আমি আর কোনও সম্ভাব্য বিধি লঙ্ঘন দেখতে পাচ্ছি না; এর সাথে সামঞ্জস্য থাকার জন্য আমি এখানে এমন কিছু খুঁজে পাচ্ছি নাsetসিকোয়েন্সড-ফুরোয়ারেরd1=13

"ডামি 1.স্টোর রিলিজ সেট ()" যুক্তিটি ত্রুটি। এই ক্রমটি কেবলমাত্র সত্যিকারের পর্যবেক্ষকের জন্য প্রযোজ্য যা এটির সাথে সিঙ্ক্রোনাইজ করে as @ এমপিটার উত্তর হিসাবে, সিক_সিএসটি সামগ্রিক অর্ডারের অস্তিত্ব সম্পর্ক তৈরি হওয়ার আগে বা ঘটে বোঝায় না, এবং এটিই একমাত্র জিনিস যা আনুষ্ঠানিকভাবে seq_cst এর বাইরে অর্ডার দেওয়ার নিশ্চয়তা দেয়।

সুসংহত ভাগযুক্ত ক্যাশেযুক্ত কোনও ধরণের "সাধারণ" সিপিইউ যেখানে রানটাইমটিতে সত্যই এই পুনরায় অর্ডারিং ঘটতে পারে তা প্রশংসনীয় বলে মনে হয় না। (তবে যদি কোনও সংকলক অপসারণ করতে পারে)dummy1 এবং dummy2তারপরে স্পষ্টভাবে আমাদের একটি সমস্যা হতে পারে, এবং আমি মনে করি এটি স্ট্যান্ডার্ড দ্বারা অনুমোদিত))

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

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

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


@ মম্পিটার প্রস্তাব দিয়েছে যে একটি সংকলক এমনকি ডেমি লোড এবং স্টোর অপারেশনগুলি এমনকি seq_cst অবজেক্টগুলিতে সরিয়ে ফেলতে পারে।

আমি মনে করি এটি সঠিক হতে পারে যখন তারা প্রমাণ করতে পারে যে কোনও কিছুই অপারেশনের সাথে সিঙ্ক্রোনাইজ করতে পারে না। উদাহরণস্বরূপ এমন একটি সংকলক যা দেখতে পাবে যে dummy2ফাংশনটি এড়ায় না সে সম্ভবত সেই seq_cst লোডটিকে সরাতে পারে।

এটির অন্তত একটি বাস্তব-জগতের পরিণতি রয়েছে: যদি আআরচ for৪ এর জন্য সংকলন করা হয় তবে এটি পূর্ববর্তী seq_cst স্টোরকে পরবর্তী শিথিল অপারেশনগুলির সাথে অনুশীলনে পুনরায় অর্ডার করতে দেয়, যা কোনও সিক_সিএসটি স্টোর + লোড দিয়ে স্টোর বাফার ড্রেনিংয়ের আগে কোনও কিছুতেই সম্ভব হত না any পরে লোডগুলি কার্যকর করতে পারে।

অবশ্যই আইএসও সি ++ এটি নিষেধ না করলেও বর্তমান সংকলকরা মোটেও পরমাণুকে অনুকূলিত করে না; এটি একটি অমীমাংসিত সমস্যাএটি স্ট্যান্ডার্ড কমিটির জন্য ।

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


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

@ এমপিটার: হ্যাঁ, আমি কেবল অনুশীলনে কথা বলছিলাম, আনুষ্ঠানিকভাবে নয়। বিভাগের শেষে একটি নোট যুক্ত করেছে। এবং হ্যাঁ, বেশিরভাগ আইএসএতে অনুশীলনে আমার মনে হয় যে একটি seq_cst স্টোরটি সাধারণত কেবল প্লেইন স্টোর (শিথিল) + একটি বাধা। অথবা না; POWER এ একটি সিক-সিএসটি স্টোর স্টোরের sync আগে (ভারী ওজন) করে, এর পরে কিছুই হয় না। Godbolt.org/z/mAr72P তবে সিক-সিএসটি লোড উভয় পক্ষের কিছু বাধা প্রয়োজন।
পিটার কর্ডেস

1

আইএসও স্ট্যান্ডার্ড এসটিডি :: মিটেক্সে কেবল অর্ডার অর্ডার এবং রিলিজ করার গ্যারান্টি দেওয়া হয়, seq_cst নয়।

তবে "seq_cst অর্ডারিং" করার কোনও কিছুর গ্যারান্টি নেই, কারণ seq_cstএটি কোনও ক্রিয়াকলাপের সম্পত্তি নয়।

seq_cstপ্রদত্ত বাস্তবায়ন std::atomicবা বিকল্প পারমাণবিক শ্রেণীর সমস্ত ক্রিয়াকলাপের গ্যারান্টি । যেমন, আপনার প্রশ্নটি নিরবচ্ছিন্ন।

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