ক্যাশ কোহেরেন্স প্রবলেম
এই পাঠে যা শিখবেন
- কেন একাধিক প্রাইভেট ক্যাশ থাকলে "স্টেল ডেটা" নামের একটি বাস্তব, গুরুতর কারেক্টনেস বাগ তৈরি হতে পারে
- MESI প্রোটোকলের চারটি স্টেট — Modified, Exclusive, Shared, Invalid — এবং প্রতিটির নির্ভুল অর্থ
- বাস স্নুপিং কীভাবে হার্ডওয়্যার-লেভেলে স্বয়ংক্রিয়ভাবে কোহেরেন্স বজায় রাখে
- Python দিয়ে একটি সরল MESI সিমুলেশন — যেখানে Core B সত্যিকারভাবে তার Invalid স্টেট শনাক্ত করে সঠিক মান রি-ফেচ করে
১ · সমস্যাটি কী — একাধিক প্রাইভেট ক্যাশের বিপদ
M11/L51-এ দেখেছিলেন একটি মাল্টিকোর চিপে প্রতিটি কোরের নিজস্ব প্রাইভেট L1 (এবং প্রায়ই L2) ক্যাশ থাকে, কিন্তু
সবাই একই মেইন মেমরি থেকে ডেটা পড়ে। এখন ধরুন একটি মেমরি অ্যাড্রেস X-এর মান দুই কোরের ক্যাশেই
কপি হয়ে আছে (উভয় কোরই সম্প্রতি সেটা পড়েছিল)। যদি Core A এখন X-এ একটি নতুন মান লেখে, কিন্তু
Core B-এর ক্যাশে থাকা পুরনো কপিটা কেউ না জানায়, তাহলে Core B পরবর্তী রিডে সেই স্টেল (পুরনো, ভুল)
মান-ই ফেরত পাবে — এটি M37-এর লোকালিটি আলোচনার ঠিক উল্টো দিক: ক্যাশিং যা সুবিধা দেয়, একাধিক স্বাধীন ক্যাশ থাকলে
ঠিক সেই একই মেকানিজম একটি গুরুতর কারেক্টনেস বাগে পরিণত হতে পারে যদি সমন্বয় না থাকে।
২ · MESI প্রোটোকল — চারটি স্টেট
এই সমস্যার বাস্তব, ইন্ডাস্ট্রি-স্ট্যান্ডার্ড সমাধান হলো MESI প্রোটোকল — প্রতিটি ক্যাশ লাইনকে সবসময় নিচের চারটি স্টেটের ঠিক একটিতে ট্যাগ করে রাখা হয়:
এই ক্যাশই একমাত্র বৈধ কপি রাখে, এবং সেটা লেখা হয়েছে — মেইন মেমরির সাথে আর মেলে না। অন্য কোনো কোর পড়ার আগে এই লাইনটি মেমরিতে রাইট-ব্যাক করতে হবে (বা সরাসরি নতুন কোরকে ফরওয়ার্ড করতে হবে)।
এই ক্যাশই একমাত্র কপি রাখে, কিন্তু এখনো লেখা হয়নি — মেইন মেমরির সাথে ঠিক মিলে যায়। এখনো "পরিষ্কার" অবস্থা।
একাধিক কোরের ক্যাশে এই একই, আপ-টু-ডেট, অপরিবর্তিত কপি একসাথে থাকতে পারে — শুধু রিডের জন্য নিরাপদ।
এই ক্যাশের কপিটি স্টেল/অব্যবহারযোগ্য — ব্যবহারের আগে অবশ্যই নতুন করে ফেচ করতে হবে।
৩ · বাস স্নুপিং — স্বয়ংক্রিয় হার্ডওয়্যার মেকানিজম
M10/L47-এর শেয়ার্ড বাসের ধারণা এখানে সরাসরি কাজে লাগে — প্রতিটি কোরের ক্যাশ কন্ট্রোলার সবসময় শেয়ার্ড বাসের দিকে "কান পেতে" থাকে (স্নুপ করে)। যখন কোনো কোর একটি অ্যাড্রেসে লেখে, বাসে সেই লেখার সিগন্যাল প্রচারিত হয়; অন্য যেকোনো কোর যদি সেই একই অ্যাড্রেসের কপি নিজের ক্যাশে রাখে, তখন সে নিজে থেকেই সেই লাইনকে Invalid স্টেটে সরিয়ে দেয়। এটাই পুরো কোহেরেন্স মেকানিজমের ভিত্তি — কোনো সফটওয়্যার/OS-কে এটি ম্যানেজ করতে হয় না, সম্পূর্ণ হার্ডওয়্যার-স্তরে স্বয়ংক্রিয়ভাবে ঘটে।
নিচের কোড সেলে আমরা এই ঠিক দৃশ্যটাই সিমুলেট করব — Core A একটি অ্যাড্রেসে লেখে, Core B-এর কপি ইনভ্যালিডেট হয়, তারপর Core B যখন পড়তে যায়, সে তার নিজের Invalid ফ্ল্যাগ শনাক্ত করে সঠিক, সাম্প্রতিক মান রি-ফেচ করে — স্টেল মান কখনো রিটার্ন করে না।
# MESI-স্টাইল সিমুলেশন -- সিমুলেটেড কোর ক্যাশ স্টেট, প্রকৃত হার্ডওয়্যার অ্যাক্সেস নয়
def core_write(core_id, address, value, cache_states):
"""এই কোর লেখে -> নিজে Modified হয়, বাকি সব কোরের কপি বাস-স্নুপ করে Invalid হয়ে যায়"""
for cid in cache_states:
if cid == core_id:
cache_states[cid] = {"state": "M", "value": value}
else:
if cache_states[cid]["state"] != "I":
# বাস স্নুপিং: অন্য কোরের লেখা দেখে নিজের কপি ইনভ্যালিডেট করে
cache_states[cid] = {"state": "I", "value": cache_states[cid]["value"]}
return cache_states
def core_read(core_id, address, cache_states, memory):
"""এই কোর পড়ে -> নিজের স্টেট Invalid হলে সঠিক, সাম্প্রতিক মান রি-ফেচ করে (স্টেল মান কখনো নয়)"""
line = cache_states[core_id]
if line["state"] == "I":
# রি-ফেচ: যদি কোনো কোর Modified (dirty) কপি রাখে, সেখান থেকেই সঠিক মান আসে
fresh_value = memory.get(address)
for cid, other in cache_states.items():
if cid != core_id and other["state"] == "M":
fresh_value = other["value"]
cache_states[core_id] = {"state": "S", "value": fresh_value}
return fresh_value, "MISS (Invalid ছিল -> রি-ফেচ করা হলো)"
return line["value"], "HIT (নিজের বৈধ কপি থেকে সরাসরি)"
# প্রাথমিক অবস্থা: দুই কোরেরই Shared কপি আছে, মান ১০
memory = {0: 10}
cache_states = {
"CoreA": {"state": "S", "value": 10},
"CoreB": {"state": "S", "value": 10},
}
print("t0 -- প্রাথমিক অবস্থা:", cache_states)
# Core A অ্যাড্রেস 0-এ নতুন মান ৯৯ লেখে
core_write("CoreA", 0, 99, cache_states)
print("t1 -- Core A লেখার পর:", cache_states)
# Core B এখন সেই একই অ্যাড্রেস পড়তে চেষ্টা করে
value, status = core_read("CoreB", 0, cache_states, memory)
print("t2 -- Core B রিড করলো ->", status, "| পাওয়া মান =", value)
print("t2 -- চূড়ান্ত ক্যাশ স্টেট:", cache_states)
assert value == 99, "স্টেল মান রিটার্ন হয়ে গেছে -- এটাই কোহেরেন্স বাগ, যা MESI প্রতিরোধ করে!"
print("\nনিশ্চিত: Core B স্টেল মান ১০ নয়, সঠিক নতুন মান ৯৯ পেয়েছে -- কোহেরেন্স সঠিকভাবে বজায় আছে।")
core_read ফাংশনটি প্রথমে line["state"] == "I" চেক করে — এই চেকটিই
পুরো সমস্যার সমাধান। যদি এই চেকটি বাদ দিয়ে সরাসরি cache_states[core_id]["value"] রিটার্ন করা
হতো, Core B পুরনো মান ১০-ই ফেরত পেত — এটাই ঠিক সেই কোহেরেন্স বাগ যা MESI প্রতিরোধ করে।
একাধিক প্রাইভেট ক্যাশ থাকলে "সবাই সবসময় একই মেমরি লোকেশনের একটাই সঠিক মান দেখবে" — এই নিশ্চয়তা আর স্বয়ংক্রিয়ভাবে থাকে না। MESI প্রোটোকল ও বাস স্নুপিং মিলে ঠিক এই নিশ্চয়তাটাই হার্ডওয়্যার-স্তরে ফিরিয়ে আনে — প্রতিটি ক্যাশ নিজের কপির বৈধতা সবসময় জানে, এবং প্রয়োজনে স্বয়ংক্রিয়ভাবে সঠিক মান রি-ফেচ করে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ কোহেরেন্স সমস্যাটি কেন শুধু মাল্টি-কোর সিস্টেমে ঘটে, সিঙ্গেল-কোর সিস্টেমে নয়?
সিঙ্গেল-কোর সিস্টেমে একটিমাত্র প্রাইভেট ক্যাশ থাকে — তাই কোনো নির্দিষ্ট মেমরি অ্যাড্রেসের একাধিক, সম্ভাব্য-ভিন্ন কপি একসাথে থাকার প্রশ্নই ওঠে না। কোহেরেন্স সমস্যাটি নির্দিষ্টভাবে তখনই তৈরি হয় যখন একাধিক স্বাধীন ক্যাশ একই মেমরি লোকেশনের কপি একসাথে রাখতে পারে — যা L51-এর মাল্টিকোর ডিজাইনের সরাসরি পরিণতি।
প্র ০২ Modified আর Exclusive স্টেটের পার্থক্য কী — দুটোতেই তো শুধু একটা কোরের কাছেই কপি আছে?
ঠিক — দুটোতেই কপিটা একমাত্র সেই কোরের কাছে আছে, কিন্তু পার্থক্য হলো মেমরির সাথে "মেলে কিনা"। Exclusive মানে কপিটা এখনো মেমরির মানের সাথে ঠিক মেলে (এখনো লেখা হয়নি, "পরিষ্কার")। Modified মানে কোরটি ইতিমধ্যে নতুন মান লিখে ফেলেছে — মেমরির পুরনো মানের সাথে আর মেলে না ("ডার্টি"), তাই অন্য কোনো কোর পড়ার আগে এই নতুন মানটি মেমরিতে রাইট-ব্যাক করা (বা সরাসরি ফরওয়ার্ড করা) জরুরি।
প্র ০৩ যদি Core B নিজের Invalid ফ্ল্যাগ উপেক্ষা করে সরাসরি নিজের পুরনো লোকাল কপি থেকে মান পড়ে ফেলত, তাহলে কী ভুল হতো?
কোড সেলের উদাহরণে ঠিক এটাই হতো: Core B রিটার্ন পেত মান ১০ — যেখানে Core A ইতিমধ্যে সেই অ্যাড্রেসে ৯৯
লিখে ফেলেছে। এটাই স্টেল-ডেটা বাগ — সাইলেন্টলি ভুল ফলাফল, কোনো এরর বা ক্র্যাশ ছাড়াই। এই কারণেই
core_read ফাংশনে state == "I" চেকটি বাদ দেওয়া চলবে না — এটাই পুরো MESI
প্রোটোকলের কারেক্টনেস-গ্যারান্টির মূল ভিত্তি।
অনুশীলন
-
চিন্তা করুন: যদি Core A এবং Core C — উভয়েই একই সময়ে একই অ্যাড্রেসে লেখার চেষ্টা করে, তাহলে কী ঘটা উচিত? (ইঙ্গিত: M10/L47-এর বাস আর্বিট্রেশন মনে করুন।)
যেহেতু সব কোর একই শেয়ার্ড বাস ব্যবহার করে, বাস আর্বিটার (L47) একবারে শুধু একটি কোরকে বাস অ্যাক্সেস দেয়। ধরুন Core A প্রথমে বাস পেয়ে লেখে — তখন Core C-সহ বাকি সব কোরের কপি ইনভ্যালিডেট হয়ে যায়। এরপর Core C যখন তার লেখাটা করতে চায়, তাকে আগে নিজের Invalid কপি রি-ফেচ করতে হবে (বা সরাসরি লিখতে গেলে তার নিজের স্টেট Modified হবে এবং এবার Core A-সহ সবার কপি ইনভ্যালিডেট হবে)। বাস কখনো দুটো একযোগে, সাংঘর্ষিক লেখা অনুমোদন করে না — সবসময় সিরিয়ালাইজড।
-
পরীক্ষা করুন: কোড সেলে যদি তৃতীয় একটি কোর
"CoreC"যোগ করা হতো (এখনো কোড পরিবর্তন করবেন না),core_writeফাংশনের লুপে কী পরিবর্তন লাগবে?আসলে কোনো পরিবর্তনই লাগবে না —
core_writeইতিমধ্যেfor cid in cache_statesদিয়ে সব কোরের ওপর লুপ চালায়, তাই যতগুলো কোরইcache_statesডিকশনারিতে থাকুক না কেন, লেখা-কোর ছাড়া বাকি সবাই স্বয়ংক্রিয়ভাবে ইনভ্যালিডেট হয়ে যাবে। এটাই দেখায় কেন এই ডিজাইনটা genuinely স্কেলেবল — কোরের সংখ্যা বাড়লেও একই লজিক কাজ করে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ পরবর্তী পাঠ — GPU আর্কিটেকচার বেসিকস।
- Operating Systems কোর্স সহোদর কোর্স মাল্টিপ্রসেসর শিডিউলিং ও সিনক্রোনাইজেশন — এই হার্ডওয়্যার কোহেরেন্সের ওপরই সেই সফটওয়্যার-স্তর দাঁড়িয়ে আছে।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems ও Computer Architecture — সব এক জায়গায়।