পাঠ ৩৮ · ৫৭-এর মধ্যে · মডিউল ৮
Home / Courses / Computer Architecture & Digital Logic / ডিরেক্ট ম্যাপড ক্যাশ

ক্যাশ মেমরি বেসিকস — ডিরেক্ট ম্যাপড

Cache memory basics — direct mapped
৯ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • ক্যাশ হিট, মিস, ক্যাশ লাইন/ব্লক ও ট্যাগ — এই মৌলিক শব্দগুলোর নির্ভুল সংজ্ঞা
  • ডিরেক্ট ম্যাপড ক্যাশে ইনডেক্স কীভাবে গণনা হয় (মডুলো অপারেশন) এবং ট্যাগ কেন দরকার
  • ডিরেক্ট ম্যাপড ক্যাশের বাস্তব দুর্বলতা — conflict miss — কী এবং কেন এটা ঘটে
  • Python দিয়ে একটি বাস্তব DirectMappedCache ক্লাস বানিয়ে সরাসরি conflict miss প্রমাণ করা

১ · ক্যাশ কী — হিট, মিস, লাইন ও ট্যাগ

L37-এ দেখা মেমরি হায়ারার্কির ঠিক CPU-র সবচেয়ে কাছের হার্ডওয়্যার স্তর হলো ক্যাশ। CPU যখন কোনো মেমরি অ্যাড্রেস চায়, প্রথমে ক্যাশে খোঁজা হয় — পাওয়া গেলে সেটা ক্যাশ হিট (দ্রুত), না পাওয়া গেলে ক্যাশ মিস — তখন ধীর মেইন মেমরি থেকে আনতে হয়, এবং সাধারণত ভবিষ্যতের জন্য ক্যাশেও একটি কপি রাখা হয়। প্রতিবার এক বাইট নয়, বরং একটি গোটা ক্যাশ লাইন/ব্লকCache Line / Blockক্যাশ ও মেইন মেমরির মধ্যে একবারে স্থানান্তরিত ডেটার একক — একগুচ্ছ পরপর বাইট (পরপর কয়েকটি বাইটের গুচ্ছ) স্থানান্তরিত হয় — কারণ L37-এর স্পেশিয়াল লোকালিটি অনুযায়ী কাছাকাছি ডেটাও শীঘ্রই লাগতে পারে।

২ · ডিরেক্ট ম্যাপড ক্যাশ — সবচেয়ে সরল ম্যাপিং

একটি ক্যাশ সাধারণত মেইন মেমরির তুলনায় অনেক ছোট, তাই প্রশ্ন ওঠে — কোনো মেমরি ব্লক ক্যাশের কোন লাইনে বসবে? ডিরেক্ট ম্যাপড ক্যাশ-এ উত্তরটা সবচেয়ে সরল — প্রতিটি মেমরি ব্লক ঠিক একটিমাত্র সম্ভাব্য ক্যাশ লাইনে ম্যাপ হয়, যা গণনা হয়:

$\text{index} = \text{block\_address} \bmod \text{number\_of\_cache\_lines}$

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

দুর্বলতা · Conflict Miss

ডিরেক্ট ম্যাপড ক্যাশের একটি বাস্তব সমস্যা আছে — যদি দুটি ঘন ঘন ব্যবহৃত ব্লক ঠিক একই ইনডেক্সে ম্যাপ হয়ে যায়, তারা বারবার একে অপরকে evict করবে, যদিও ক্যাশের বাকি অংশে অনেক ফাঁকা জায়গা থাকতে পারে। এটাকে বলা হয় conflict miss — নিচের কোড সেলে ঠিক এই সমস্যাটাই সরাসরি দেখানো হয়েছে।

৩ · কোডে — একটি ডিরেক্ট ম্যাপড ক্যাশ ও একটি প্রকৃত conflict

নিচের কোড সেলে ৮টি লাইনের একটি ডিরেক্ট ম্যাপড ক্যাশ বানানো হয়েছে। address ৩ ও ১১ — এই দুটোর দূরত্ব ঠিক ৮ (number_of_cache_lines-এর সমান) — তাই দুটোই ইনডেক্স ৩-এ ম্যাপ হবে, কিন্তু ভিন্ন ট্যাগ নিয়ে। এই দুটো address পালাক্রমে বারবার অ্যাক্সেস করলে কী হয় দেখা যাক।

Python
NUM_CACHE_LINES = 8

class DirectMappedCache:
    def __init__(self, num_lines):
        self.num_lines = num_lines
        self.lines = [{"valid": False, "tag": None} for _ in range(num_lines)]

    def access(self, address):
        index = address % self.num_lines
        tag = address // self.num_lines
        line = self.lines[index]
        if line["valid"] and line["tag"] == tag:
            result = "HIT"
        else:
            result = "MISS"
            line["valid"] = True
            line["tag"] = tag
        return index, tag, result

cache = DirectMappedCache(NUM_CACHE_LINES)

# 3 ও 11 ঠিক NUM_CACHE_LINES (=8) দূরত্বে -- একই ইনডেক্সে ম্যাপ হবে, ভিন্ন ট্যাগ নিয়ে
conflict_sequence = [3, 11, 3, 11, 3, 11]

print("address | index | tag | ফলাফল")
print("-" * 34)
results = []
for addr in conflict_sequence:
    index, tag, result = cache.access(addr)
    results.append(result)
    print(f"{addr:>7} | {index:>5} | {tag:>3} | {result}")

miss_count = results.count("MISS")
print()
print(f"মোট {len(conflict_sequence)} বার অ্যাক্সেসে {miss_count} বার MISS হয়েছে -- প্রতিবারই!")
print("লক্ষ্য করুন: 3 ও 11 উভয়েই index=3-এ পড়ে (3 mod 8 = 3, 11 mod 8 = 3) কিন্তু ট্যাগ আলাদা")
print("(0 বনাম 1) -- তাই প্রতিবার একে অপরকে evict করছে -- এটাই direct-mapped cache-এর 'conflict' সমস্যা।")

    
লক্ষ্য করুন ক্যাশে মোট ৮টি লাইন থাকা সত্ত্বেও (মাত্র ২টি ব্লক সক্রিয়ভাবে ব্যবহৃত হচ্ছে), হিট রেট এখানে ০% — পুরো ক্যাশের ক্ষমতার প্রায় পুরোটাই অব্যবহৃত থেকে যাচ্ছে, শুধু একটি নির্দিষ্ট ইনডেক্সেই সংঘর্ষ হচ্ছে বলে। এটাই ডিরেক্ট ম্যাপড ডিজাইনের সবচেয়ে বড়, বাস্তব সীমাবদ্ধতা।
মূল কথা · Key takeaway

ডিরেক্ট ম্যাপড ক্যাশ সরল ও দ্রুত (একটিমাত্র লাইন চেক করলেই চলে), কিন্তু এই সরলতার মূল্য হলো conflict miss-এর ঝুঁকি — দুটো ব্যস্ত ব্লক দুর্ভাগ্যক্রমে একই ইনডেক্সে পড়লে ক্যাশ কার্যত অকেজো হয়ে যেতে পারে। পরের পাঠে (L39) ঠিক এই একই address সিকোয়েন্স একটি সেট-অ্যাসোসিয়েটিভ ক্যাশে চালিয়ে দেখা হবে সমস্যাটা কীভাবে সমাধান হয়।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ ট্যাগ ছাড়া শুধু ইনডেক্স মিললেই কি "হিট" ধরে নেওয়া যেত না? ট্যাগ আলাদাভাবে দরকার কেন?

না — কারণ ইনডেক্স শুধু বলে ব্লকটা কোন লাইনে থাকতে পারে, কোন নির্দিষ্ট ব্লক সেখানে আছে তা বলে না। যেমন address 3 ও 11 দুটোই index=3-এ ম্যাপ হয় — যদি ট্যাগ চেক না করা হতো, address 11-এর জন্য index=3 লাইনে address 3-এর পুরনো ডেটা থাকলেও ভুলভাবে "হিট" ধরে নেওয়া হতো — একটা প্রকৃত করাপশন বাগ। ট্যাগ-ই নিশ্চিত করে ইনডেক্স মিললেও সঠিক ব্লক আছে কি না।

প্র ০২ ক্যাশ যদি এক বাইট করে না এনে পুরো একটা "লাইন/ব্লক" আনে, তাতে কি অতিরিক্ত ডেটা মেমরি ট্রাফিক নষ্ট হচ্ছে না?

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

প্র ০৩ উপরের কোড সেলে address 3 আর 19 (৩ ও ১৯-এর দূরত্বও ১৬, অর্থাৎ ৮-এর গুণিতক) ব্যবহার করলে কি একই conflict দেখা যাবে?

হ্যাঁ — কারণ conflict ঘটার শর্ত হলো দুই address-এর দূরত্ব number_of_cache_lines-এর যেকোনো গুণিতক হওয়া (৮, ১৬, ২৪...), কারণ মডুলো অপারেশনে এরা সবাই একই ইনডেক্সে পড়বে। ৩ mod 8 = 3 এবং ১৯ mod 8 = 3 — তাই দুটো একই ইনডেক্সে সংঘর্ষ করবে, ঠিক ৩ ও ১১-এর মতোই।

অনুশীলন

  1. চিন্তা করুন: ৪টি লাইনের একটি ডিরেক্ট ম্যাপড ক্যাশে address 2, 5, 6, 9 পরপর অ্যাক্সেস করলে কোন দুটো address একই ইনডেক্সে পড়বে?

    ইনডেক্স = address mod 4: 2→2, 5→1, 6→2, 9→1। তাই address 2 ও 6 একই ইনডেক্স (2)-এ পড়বে, এবং address 5 ও 9 একই ইনডেক্স (1)-এ পড়বে — দুই জোড়া conflict তৈরি হবে।

  2. পরীক্ষা করুন: উপরের কোড সেলে conflict_sequence-এ যদি address 3 শুধু একবারই থাকত ([3, 11, 11, 11]), হিট/মিস প্যাটার্নটা কেমন হতো (এখনো কোড পরিবর্তন করবেন না)?

    address 3 (MISS, প্রথমবার), তারপর address 11 (MISS, কারণ index=3-এ এখন ট্যাগ 3-এর, 11-এর নয়) — কিন্তু তারপর 11 আবার দুইবার আসলে দুটোই HIT হবে, কারণ 11 নিজেই এখন লাইনটি দখল করে আছে এবং আর কোনো ভিন্ন address সেটাকে বিরক্ত করছে না। conflict শুধু তখনই বারবার ঘটে যখন দুটো ভিন্ন, ব্যস্ত address পালাক্রমে আসে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
মেমরি হায়ারার্কির ধারণা — লোকালিটি অফ রেফারেন্স