সরাসরি পর্বে যান
পর্ব ০৩ · ৬২-এর মধ্যে · মডিউল ১

পারফরম্যান্স মাপা — CPU time, CPI, MIPS, Amdahl-এর সূত্র আর benchmark

Measuring Performance — CPU Time, CPI, MIPS, Amdahl's Law & Benchmarks

Taught in Bangla, this lesson defines latency, throughput and 'n times faster', derives the iron law CPU time = IC × CPI × cycle time, computes weighted CPI from instruction mixes in worked exam numericals, exposes MIPS and MFLOPS pitfalls, explains SPEC benchmarks and the geometric mean, proves Amdahl's law with its multicore form and Gustafson's counterpoint, covers energy and race-to-idle, and measures real programs with time, perf stat and live C code.

এই পর্বে যা শিখবেন

  • “X, Y-এর চেয়ে n গুণ দ্রুত” — সঠিক সংজ্ঞা আর হিসাব
  • CPU time-এর লৌহসূত্র, প্রতিটি গুণক কে প্রভাবিত করে, আর instruction mix থেকে weighted CPI
  • MIPS, MFLOPS আর benchmark-এর ফাঁদ; SPEC কেন geometric mean নেয়
  • Amdahl-এর সূত্রের প্রমাণ, সীমা, multicore রূপ আর Gustafson-এর পাল্টা যুক্তি
  • Energy, power, race-to-idle আর বাস্তব প্রোগ্রামে time ও perf stat দিয়ে মাপা

১ · “দ্রুত” মানে আসলে কী — latency বনাম throughput

দুটি দোকানের কথা ভাবুন। প্রথমটি পাড়ার চায়ের দোকান — একজন মামা, অর্ডার দিলে এক মিনিটে চা হাতে। দ্বিতীয়টি অফিস-পাড়ার বড় ক্যান্টিন — অর্ডার দিলে পাঁচ মিনিট অপেক্ষা, কিন্তু একসাথে দশটি চুলায় রান্না চলে, ঘণ্টায় তিনশো কাপ বের হয়। কোনটি “দ্রুত”?

উত্তর নির্ভর করে আপনি কী জানতে চান তার ওপর। আপনি একা এক কাপ চা চাইলে মামার দোকান দ্রুত। আর অফিসের দুশো জনের চা দরকার হলে ক্যান্টিন দ্রুত। কম্পিউটারেও ঠিক এই দুই মাপকাঠি আছে:

Response time / latency

একটি কাজ শুরু থেকে শেষ হতে কত সময় লাগে। ব্যবহারকারী এটিই টের পান — অ্যাপ খুলতে কত সেকেন্ড, একটি বাটনে চাপ দেওয়ার পর কত মিলিসেকেন্ডে সাড়া।

একক: সেকেন্ড, ms, ns

Throughput / bandwidth

নির্দিষ্ট সময়ে কতগুলো কাজ শেষ হয়। সার্ভারের মালিক এটিই দেখেন — প্রতি সেকেন্ডে কতগুলো bKash লেনদেন, প্রতি ঘণ্টায় কতগুলো ভিডিও encode.

একক: কাজ/সেকেন্ড, GB/s

দুটি প্রায়ই একসাথে নড়ে, কিন্তু সবসময় নয়। কয়েকটি উদাহরণ:

  • দ্রুততর প্রসেসর বসালে প্রতিটি কাজ আগে শেষ হয় (latency কমে), ফলে প্রতি সেকেন্ডে বেশি কাজও হয় (throughput বাড়ে) — দুটোরই উন্নতি।
  • একই রকম আরও প্রসেসর যোগ করলে (একটি সার্ভারে আরও core) একটি কাজের latency বদলায় না, কিন্তু একসাথে বেশি কাজ চলে — শুধু throughput বাড়ে। ক্যান্টিনে নতুন চুলা বসানোর মতো।
  • ঢাকা–চট্টগ্রাম হাইওয়েতে লেন বাড়ালে ঘণ্টায় বেশি গাড়ি যায় (throughput), কিন্তু একটি গাড়ির যাত্রার সময় খালি রাস্তায় যা ছিল তাই (latency). বরং ভিড়ের সময় latency কমে, কারণ অপেক্ষা (queue) কমে — এখানে দুটি জড়িয়ে যায়।
মূল ধারণা

এই পর্বের বেশিরভাগ অংশে আমরা একটি প্রোগ্রাম চালাতে কত সময় লাগে — অর্থাৎ execution time — সেটিকে পারফরম্যান্সের মাপ ধরব। পরে pipelining (পর্ব ৩৫) আর multicore (পর্ব ৫৩)-এ দেখবেন, অনেক কৌশল একটি instruction-এর latency না কমিয়ে throughput বাড়ায়।

এই পর্বটি কোর্সের সবচেয়ে বেশি হিসাবের পর্বগুলোর একটি। পর্ব ০১-এ দেখেছেন কম্পিউটার কতগুলো স্তরে সাজানো, আর পর্ব ০২-এ দেখেছেন কেন clock rate একসময় থেমে গেল। এখন প্রশ্ন — কোনো নকশা “ভালো” কি না, সেটা সংখ্যা দিয়ে কীভাবে বলবেন? পরের প্রতিটি মডিউলে (pipeline, cache, multicore) এই পর্বের সূত্রগুলোই বারবার কাজে লাগবে।

২ · Performance = ১ / execution time, আর “n গুণ দ্রুত”-এর সঠিক অর্থ

সময় যত কম, পারফরম্যান্স তত বেশি। তাই সংজ্ঞাটি উল্টো অনুপাতের:

PerformanceX = 1 / Execution timeX

এখন দুটি কম্পিউটার X আর Y তুলনা করতে চাইলে:

PerformanceX / PerformanceY = TimeY / TimeX = n
অর্থাৎ “X, Y-এর চেয়ে n গুণ দ্রুত”

উদাহরণ: একটি প্রোগ্রাম কম্পিউটার A-তে ১০ সেকেন্ডে, B-তে ১৫ সেকেন্ডে চলে। তাহলে Time_B / Time_A = 15 / 10 = 1.5 — A, B-এর চেয়ে ১.৫ গুণ দ্রুত। একই কথা উল্টো দিক থেকে: B, A-এর চেয়ে ১.৫ গুণ ধীর।

সাধারণ ভুল: অনেকে লেখেন “A ৫০% দ্রুত” আর অন্যজন সেটা পড়েন “A-র সময় ৫০% কম”। দুটি এক নয়! A-র সময় B-এর চেয়ে ৩৩.৩% কম (১৫ থেকে ১০), কিন্তু A-র performance ৫০% বেশি। পরীক্ষায় আর ইন্টারভিউতে সবসময় অনুপাতে (“১.৫ গুণ”) উত্তর দিন, আর কোন দিক থেকে বলছেন তা স্পষ্ট লিখুন।

কোন “সময়” মাপছি?

একটি প্রোগ্রাম চালালে ঘড়ির কাঁটা যতটা ঘোরে, তাকে বলে wall-clock time (elapsed time) — এতে disk থেকে পড়ার অপেক্ষা, নেটওয়ার্কের অপেক্ষা, অন্য প্রোগ্রামের পালা — সব ঢুকে যায়। আর CPU আসলে এই প্রোগ্রামের কাজে যতক্ষণ ব্যস্ত ছিল, তাকে বলে CPU time. CPU time-কে আবার দুই ভাগ করা হয় — প্রোগ্রামের নিজের কোডে (user CPU time) আর প্রোগ্রামের হয়ে OS-এর কাজে (system CPU time). প্রসেসরের নকশা তুলনা করতে আমরা মূলত CPU time নিয়েই কাজ করব; ব্যবহারকারীর কাছে অবশ্য শেষ বিচারে wall-clock time-ই সত্য। অংশ ৯-এ দুটোই নিজের হাতে মেপে দেখবেন।

৩ · Clock আর CPU time-এর লৌহসূত্র

প্রসেসরের ভেতরে সবকিছু চলে একটি clock-এর তালে — একটি সংকেত যা নির্দিষ্ট ছন্দে 0 থেকে 1, আবার 1 থেকে 0 হয়। রেলস্টেশনের সিগন্যালের মতো: প্রতিটি “টিক”-এ সব register একসাথে নতুন মান নেয়। এক টিক থেকে পরের টিক পর্যন্ত সময় হলো একটি clock cycle; তার দৈর্ঘ্যকে বলে clock period বা cycle time (Tc), আর প্রতি সেকেন্ডে কতগুলো cycle — সেটি clock rate বা frequency (f).

Clock rate = 1 / Clock cycle time
1 GHz ↔ 1 ns · 2 GHz ↔ 0.5 ns = 500 ps · 4 GHz ↔ 250 ps

এককগুলো মনে রাখুন: ১ GHz = ১০৯ Hz, ১ ns = ১০−৯ s, ১ ps = ১০−১২ s. তাই ২.৫ GHz-এর cycle time = ১ / (২.৫ × ১০৯) = ০.৪ ns = ৪০০ ps.

তিন গুণকের সূত্র

একটি প্রোগ্রাম চালাতে CPU-র মোট কতগুলো clock cycle লাগল, সেটিকে cycle time দিয়ে গুণ করলেই CPU time:

CPU time = CPU clock cycles × Clock cycle time = CPU clock cycles / Clock rate

এখন clock cycle কোথা থেকে আসে? প্রোগ্রাম চালায় কিছু সংখ্যক instruction — একে বলে instruction count (IC). প্রতিটি instruction গড়ে কতগুলো cycle নেয়, সেটি CPI (cycles per instruction). তাহলে মোট cycle = IC × CPI. বসিয়ে দিলে পাওয়া যায় কম্পিউটার আর্কিটেকচারের সবচেয়ে বিখ্যাত সূত্রটি — অনেকে একে বলেন পারফরম্যান্সের লৌহসূত্র (iron law):

CPU time = IC × CPI × Tc = (IC × CPI) / f

এককগুলো মিলিয়ে দেখলে সূত্রটি নিজেই নিজেকে প্রমাণ করে:

seconds/program = (instructions/program) × (cycles/instruction) × (seconds/cycle)

একটি গার্মেন্টস কারখানায় একটি অর্ডারের শার্ট বানাতে কত সময় লাগবে? (১) কতগুলো সেলাইয়ের কাজ (IC), (২) প্রতিটি কাজে গড়ে কতগুলো মেশিন-ঘোরানো লাগে (CPI), (৩) মেশিন প্রতিবার ঘুরতে কত সময় নেয় (Tc). নকশা সহজ করে কাজ কমানো যায়, দক্ষ অপারেটর দিয়ে ঘোরানো কমানো যায়, দ্রুত মোটর দিয়ে প্রতিবারের সময় কমানো যায় — কিন্তু একটি কমাতে গিয়ে আরেকটি প্রায়ই বেড়ে যায়।

কোন গুণককে কে প্রভাবিত করে

এই টেবিলটি পরীক্ষায় খুব আসে, আর বাস্তবেও কাজের — কোনো পরিবর্তনের প্রভাব কোথায় পড়বে তা আগে থেকে অনুমান করা যায়:

যে বিষয় বদলায়ICCPIClock rate / Tcকেন
Algorithmহ্যাঁসম্ভবতনাকাজের পরিমাণ ঠিক করে; কোন ধরনের instruction বেশি (যেমন ভাগ বেশি হলে CPI বাড়ে)
প্রোগ্রামিং ভাষাহ্যাঁহ্যাঁনাভাষার গঠন কতগুলো ও কী ধরনের instruction-এ অনুবাদ হয় (যেমন interpreted ভাষায় বাড়তি কাজ)
Compilerহ্যাঁহ্যাঁনাকোন instruction বাছবে আর কোন ক্রমে সাজাবে
ISAহ্যাঁহ্যাঁহ্যাঁকী কী instruction আছে, প্রতিটি কত কাজ করে, hardware কত সহজে বানানো যায়
Microarchitecture (organization)নাহ্যাঁহ্যাঁpipeline, cache, branch predictor — CPI আর critical path (সবচেয়ে ধীর signal-পথ, যা cycle time ঠিক করে) ঠিক করে
Technology (transistor, process)নানা*হ্যাঁদ্রুত transistor মানে ছোট cycle time

* মেমরি আর CPU-র গতি আলাদা হারে বদলালে cycle-এ মাপা memory latency বদলে যায়, তাই বাস্তবে CPI-ও কিছুটা বদলায় — পর্ব ৪৭-এ cache-এর হিসাবে দেখবেন।

একটি ক্লাসিক হিসাব: clock কত হলে চলবে?

একটি প্রোগ্রাম ২ GHz-এর কম্পিউটার A-তে ১০ সেকেন্ডে চলে। নকশাকারী চান নতুন কম্পিউটার B-তে এটি ৬ সেকেন্ডে চলুক। B-এর clock বাড়ানো যাবে, কিন্তু তার ফলে একই প্রোগ্রামে A-র চেয়ে ১.২ গুণ বেশি clock cycle লাগবে। B-এর clock rate কত হতে হবে?

  1. A-র cycle = time × rate = ১০ × ২ × ১০৯ = ২০ × ১০৯.
  2. B-এর cycle = ১.২ × ২০ × ১০৯ = ২৪ × ১০৯.
  3. B-এর rate = cycle / time = ২৪ × ১০৯ / ৬ = ৪ × ১০৯ = ৪ GHz.

অর্থাৎ ১.৬৭ গুণ দ্রুত হতে clock দ্বিগুণ করতে হচ্ছে — কারণ বাড়তি cycle-এর দাম দিতে হয়েছে।

৪ · CPI আর instruction mix — ধাপে ধাপে হিসাব

প্রথমে সবচেয়ে সরল তুলনা: একই ISA-র দুটি machine, একই প্রোগ্রাম, তাই IC একই। একটির clock দ্রুত, অন্যটির CPI কম। কে জিতবে? “পরে” চাপুন:

হিসাবের খাতা (IC = 10⁹, একই ISA)
এই ধাপের কাজ
Machine A (Tc = 250 ps, CPI 2.0)
Machine B (Tc = 500 ps, CPI 1.2)
ফল
  1. তথ্য সাজানো। দুই machine-এ একই ISA, তাই একই প্রোগ্রামের instruction count একই: IC = ১০৯. A-র cycle time ২৫০ ps (৪ GHz), CPI ২.০; B-এর cycle time ৫০০ ps (২ GHz), CPI ১.২. শুধু CPI দেখলে B ভালো, শুধু clock দেখলে A ভালো — তাই পুরো সূত্র লাগবে।
  2. A-র মোট cycle. মোট cycle = IC × CPI = ১০৯ × ২.০ = ২ × ১০৯ cycle.
  3. A-র CPU time. cycle × cycle time = ২ × ১০৯ × ২৫০ × ১০−১২ s = ০.৫ s. (অথবা ২ × ১০৯ / ৪ GHz — একই উত্তর।)
  4. B-এর মোট cycle. ১০৯ × ১.২ = ১.২ × ১০৯ cycle — A-র চেয়ে অনেক কম cycle!
  5. B-এর CPU time. ১.২ × ১০৯ × ৫০০ × ১০−১২ s = ০.৬ s. প্রতিটি cycle দ্বিগুণ লম্বা, তাই কম cycle-এর সুবিধা পুরোটা টেকেনি।
  6. অনুপাত। TimeB / TimeA = ০.৬ / ০.৫ = ১.২. অর্থাৎ A, B-এর চেয়ে ১.২ গুণ দ্রুত। শিক্ষা: CPI আর clock rate আলাদাভাবে কিছুই বলে না, তাদের গুণফল বলে। IC আলাদা হলে (ভিন্ন ISA বা compiler) তিনটিই গুণ করতে হবে।

Instruction mix থেকে weighted CPI

বাস্তবে সব instruction সমান cycle নেয় না। যোগ-বিয়োগ (ALU) হয়তো ১ cycle-এ, মেমরি থেকে load করতে বেশি, শাখা (branch) মাঝামাঝি। প্রোগ্রামে কোন ধরনের instruction কত ভাগ — সেটিকে বলে instruction mix. তখন CPI হলো প্রতিটি class-এর CPI-এর ভারযুক্ত গড়:

CPU clock cycles = Σ (CPIi × ICi)
CPI = Σ (CPIi × Fi), যেখানে Fi = ICi / IC

কেন এটা ঠিক? মোট cycle = প্রতিটি class-এর (instruction সংখ্যা × সেই class-এর CPI)-এর যোগফল। দুই পাশকে IC দিয়ে ভাগ করলে বাঁ পাশে আসে CPI-এর সংজ্ঞা (cycle/IC), আর ডান পাশে প্রতিটি ICi/IC হয়ে যায় ভগ্নাংশ Fi. তাই weighted CPI কোনো আনুমানিক কৌশল নয় — এটি সংজ্ঞা থেকেই আসা একটি অভেদ।

নিচের mix-টি একটি সরল multi-cycle প্রসেসরের মতো ধরা (load ধীর, কারণ মেমরিতে যেতে হয়)। ধাপে ধাপে যোগফল দেখুন:

Class (Fi, CPIi) → অবদান Fi × CPIi
ALU (50%, CPI 1)
Load (20%, CPI 5)
Store (10%, CPI 3)
Branch (20%, CPI 2)
CPI (এ পর্যন্ত যোগফল)
  1. Mix সাজানো। চারটি class: ALU ৫০%, Load ২০%, Store ১০%, Branch ২০% — ভগ্নাংশগুলোর যোগফল ১ হতেই হবে (০.৫ + ০.২ + ০.১ + ০.২ = ১)। যোগফল এখনো ০।
  2. ALU. অবদান ০.৫ × ১ = ০.৫. সবচেয়ে বেশি instruction এই class-এর, কিন্তু প্রতিটি সস্তা।
  3. Load. অবদান ০.২ × ৫ = ১.০. মাত্র ২০% instruction, অথচ অবদান ALU-র দ্বিগুণ! যোগফল ১.৫.
  4. Store. অবদান ০.১ × ৩ = ০.৩. যোগফল ১.৮.
  5. Branch. অবদান ০.২ × ২ = ০.৪. চূড়ান্ত CPI = ২.২.
  6. সময় কোথায় যায়। প্রতিটি অবদানকে ২.২ দিয়ে ভাগ করলে সময়ের ভাগ: Load ১.০ / ২.২ ≈ ৪৫.৫% — সবচেয়ে বড়। তাই উন্নতির জায়গা load: cache ভালো করে load-এর CPI ৫ থেকে ২ করলে নতুন CPI = ০.৫ + ০.৪ + ০.৩ + ০.৪ = ১.৬, speedup = ২.২ / ১.৬ = ১.৩৭৫. “সবচেয়ে বেশি সময় যেখানে, আগে সেখানে হাত দিন” — এই ভাবনাই অংশ ৭-এর Amdahl-এর সূত্র।

পরীক্ষার ক্লাসিক: দুই compiler, কে দ্রুত?

একটি ২ GHz machine-এ তিন class-এর instruction: A (CPI ১), B (CPI ২), C (CPI ৩)। একই C প্রোগ্রাম দুটি compiler দুইভাবে অনুবাদ করল (সংখ্যা ১০৯ এককে):

CompilerABCICমোট cycleCPIসময় (২ GHz)
Compiler 12125 × 10⁹2·1 + 1·2 + 2·3 = 10 × 10⁹2.05.0 s
Compiler 24116 × 10⁹4·1 + 1·2 + 1·3 = 9 × 10⁹1.54.5 s

Compiler 2 বেশি instruction দিয়েছে (৬ বনাম ৫), তবু কম cycle লেগেছে, কারণ সে দামি C-class instruction কম ব্যবহার করেছে। Compiler 2-এর কোড ৫.০ / ৪.৫ ≈ ১.১১ গুণ দ্রুত। শিক্ষা: IC কম মানেই দ্রুত নয়।

নিচের simulator লৌহসূত্র দিয়ে কয়েকটি পরিস্থিতি হিসাব করে — CPU time, effective CPI, MIPS (পরের অংশে ব্যাখ্যা) আর “কতগুণ দ্রুত”। শেষ দুটি পরিস্থিতি খেয়াল করুন। Run চাপুন, সংখ্যা বদলে নিজের প্রশ্নও চালান:

// Iron law: CPU time = IC x CPI / clock rate. mix = [[count in 1e9, CPI], ...]
function run(name, mix, clockGHz) {
  let ic = 0, cycles = 0;
  for (const [n, cpi] of mix) { ic += n * 1e9; cycles += n * 1e9 * cpi; }
  const time = cycles / (clockGHz * 1e9);            // seconds
  const cpi = cycles / ic, mips = ic / (time * 1e6);
  console.log(`  ${name.padEnd(11)} IC=${(ic / 1e9).toFixed(1)}e9  CPI=${cpi.toFixed(3)}` +
              `  time=${time.toFixed(3)} s  MIPS=${mips.toFixed(0)}`);
  return { name, time, mips };
}
function compare(a, b) {
  const [fast, slow] = a.time <= b.time ? [a, b] : [b, a];
  const byMips = a.mips >= b.mips ? a : b;
  console.log(`  -> ${fast.name} is ${(slow.time / fast.time).toFixed(3)} times faster;` +
              ` higher MIPS: ${byMips.name}` + (byMips === fast ? " (agrees)" : "  <-- MIPS is WRONG"));
}
console.log("1) Same program, same ISA: A = 4 GHz CPI 2.0, B = 2 GHz CPI 1.2");
compare(run("Machine A", [[1, 2.0]], 4), run("Machine B", [[1, 1.2]], 2));
console.log("2) Two compilers on a 2 GHz machine, classes A/B/C have CPI 1/2/3");
compare(run("Compiler 1", [[2, 1], [1, 2], [2, 3]], 2), run("Compiler 2", [[4, 1], [1, 2], [1, 3]], 2));
console.log("3) Two compilers on a 4 GHz machine, same CPI 1/2/3");
compare(run("Compiler X", [[5, 1], [1, 2], [1, 3]], 4), run("Compiler Y", [[10, 1], [1, 2], [1, 3]], 4));
console.log("4) Different ISAs, both 3 GHz: simple ISA needs more instructions");
compare(run("Simple ISA", [[1.5, 1.0]], 3), run("Rich ISA", [[1.0, 1.2]], 3));

আউটপুট (নির্ধারিত — প্রতিবার একই):

1) Same program, same ISA: A = 4 GHz CPI 2.0, B = 2 GHz CPI 1.2
  Machine A   IC=1.0e9  CPI=2.000  time=0.500 s  MIPS=2000
  Machine B   IC=1.0e9  CPI=1.200  time=0.600 s  MIPS=1667
  -> Machine A is 1.200 times faster; higher MIPS: Machine A (agrees)
2) Two compilers on a 2 GHz machine, classes A/B/C have CPI 1/2/3
  Compiler 1  IC=5.0e9  CPI=2.000  time=5.000 s  MIPS=1000
  Compiler 2  IC=6.0e9  CPI=1.500  time=4.500 s  MIPS=1333
  -> Compiler 2 is 1.111 times faster; higher MIPS: Compiler 2 (agrees)
3) Two compilers on a 4 GHz machine, same CPI 1/2/3
  Compiler X  IC=7.0e9  CPI=1.429  time=2.500 s  MIPS=2800
  Compiler Y  IC=12.0e9  CPI=1.250  time=3.750 s  MIPS=3200
  -> Compiler X is 1.500 times faster; higher MIPS: Compiler Y  <-- MIPS is WRONG
4) Different ISAs, both 3 GHz: simple ISA needs more instructions
  Simple ISA  IC=1.5e9  CPI=1.000  time=0.500 s  MIPS=3000
  Rich ISA    IC=1.0e9  CPI=1.200  time=0.400 s  MIPS=2500
  -> Rich ISA is 1.250 times faster; higher MIPS: Simple ISA  <-- MIPS is WRONG

পরিস্থিতি ১ ঠিক উপরের stepper: ০.৫ s বনাম ০.৬ s, A ১.২ গুণ দ্রুত। পরিস্থিতি ২ হলো দুই compiler-এর টেবিল — এখানে MIPS আর সময় একই রায় দিয়েছে। কিন্তু পরিস্থিতি ৩-এ Compiler Y-এর কোড ৩.৭৫ s নেয়, X-এর ২.৫ s — X ১.৫ গুণ দ্রুত, অথচ Y-এর MIPS বেশি (৩২০০ বনাম ২৮০০)! কারণ Y অনেকগুলো বাড়তি সস্তা instruction যোগ করেছে: IC বেড়েছে, গড় CPI কমেছে, MIPS ফুলে উঠেছে, কিন্তু আসল কাজ ধীর হয়েছে। পরিস্থিতি ৪-এ একই ঘটনা দুই ISA-র মধ্যে — সরল ISA-র MIPS বেশি, কিন্তু প্রোগ্রাম ধীর।

৫ · MIPS আর MFLOPS — সহজ সংখ্যা, কিন্তু ফাঁদ

CPU time-এর হিসাবে তিনটি সংখ্যা লাগে, অথচ বিজ্ঞাপনে মানুষ একটি সংখ্যা চায়। তাই একসময় খুব জনপ্রিয় ছিল MIPS — million instructions per second (প্রতি সেকেন্ডে কত দশ লক্ষ instruction; ১ MIPS = ১০৬ instruction/s). সতর্কতা: এখানে MIPS একটি মাপকাঠি, MIPS নামের ISA (পর্ব ২৫-এ দেখবেন) নয়।

MIPS = IC / (Execution time × 106) = Clock rate / (CPI × 106)

দ্বিতীয় রূপটি দেখলেই সমস্যা ধরা পড়ে: MIPS-এ IC নেই! অথচ CPU time = IC / (MIPS × ১০৬)। তিনটি কারণে MIPS ভুল রায় দিতে পারে:

  1. ভিন্ন ISA-র তুলনা চলে না। একটি জটিল instruction যে কাজ করে, সরল ISA-তে তার জন্য তিনটি লাগতে পারে। “বেশি instruction প্রতি সেকেন্ড” তখন “বেশি কাজ প্রতি সেকেন্ড” নয়।
  2. একই machine-এও প্রোগ্রাম ভেদে MIPS বদলায়। Instruction mix বদলালে CPI বদলায়, তাই কোনো machine-এর “একটি MIPS সংখ্যা” হয় না।
  3. MIPS পারফরম্যান্সের উল্টো দিকে যেতে পারে। উপরের পরিস্থিতি ৩-এর মতো — সস্তা instruction যোগ করে MIPS বাড়ানো যায়, অথচ সময় বাড়ে।

এজন্য রসিকতা করে MIPS-এর পূর্ণরূপ বলা হতো “Meaningless Indicator of Processor Speed”. তবে একই ISA, একই compiler আর একই প্রোগ্রামে MIPS দিয়ে তুলনা চলে — তখন IC স্থির, আর MIPS সময়ের উল্টো অনুপাতে চলে।

MFLOPS আর peak FLOPS

বিজ্ঞান ও AI-এর কাজে মাপা হয় MFLOPS — প্রতি সেকেন্ডে কত মিলিয়ন floating-point operation (ভগ্নাংশসহ সংখ্যার যোগ-গুণ)। এখানেও ফাঁদ আছে: যোগ আর ভাগের খরচ এক নয়, ভিন্ন machine-এ ভিন্ন operation আছে, আর বিজ্ঞাপনের সংখ্যাটি প্রায়ই peak — তাত্ত্বিক সর্বোচ্চ:

Peak FLOPS = cores × clock rate × FLOP per cycle per core

ধরা যাক ৮ core, ৩ GHz, আর প্রতিটি core প্রতি cycle-এ ১৬টি double-precision (৬৪-বিট) FLOP করতে পারে (যেমন দুটি ২৫৬-bit FMA (গুণ আর যোগ এক ধাপে) unit: ২ × ৪ সংখ্যা × ২ operation). তাহলে peak = ৮ × ৩ × ১০৯ × ১৬ = ৩৮৪ GFLOPS. বাস্তব প্রোগ্রাম এর কাছাকাছি পৌঁছায় কেবল তখনই, যখন ডেটা ঠিক সময়ে cache থেকে আসে আর কোড SIMD (পর্ব ৪২) ব্যবহার করে। সাধারণ প্রোগ্রামে sustained (টানা যা পাওয়া যায়) গতি এর অনেক নিচে থাকে।

৬ · Benchmark — SPEC, geometric mean আর সাধারণ ভুল

সবচেয়ে সৎ মাপ হলো আপনার নিজের প্রোগ্রাম চালিয়ে দেখা। কিন্তু কেনার আগে সব প্রোগ্রাম চালানো সম্ভব নয়। তাই তৈরি হয়েছে benchmark — বাস্তব প্রোগ্রাম থেকে বাছাই করা একটি সংগ্রহ, যা সবাই একই নিয়মে চালায়।

SPEC CPU

১৯৮৮ সালে কয়েকটি কম্পিউটার কোম্পানি মিলে অলাভজনক সংস্থা SPEC (Standard Performance Evaluation Corporation) গঠন করে। এর CPU benchmark (বহু বছর ধরে প্রচলিত সংস্করণ SPEC CPU 2017) দুই ভাগে: integer (compiler, compression, chess engine, XML প্রক্রিয়ার মতো প্রোগ্রাম) আর floating point (আবহাওয়া, পদার্থবিজ্ঞান, molecular dynamics-এর মতো বৈজ্ঞানিক প্রোগ্রাম)। প্রতিটি আবার দুইভাবে চালানো হয় — “speed” (একটি কাজ কত দ্রুত — latency) আর “rate” (একসাথে অনেক কপি — throughput).

প্রতিটি প্রোগ্রামের জন্য হিসাব করা হয় একটি অনুপাত: SPECratio = reference machine-এর সময় / আপনার machine-এর সময়. তারপর সব অনুপাতের geometric mean নেওয়া হয়:

Geometric mean = (ratio1 × ratio2 × … × ration)1/n

কেন geometric mean, arithmetic নয়?

কারণ অনুপাতের গাণিতিক গড় (arithmetic mean) নির্ভর করে কোন machine-কে reference ধরা হলো তার ওপর — reference বদলালে রায়ও উল্টে যেতে পারে। Geometric mean-এ এই সমস্যা নেই: GM(Tref/TX) / GM(Tref/TY) = GM(TY/TX), অর্থাৎ ভাগ করলে reference-এর সময় কেটে যায়। একটি ছোট উদাহরণ — দুটি প্রোগ্রাম P1, P2; X-এ সময় ১০ s ও ৫০ s, Y-এ ২০ s ও ২৫ s:

Reference-এর সময় (P1, P2)X-এর ratioY-এর ratioArithmetic mean (X, Y)Geometric mean (X, Y)
100 s, 100 s10, 25, 46.0, 4.5 → X “ভালো”4.47, 4.47 → সমান
10 s, 1000 s1, 200.5, 4010.5, 20.25 → Y “ভালো”4.47, 4.47 → সমান

একই দুটি machine, একই মাপা সময় — শুধু reference বদলাতেই arithmetic mean রায় উল্টে দিল, আর geometric mean স্থির থাকল। (তবে মনে রাখুন, geometric mean মোট সময়ের সাথে মেলে না; নিজের নির্দিষ্ট কাজের মিশ্রণ জানা থাকলে ভারযুক্ত মোট সময়ই সবচেয়ে অর্থপূর্ণ।)

অন্যান্য benchmark

  • Geekbench — ফোন আর ল্যাপটপের রিভিউতে খুব দেখা যায়; কয়েকটি ছোট কাজ (যেমন compression, ছবি প্রক্রিয়া) মিলিয়ে একটি single-core আর একটি multi-core স্কোর দেয়। তুলনার জন্য সুবিধাজনক, তবে কাজগুলো ছোট বলে বড় প্রোগ্রামের memory আচরণ পুরোপুরি ধরা পড়ে না বলে সমালোচনা আছে।
  • MLPerf — MLCommons নামের একটি consortium-এর benchmark, machine learning-এর training আর inference মাপে; AI accelerator (পর্ব ৫৫) তুলনায় ব্যবহৃত হয়।
  • LINPACK / HPL — বিশাল ম্যাট্রিক্স সমীকরণ সমাধান; বিশ্বের সুপারকম্পিউটারের TOP500 তালিকা এটি দিয়ে সাজানো হয়।

সাধারণ ভুল ও ফাঁদ

  • Synthetic benchmark: Dhrystone-এর মতো ছোট কৃত্রিম প্রোগ্রাম বাস্তব কাজের প্রতিনিধি নয়, আর compiler বিশেষ কৌশলে এদের প্রায় “উড়িয়ে” দিতে পারে।
  • Peak বনাম sustained: তাত্ত্বিক সর্বোচ্চ (peak) প্রায় কখনো টানা পাওয়া যায় না। দাবি দেখলে জিজ্ঞেস করুন — কোন প্রোগ্রামে, কতক্ষণ?
  • সূত্রের একটি অংশ দিয়ে বিচার: শুধু GHz, শুধু core সংখ্যা, বা শুধু MIPS — লৌহসূত্রের তিন গুণকের একটি দিয়ে রায় দেওয়া সবচেয়ে প্রচলিত ভুল।
  • Benchmark-এর জন্য বিশেষ tuning: কোম্পানি চাইলে compiler-কে নির্দিষ্ট benchmark চিনে বিশেষ কৌশল প্রয়োগ করাতে পারে। SPEC-এর নিয়মে এমন benchmark-নির্দিষ্ট optimization নিষিদ্ধ, আর ফলাফলের সাথে compiler flag প্রকাশ করতে হয়।
  • এক প্রোগ্রামের উন্নতিকে সবখানে ধরে নেওয়া: একটি অংশ দ্রুত হলে পুরো প্রোগ্রাম ততটা দ্রুত হয় না — ঠিক পরের অংশের বিষয়।

৭ · Amdahl-এর সূত্র — যে অংশ দ্রুত হয়নি, সেটিই সীমা

ধরুন ঢাকা থেকে কক্সবাজার যাবেন। প্লেনে ঢাকা–কক্সবাজার এক ঘণ্টা, কিন্তু বাসা থেকে বিমানবন্দরে যেতে, চেক-ইন আর অপেক্ষায় লাগে আরও তিন ঘণ্টা। এখন প্লেন দ্বিগুণ দ্রুত হলেও মোট যাত্রা চার ঘণ্টা থেকে মাত্র সাড়ে তিন ঘণ্টায় নামে। প্লেন অসীম দ্রুত হলেও তিন ঘণ্টার নিচে নামবে না। এই সাধারণ সত্যটির নামই Amdahl-এর সূত্র — ১৯৬৭ সালে IBM-এর প্রকৌশলী Gene Amdahl এই যুক্তি দেন।

প্রমাণ (derivation)

পুরোনো execution time-কে ১ ধরুন। এর মধ্যে f ভগ্নাংশ (০ ≤ f ≤ ১) এমন অংশ, যা আমরা s গুণ দ্রুত করতে পারি; বাকি (১ − f) অংশ যেমন ছিল তেমন থাকে।

  1. অপরিবর্তিত অংশের নতুন সময় = (১ − f) × ১ = ১ − f.
  2. উন্নত অংশের নতুন সময় = f / s (s গুণ দ্রুত মানে সময় s ভাগের এক)।
  3. নতুন মোট সময় = (১ − f) + f / s.
  4. Speedup = পুরোনো সময় / নতুন সময় = ১ / ((১ − f) + f/s).
Speedupoverall = 1 / ((1 − f) + f / s)
s → ∞ হলে Speedup → 1 / (1 − f)

সীমার যুক্তি: s যত বড়ই হোক, f/s > ০, তাই হর সবসময় (১ − f)-এর চেয়ে বড় — speedup কখনো ১/(১ − f) ছোঁয় না, শুধু তার দিকে এগোয়। আর s বাড়লে হর কমে, তাই speedup s-এর সাথে বাড়তে থাকে কিন্তু ক্রমশ কম হারে (diminishing returns).

উদাহরণ ১: একটি প্রোগ্রামের ৮০% সময় একটি অংশে যায়, সেটিকে ৪ গুণ দ্রুত করলেন। Speedup = ১ / (০.২ + ০.৮/৪) = ১ / (০.২ + ০.২) = ১ / ০.৪ = ২.৫ — ৪ নয়।
উদাহরণ ২: একই অংশকে অসীম দ্রুত করলেও (সময় শূন্য) speedup = ১ / ০.২ = ৫, তার বেশি কখনো নয়।

নিচের stepper-এ f = ০.৯ (৯০% অংশ দ্রুত করা যায়) রেখে s ধাপে ধাপে বাড়ানো হয়েছে। ▓ = অপরিবর্তিত অংশের সময়, ░ = দ্রুত হওয়া অংশের সময় (২০ ঘর = পুরোনো মোট সময়):

নতুন সময় = 0.1 + 0.9 / s
s (উন্নত অংশ কতগুণ দ্রুত)
অপরিবর্তিত অংশ (1 − f)
উন্নত অংশ (f / s)
নতুন মোট সময়
Speedup
সময়ের ছবি
  1. শুরু, s = ১. কিছুই দ্রুত হয়নি: সময় ০.১ + ০.৯ = ১.০, speedup ১. পুরো বারের মাত্র ১০% (দুই ঘর ▓) এমন অংশ, যা আমরা ছুঁতে পারব না।
  2. s = ২. উন্নত অংশ ০.৯ / ২ = ০.৪৫. মোট ০.৫৫, speedup ১ / ০.৫৫ ≈ ১.৮২ — ২-এর কাছাকাছি, কারণ বেশিরভাগ সময় উন্নত অংশেই ছিল।
  3. s = ৪. উন্নত অংশ ০.২২৫, মোট ০.৩২৫, speedup ≈ ৩.০৮. এখনই ৪ থেকে অনেকটা পিছিয়ে — ছোট্ট ▓ অংশটি মোট সময়ের প্রায় এক-তৃতীয়াংশ হয়ে গেছে।
  4. s = ১০. উন্নত অংশ ০.০৯ — অপরিবর্তিত অংশের (০.১) চেয়েও ছোট! মোট ০.১৯, speedup ≈ ৫.২৬. এখন থেকে বড় খরচ ▓ অংশ।
  5. s = ১০০. উন্নত অংশ ০.০০৯ — প্রায় অদৃশ্য। মোট ০.১০৯, speedup ≈ ৯.১৭. ১০ গুণ বেশি খাটুনি (s = ১০ থেকে ১০০) দিয়ে speedup ৫.২৬ থেকে মাত্র ৯.১৭.
  6. s = ১০০০. মোট ০.১০০৯, speedup ≈ ৯.৯১. আরও ১০ গুণ খাটুনিতে লাভ ১-এরও কম।
  7. s → ∞. উন্নত অংশ শূন্য, মোট শুধু ০.১, speedup = ১ / ০.১ = ১০ — এটিই ছাদ, ১ / (১ − f). শিক্ষা: f = ০.৯ হলে কোনো জাদুতেই ১০ গুণের বেশি হবে না; আরও চাইলে অপরিবর্তিত ১০%-কেই আক্রমণ করতে হবে।

Multicore রূপ: serial অংশ সমান্তরালতার সীমা টানে

N টি core-এ প্রোগ্রামের সমান্তরাল অংশ (১ − sf) আদর্শভাবে N ভাগ হয়, আর serial ভগ্নাংশ sf (এক core-এ মাপা মূল সময়ের ভাগ) একটি core-এই চলে। তাহলে Amdahl-এ s = N বসিয়ে:

Speedup(N) = 1 / (sf + (1 − sf) / N) · সীমা 1 / sf

মাত্র ৫% serial অংশ থাকলে ১০০০ core দিয়েও speedup ২০-এর নিচে থাকবে। আর বাস্তবে core-দের মধ্যে যোগাযোগ, lock আর cache coherence (পর্ব ৫৩)-এর বাড়তি খরচ এটিকে আরও কমায়।

Gustafson-এর পাল্টা যুক্তি: সমস্যাও বড় হয়

১৯৮৮ সালে John Gustafson লক্ষ করেন, বাস্তবে বেশি core পেলে মানুষ একই ছোট সমস্যা দ্রুত শেষ করে না — বরং বড় সমস্যা (বেশি রেজোলিউশনের আবহাওয়ার মডেল, বড় AI মডেল) একই সময়ে চালায়। তখন serial অংশ মোটামুটি একই থাকে আর সমান্তরাল অংশ N-এর সাথে বাড়ে। এই “scaled speedup” হলো:

Scaled speedup (Gustafson) = sf + (1 − sf) × N = N − sf (N − 1)

এখানে sf হলো সমান্তরাল machine-এ মাপা সময়ের serial ভগ্নাংশ। দুটি সূত্র পরস্পরবিরোধী নয় — Amdahl প্রশ্ন করেন “নির্দিষ্ট কাজ কত দ্রুত হবে?”, Gustafson প্রশ্ন করেন “নির্দিষ্ট সময়ে কত বড় কাজ হবে?”। নিচের simulator দুটিকেই পাশাপাশি হিসাব করে:

// Amdahl: speedup = 1 / ((1 - f) + f / s); s = Infinity gives the limit 1 / (1 - f)
const amdahl = (f, s) => 1 / ((1 - f) + f / s);
const S = [2, 4, 16, Infinity];
console.log("f       " + S.map(s => ("s=" + (s === Infinity ? "inf" : s)).padStart(8)).join(""));
for (const f of [0.5, 0.8, 0.9, 0.95, 0.99])
  console.log(f.toFixed(2).padEnd(8) + S.map(s => amdahl(f, s).toFixed(3).padStart(8)).join(""));

console.log("\nN cores   Amdahl 5%  Amdahl 10%  Gustafson 5%  Gustafson 10%");
for (const N of [1, 2, 4, 8, 16, 32, 64]) {
  const a = sf => (1 / (sf + (1 - sf) / N)).toFixed(2);   // fixed problem size
  const g = sf => (N - sf * (N - 1)).toFixed(2);          // problem grows with N
  console.log(String(N).padStart(7) + a(0.05).padStart(12) + a(0.10).padStart(12) +
              g(0.05).padStart(14) + g(0.10).padStart(15));
}
console.log("\nlimit with 1000000 cores: Amdahl 5% = " + amdahl(0.95, 1e6).toFixed(2) +
            ", Amdahl 10% = " + amdahl(0.90, 1e6).toFixed(2));

আউটপুট (নির্ধারিত):

f            s=2     s=4    s=16   s=inf
0.50       1.333   1.600   1.882   2.000
0.80       1.667   2.500   4.000   5.000
0.90       1.818   3.077   6.400  10.000
0.95       1.905   3.478   9.143  20.000
0.99       1.980   3.883  13.913 100.000

N cores   Amdahl 5%  Amdahl 10%  Gustafson 5%  Gustafson 10%
      1        1.00        1.00          1.00           1.00
      2        1.90        1.82          1.95           1.90
      4        3.48        3.08          3.85           3.70
      8        5.93        4.71          7.65           7.30
     16        9.14        6.40         15.25          14.50
     32       12.55        7.80         30.45          28.90
     64       15.42        8.77         60.85          57.70

limit with 1000000 cores: Amdahl 5% = 20.00, Amdahl 10% = 10.00

প্রথম টেবিলে প্রতিটি সারির শেষ কলাম (s = inf) হলো ছাদ ১/(১ − f): f = ০.৮-এ ৫, f = ০.৯৯-এ ১০০. খেয়াল করুন f = ০.৮, s = ৪-এ ঠিক ২.৫০০ — উপরের উদাহরণ ১। দ্বিতীয় টেবিলে ৬৪ core দিয়ে ১০% serial অংশে Amdahl-এর speedup মাত্র ৮.৭৭, আর ৫%-এ ১৫.৪২ — অথচ Gustafson-এর scaled speedup ৫৭.৭০ ও ৬০.৮৫ (মনে রাখুন, দুই সূত্রে ৫% বা ১০%-এর অর্থ আলাদা: Amdahl-এ এক core-এর মূল সময়ের ভাগ, Gustafson-এ N-core machine-এ মাপা সময়ের ভাগ)। দশ লক্ষ core-এও Amdahl দুই দশমিকে ২০.০০ আর ১০.০০-তে আটকে থাকে — ছাদের প্রায় গায়ে, কিন্তু কখনো পেরিয়ে নয়। পার্থক্যটা প্রশ্নের: সমস্যার আকার স্থির, না core-এর সাথে বাড়ে।

৮ · Power আর energy — দ্রুত হওয়াই একমাত্র লক্ষ্য নয়

ফোনে চার্জ কতক্ষণ থাকবে, ল্যাপটপ কতটা গরম হবে, ডেটা সেন্টারের বিদ্যুৎ বিল কত — আজকের নকশায় এগুলো গতির মতোই গুরুত্বপূর্ণ। দুটি শব্দ আলাদা করে বুঝুন:

  • Power (ক্ষমতা): প্রতি সেকেন্ডে কত energy খরচ হচ্ছে, একক watt (W = J/s). এটি ঠিক করে chip কতটা গরম হবে আর কতটা ঠান্ডা করার ব্যবস্থা লাগবে।
  • Energy (শক্তি): একটি কাজ শেষ করতে মোট কত খরচ, একক joule (J). ব্যাটারির চার্জ আর বিদ্যুৎ বিল এটিই ঠিক করে।
Energy per task = Average power × Execution time

CMOS chip-এর dynamic power মোটামুটি α × C × V² × f-এর সমানুপাতিক — voltage আর frequency কমালে power অনেক কমে; এর বিস্তারিত আর “power wall”-এর গল্প আছে পর্ব ০২-এ, আর embedded প্রসঙ্গে পর্ব ৫৬-এ।

Data center আর ফোন — দুই জায়গাতেই এখন বড় মাপকাঠি performance per watt: প্রতি watt-এ কত কাজ, অথবা সমার্থকভাবে প্রতি joule-এ কতগুলো কাজ।

Race-to-idle: দ্রুত শেষ করে ঘুমিয়ে পড়া

অবাক করা একটি কৌশল হলো race-to-idle: বেশি power-এ কাজটি দ্রুত শেষ করে chip-কে গভীর ঘুমে (খুব কম power) পাঠানো। কারণ chip চালু থাকলেই memory, clock আর leakage (transistor বন্ধ থাকলেও চুইয়ে যাওয়া current)-এর একটি স্থির খরচ চলতে থাকে — দ্রুত শেষ করলে সেই স্থির খরচ কম সময় ধরে দিতে হয়। একটি ১০ সেকেন্ডের সময়-জানালায় একই কাজের তিনটি কাল্পনিক উদাহরণ:

কৌশলকাজের সময়বাকি সময় ঘুম (১ W)মোট energy
ধীরে চালানো, ১৮ W10 s → 180 J—180 J
দ্রুত, ৩০ W5 s → 150 J5 s → 5 J155 J (জিতল)
দ্রুত কিন্তু খুব গরম, ৪০ W5 s → 200 J5 s → 5 J205 J (হারল)

অর্থাৎ race-to-idle সবসময় জেতে না — দ্রুত মোডে power কতটা বাড়ে আর ঘুমের অবস্থা কতটা গভীর, তার ওপর নির্ভর করে। বাস্তবে OS আর firmware এই সিদ্ধান্ত নেয় DVFS (voltage-frequency বদলানো) দিয়ে, আর সঠিক কৌশল chip ও কাজ ভেদে আলাদা — তাই মেপে দেখাই একমাত্র নিশ্চিত উপায়।

৯ · আসল প্রোগ্রাম মাপা — wall time, CPU time, time আর performance counter

তত্ত্ব যথেষ্ট, এবার নিজের হাতে মাপি। C-তে দুটি ঘড়ি আছে: clock_gettime(CLOCK_MONOTONIC) দেয় wall-clock সময় (দেয়ালের ঘড়ি, যা কখনো পেছনে যায় না), আর clock() দেয় এই process-এর ব্যবহার করা CPU time. নিচের প্রোগ্রাম প্রথমে একটি হিসাবের loop চালায়, তারপর ০.৩ সেকেন্ড ঘুমায় — দুটি ঘড়ি দুই ক্ষেত্রে কী বলে দেখুন:

#define _POSIX_C_SOURCE 199309L
#include <stdio.h>
#include <stdint.h>
#include <time.h>

static double wall(void) {                       /* wall-clock seconds */
    struct timespec t;
    clock_gettime(CLOCK_MONOTONIC, &t);
    return (double)t.tv_sec + (double)t.tv_nsec / 1e9;
}
static double cpu(void) { return (double)clock() / CLOCKS_PER_SEC; }  /* CPU seconds */

int main(void) {
    const uint64_t N = 200000000u;               /* 2 x 10^8 iterations */
    volatile uint64_t x = 1;                     /* volatile: loop is not optimized away */
    double w0 = wall(), c0 = cpu();
    for (uint64_t i = 0; i < N; i++)
        x = x * 6364136223846793005u + 1442695040888963407u;
    double w1 = wall(), c1 = cpu();
    struct timespec nap = {0, 300000000L};       /* sleep 0.3 s: waiting, not computing */
    nanosleep(&nap, NULL);
    double w2 = wall(), c2 = cpu();
    printf("loop : wall %.2f s, cpu %.2f s\n", w1 - w0, c1 - c0);
    printf("sleep: wall %.2f s, cpu %.2f s\n", w2 - w1, c2 - c1);
    printf("about %.1f ns per iteration (varies per run and machine)\n", (w1 - w0) / (double)N * 1e9);
    printf("check value: %llu\n", (unsigned long long)(x % 1000u));
    return 0;
}

একটি run-এর আউটপুট (একটি shared virtual machine-এ, x86-64; সময়গুলো প্রতি run আর প্রতি machine-এ বদলাবে, শুধু শেষ লাইনটি সবসময় একই):

loop : wall 0.85 s, cpu 0.38 s
sleep: wall 0.31 s, cpu 0.00 s
about 4.2 ns per iteration (varies per run and machine)
check value: 465

loop লাইনে wall ০.৮৫ s কিন্তু cpu মাত্র ০.৩৮ s. নিজের ফাঁকা কম্পিউটারে একটি হিসাবের loop-এ এ দুটি প্রায় সমান আসে; এখানে wall দ্বিগুণেরও বেশি, কারণ runner একটি ভাগাভাগি করা সার্ভার — প্রোগ্রাম প্রস্তুত ছিল, কিন্তু অর্ধেকের বেশি সময় CPU পায়নি (অন্য কাজের পালা বা CPU-সীমা)। ঠিক এই পার্থক্য দেখাতেই দুটি ঘড়ি আলাদা। sleep লাইনে wall ০.৩১ s, cpu ০.০০ s — অপেক্ষায় ঘড়ি চলে, CPU খরচ হয় না। “ns per iteration” wall time দিয়ে হিসাব করা (০.৮৫ s / ২ × ১০৮ ≈ ৪.২ ns); CPU time দিয়ে নিলে ≈ ১.৯ ns. volatile থাকায় প্রতিটি iteration-এ মেমরি থেকে পড়া, গুণ, যোগ আর আবার লেখা হয় — মাত্র কয়েক cycle-এর কাজ। শেষ লাইনের 465 সবসময় একই, কারণ হিসাবটি নির্ধারিত; বাকি সব সংখ্যা প্রতি run-এ বদলাবে। আপনার নিজের কম্পিউটারে -O2 দিয়ে চালালে আরও ভিন্ন ফল পাবেন — compiler বদলালে IC আর CPI বদলায়।

Shell-এর time: real, user, sys

যেকোনো কমান্ডের আগে time লিখলে তিনটি সংখ্যা পাবেন: real = wall-clock সময়, user = প্রোগ্রামের নিজের কোডে CPU time, sys = প্রোগ্রামের হয়ে kernel-এর কাজে CPU time. নিয়মগুলো এই:

  • একটি thread, পুরোটা হিসাব: real ≈ user + sys.
  • অপেক্ষা (sleep, disk, নেটওয়ার্ক) থাকলে: real > user + sys.
  • কয়েকটি core একসাথে কাজ করলে: user + sys > real হতে পারে — কারণ CPU time সব core-এর যোগফল।
# bash 'time' keyword: real = wall clock, user/sys = CPU time used by the commands
TIMEFORMAT='   real %3Rs   user %3Us   sys %3Ss'
work() { local s=0 i; for ((i = 0; i < 200000; i++)); do s=$((s + i)); done; }
echo "cores available: $(nproc 2>/dev/null || echo unknown)"
echo "1) CPU-bound bash loop (200000 additions):"
{ time work; } 2>&1
echo "2) sleep 0.5 (only waiting):"
{ time sleep 0.5; } 2>&1
echo "3) the same loop twice, in parallel:"
{ time { work & work & wait; }; } 2>&1
if command -v gcc >/dev/null 2>&1; then
  printf 'int main(void){volatile unsigned long x=0;for(unsigned long i=0;i<300000000UL;i++)x+=i;return 0;}\n' > t.c
  if gcc -O1 t.c -o t 2>/dev/null; then
    echo "4) compiled C loop (3 x 10^8 additions):"
    { time ./t; } 2>&1
  else echo "4) gcc found but compile failed - skipped"; fi
else
  echo "4) gcc not available here - compiled test skipped"
fi

একটি run-এর আউটপুট (সংখ্যাগুলো প্রতিবার কিছুটা বদলাবে):

cores available: 2
1) CPU-bound bash loop (200000 additions):
   real 3.921s   user 1.058s   sys 0.017s
2) sleep 0.5 (only waiting):
   real 0.523s   user 0.002s   sys 0.002s
3) the same loop twice, in parallel:
   real 7.198s   user 2.421s   sys 0.483s
4) compiled C loop (3 x 10^8 additions):
   real 1.288s   user 0.425s   sys 0.008s

২) নিয়মমতো: sleep-এ real ≈ ০.৫ s, কিন্তু user আর sys প্রায় শূন্য। ১) নিজের ফাঁকা কম্পিউটারে real ≈ user + sys আসার কথা; এখানে real (৩.৯ s) user-এর (১.০৬ s) প্রায় চার গুণ — runner-এর ভাগাভাগি করা container প্রোগ্রামকে পুরো CPU দেয়নি, তাই বেশিরভাগ সময় অপেক্ষায় গেছে। ৩) দুটি loop একসাথে: user (২.৪২ s) প্রায় দ্বিগুণ হয়েছে, কারণ মোট কাজ দ্বিগুণ — CPU time সব core-এর যোগফল। nproc ২ বললেও real প্রায় দ্বিগুণ হয়েছে (৭.২ s), অর্থাৎ এই মুহূর্তে container দুটি পুরো core একসাথে পায়নি। ফাঁকা ২-core কম্পিউটারে real প্রায় ১)-এর সমান আর user তার দ্বিগুণ আসত। শিক্ষা: “কয়টি core আছে” আর “কয়টি core পাচ্ছি” এক নয় — মেপে দেখুন। ৪) লৌহসূত্রের IC নিজের চোখে: bash-এ প্রতিটি যোগে user time ≈ ১.০৫৮ / ২ × ১০৫ ≈ ৫.৩ µs, compiled C-তে ≈ ০.৪২৫ / ৩ × ১০৮ ≈ ১.৪ ns — প্রায় ৩৭০০ গুণ পার্থক্য। একই CPU, একই clock; পার্থক্য হলো interpreter একটি যোগের জন্য হাজার হাজার machine instruction চালায় (বিশাল IC), আর compiled কোড চালায় কয়েকটি। (আপনার run-এ সংখ্যাগুলো আলাদা আসবে, কিন্তু অনুপাতের মাত্রা একই রকম থাকবে।)

Performance counter: cycle আর instruction সরাসরি গোনা

আধুনিক CPU-র ভেতরে কিছু বিশেষ register আছে, যারা গোনে কতগুলো cycle গেল, কতগুলো instruction শেষ হলো, কতবার cache miss বা branch mispredict হলো — এদের বলে hardware performance counter. Linux-এ perf stat এগুলো পড়ে দেয়। এই পাতার runner-এ perf চালানো যায় না (container-এ counter পড়ার অনুমতি থাকে না), তাই নিচেরটি শুধু উদাহরণ — একটি কাল্পনিক ম্যাট্রিক্স-গুণের প্রোগ্রামে আউটপুট দেখতে মোটামুটি এমন:

# উদাহরণ (চালানো হয়নি) — সংখ্যাগুলো দেখানোর জন্য বানানো
$ perf stat -e cycles,instructions ./matmul

 Performance counter stats for './matmul':

     6,012,345,678      cycles
     9,018,518,517      instructions              #    1.50  insn per cycle

       2.004123456 seconds time elapsed
       1.998000000 seconds user
       0.004000000 seconds sys

এখান থেকে লৌহসূত্রের প্রতিটি গুণক পড়ে নেওয়া যায়: IPC (instructions per cycle) = ৯,০১৮,৫১৮,৫১৭ / ৬,০১২,৩৪৫,৬৭৮ = ১.৫০, তাই CPI = 1 / IPC ≈ 0.667; গড় clock ≈ ৬.০১ × ১০৯ cycle / ২.০০৪ s ≈ ৩.০ GHz. CPI ১-এর কম কেন? কারণ আধুনিক core প্রতি cycle-এ একাধিক instruction শেষ করতে পারে (superscalar, পর্ব ৩৯)। অপ্টিমাইজ করার পর আবার মাপলে বোঝা যায় উন্নতিটা IC কমিয়ে এসেছে, নাকি CPI কমিয়ে — অনুমানের দরকার হয় না।

আরও গভীরে: বাস্তব CPU-তে একসাথে মাত্র কয়েকটি programmable counter থাকে, তাই অনেক event একসাথে মাপতে perf সময় ভাগ করে (multiplexing) আর অনুমানে scale করে। Intel-এর প্রকৌশলীদের প্রস্তাবিত “Top-down Microarchitecture Analysis” পদ্ধতি এই counter দিয়ে প্রতিটি cycle-কে চার ভাগে ফেলে — কাজ হয়েছে (retiring), ভুল অনুমানে নষ্ট (bad speculation), front-end আটকে, back-end (মেমরি বা execution unit) আটকে — যাতে বোঝা যায় CPI কোথায় বাড়ছে। বড় ওয়েব সেবায় আবার গড় latency-র চেয়ে “tail latency” (যেমন সবচেয়ে ধীর ১% অনুরোধ) বেশি গুরুত্ব পায়, কারণ একটি পাতা বানাতে শত শত সার্ভারের উত্তর লাগলে সবচেয়ে ধীরটিই পুরো পাতাকে আটকে রাখে।

OS-এর দিক থেকে top, vmstat, perf দিয়ে পুরো সিস্টেমের তদন্ত কীভাবে করা হয়, তা বিস্তারিত আছে Operating Systems কোর্সের পারফরম্যান্স আর observability পর্বে।

পরীক্ষা ও ইন্টারভিউতে — কয়েকটি প্রচলিত ধরনের প্রশ্ন আর এক লাইনের উত্তর:

  • লৌহসূত্র লিখুন ও প্রতিটি গুণক কে প্রভাবিত করে বলুন। CPU time = IC × CPI × Tc; algorithm/ভাষা/compiler → IC ও CPI, ISA → তিনটিই, microarchitecture → CPI ও Tc, technology → Tc.
  • বেশি GHz-এর CPU কি সবসময় দ্রুত? না — CPI আর IC-ও দেখতে হবে; ভিন্ন microarchitecture-এ কম GHz-এর CPU বেশি দ্রুত হতে পারে।
  • Amdahl-এর সূত্র প্রমাণ করুন; ৯৫% সমান্তরাল প্রোগ্রামের সর্বোচ্চ speedup কত? নতুন সময় (১ − f) + f/s থেকে; সর্বোচ্চ ১ / ০.০৫ = ২০.
  • MIPS কেন ভালো মাপকাঠি নয়? IC বাদ দেয়, ISA ও instruction mix ভেদে বদলায়, এমনকি পারফরম্যান্সের উল্টো দিকে যেতে পারে।
  • time-এ user, real-এর চেয়ে বেশি এলে মানে কী? প্রোগ্রাম একাধিক core-এ একসাথে চলেছে; CPU time সব core-এর যোগফল।

১০ · AI-এর যুগে — পারফরম্যান্সের হিসাব AI দিয়ে, যাচাই নিজে

“এই দুই CPU-র মধ্যে কোনটি দ্রুত?” বা “CPI কত হবে?” — এমন প্রশ্নে AI খুব দ্রুত উত্তর দেয়, আর প্রায়ই আত্মবিশ্বাসের সাথে ভুল দেয়। এই পর্বের বিষয়ে AI-এর সাধারণ ভুলগুলো:

  • একক গুলিয়ে ফেলা — ps, ns, GHz, MHz মেলাতে গিয়ে ১০৩ গুণ ভুল; “৫০% দ্রুত” আর “৫০% কম সময়” গুলিয়ে ফেলা।
  • Instruction mix-এ ভগ্নাংশের যোগফল ১ কি না না দেখা, বা IC বদলেছে অথচ শুধু CPI তুলনা করা।
  • Amdahl-এ f আর s উল্টে বসানো, বা “৮০% অংশ ৪ গুণ দ্রুত” থেকে সরাসরি “৩.২ গুণ” (০.৮ × ৪) বলে দেওয়া।
  • MIPS বা GHz দিয়ে ভিন্ন ISA-র CPU তুলনা করা; চিপের spec (cache, clock, core সংখ্যা) বানিয়ে বলা।
  • Benchmark স্কোরের উৎস, সংস্করণ বা পরীক্ষার শর্ত না বলে সংখ্যা দেওয়া।

AI-কে শিক্ষক হিসেবে ব্যবহার করুন, উত্তরদাতা হিসেবে নয় — সূত্র আর প্রতিটি ধাপ চেয়ে নিন, যাতে মেলানো যায়:

আমি কম্পিউটার আর্কিটেকচার শিখছি। নিচের সমস্যাটি সমাধান করো, কিন্তু উত্তরের আগে: ১. কোন সূত্র ব্যবহার করছ (CPU time = IC × CPI / clock rate, বা Amdahl-এর সূত্র) সেটা লেখো। ২. প্রতিটি সংখ্যা এককসহ বসাও (ps/ns/GHz), আর প্রতিটি ধাপে মধ্যবর্তী ফল দেখাও। ৩. instruction mix থাকলে ভগ্নাংশগুলোর যোগফল ১ কি না যাচাই করো। ৪. শেষে বলো "কোনটি কতগুণ দ্রুত" — কোন দিক থেকে বলছ তা স্পষ্ট করে। ৫. শেষে একটি ছোট JavaScript বা Python স্ক্রিপ্ট দাও, যা দিয়ে আমি নিজে উত্তরটি মিলিয়ে নিতে পারি। সমস্যা: [এখানে আপনার প্রশ্ন লিখুন]

AI-এর দেওয়া পারফরম্যান্স-হিসাব যাচাইয়ের চেকলিস্ট

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

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

প্র ০১“Megahertz myth” কী ছিল? বেশি GHz-এর প্রসেসর কেন কম GHz-এর প্রসেসরের কাছে হারতে পারে?

২০০০-এর দশকের শুরুতে কম্পিউটারের দোকানে প্রসেসরের পরিচয় ছিল প্রায় একটিই সংখ্যা — কত MHz বা GHz. Intel ২০০০ সালে Pentium 4 আনে, যার NetBurst microarchitecture খুব লম্বা pipeline দিয়ে নকশা করা হয়েছিল, যাতে প্রতিটি ধাপ ছোট হয় আর clock অনেক বাড়ানো যায়। কয়েক বছরের মধ্যে এটি ৩ GHz ছাড়িয়ে যায়। কিন্তু লম্বা pipeline-এর দাম আছে: branch ভুল অনুমান হলে অনেক ধাপের কাজ ফেলে দিতে হয়, আর প্রতি cycle-এ গড়ে কম instruction শেষ হয় — অর্থাৎ CPI বেশি।

একই সময়ে AMD-এর Athlon কম clock-এ চলত, কিন্তু প্রতি cycle-এ বেশি কাজ করত। লৌহসূত্রের ভাষায়: time = IC × CPI / f — f বড় হলেও CPI যদি আনুপাতিক হারে বড় হয়, লাভ নেই। AMD তখন প্রসেসরের নামে সরাসরি GHz না লিখে “Athlon XP 2000+” ধরনের model number ব্যবহার শুরু করে, যা বোঝাত “তুলনামূলক পারফরম্যান্স”। Apple-ও তখন তাদের PowerPC-ভিত্তিক Mac-এর পক্ষে “Megahertz Myth” নামে প্রকাশ্য উপস্থাপনা করেছিল বলে জানা যায়।

শেষ পর্যন্ত NetBurst-এর পথ power wall-এ ধাক্কা খায় — বেশি clock মানে বেশি power আর তাপ (পর্ব ০২)। Intel পরিকল্পিত আরও উঁচু clock-এর নকশা বাতিল করে, আর ২০০৬ সালে Core microarchitecture আনে, যার clock কম কিন্তু প্রতি cycle-এ কাজ অনেক বেশি। তারপর থেকে শিল্পজগৎ “IPC × frequency”-র ভারসাম্যের দিকে ফিরে আসে, আর বিজ্ঞাপনে core সংখ্যা ও প্রজন্ম গুরুত্ব পায়।

আজও একই ফাঁদ নতুন রূপে আসে — ফোনের বিজ্ঞাপনে “৮ core” বা “৩ GHz”, অথচ core-গুলো ভিন্ন আকারের (বড়-ছোট) আর প্রতি cycle-এর কাজ ভিন্ন। আরও একটি বিষয়: কোনো প্রসেসর হয়তো কয়েক সেকেন্ডের জন্য উঁচু “boost” clock-এ ওঠে, তারপর গরম হয়ে নেমে যায় — তাই ছোট benchmark আর লম্বা কাজের ফল আলাদা আসে।

মূল উপলব্ধি: পারফরম্যান্স তিন গুণকের গুণফল; একটি গুণক বাড়াতে গিয়ে আরেকটি খারাপ হলে লাভ শূন্য বা ঋণাত্মক হতে পারে। কোনো একক সংখ্যা দেখে নয়, আসল কাজের সময় দেখে বিচার করুন।

প্র ০২Benchmark-এর ফল এক সংখ্যায় আনতে কোন গড় নেওয়া উচিত — arithmetic, harmonic, না geometric? বিতর্কটা কী?

এই প্রশ্ন নিয়ে ১৯৮০-এর দশকে গবেষকদের মধ্যে রীতিমতো বিতর্ক হয়েছে। ১৯৮৬ সালে Philip Fleming আর John Wallace “How not to lie with statistics: the correct way to summarize benchmark results” নামের একটি প্রবন্ধে দেখান — যখন ফলগুলো কোনো reference machine-এর সাপেক্ষে normalized অনুপাত, তখন একমাত্র geometric mean-ই সুসংগত রায় দেয়, কারণ reference বদলালেও দুই machine-এর আপেক্ষিক অবস্থান বদলায় না। এই পর্বের টেবিলে আপনি ঠিক সেটিই দেখেছেন: arithmetic mean-এ reference বদলাতেই X আর Y-এর রায় উল্টে গেল।

অন্যদিকে James Smith ১৯৮৮ সালে যুক্তি দেন, geometric mean-এর একটি বড় দুর্বলতা আছে — এটি কোনো বাস্তব “মোট সময়”-এর সাথে মেলে না। আপনার কাজের মিশ্রণে যদি P1 প্রতিদিন একবার আর P2 প্রতিদিন একশোবার চলে, তাহলে আসল প্রশ্ন “দিনের মোট সময় কত”, আর সেটির উত্তর দেয় সময়ের (ভারযুক্ত) arithmetic mean. আর যখন ফলগুলো হার (যেমন MFLOPS — কাজ/সময়), তখন সমান কাজের জন্য সঠিক গড় হলো harmonic mean, কারণ এটি মোট কাজ ÷ মোট সময়ের সমান হয়।

SPEC শেষ পর্যন্ত geometric mean বেছে নেয় একটি ব্যবহারিক কারণে: সারা বিশ্বের ক্রেতার কাজের মিশ্রণ আলাদা, তাই কোনো একটি “ভার” সবার জন্য সঠিক নয়। ভারহীন অবস্থায় geometric mean অন্তত reference-নিরপেক্ষ, আর একটি প্রোগ্রামে বিশাল উন্নতি (যেমন compiler-এর বিশেষ কৌশলে একটি প্রোগ্রাম ১০০ গুণ দ্রুত) পুরো স্কোরকে arithmetic mean-এর মতো অতিরিক্ত টেনে তোলে না। তবুও SPEC প্রতিটি প্রোগ্রামের আলাদা অনুপাতও প্রকাশ করতে বলে, যাতে পাঠক নিজের প্রয়োজনের প্রোগ্রামগুলো আলাদা করে দেখতে পারেন।

ইতিহাসের আরেকটি শিক্ষা: benchmark যত জনপ্রিয় হয়, তত তাকে “খেলিয়ে” স্কোর বাড়ানোর চেষ্টা হয়। এজন্য SPEC নিয়মিত পুরোনো প্রোগ্রাম বাদ দিয়ে নতুন সংস্করণ আনে (CPU89, 92, 95, 2000, 2006, 2017), আর ফল প্রকাশের নিয়ম কড়া করে।

মূল উপলব্ধি: কোন গড় ঠিক, তা নির্ভর করে আপনি কী জিজ্ঞেস করছেন — normalized অনুপাতের সারাংশ (geometric), জানা কাজের মোট সময় (ভারযুক্ত arithmetic), না সমান কাজের হার (harmonic). এক সংখ্যার পেছনের প্রশ্নটি সবসময় খুঁজুন।

প্র ০৩Amdahl বলছেন সমান্তরালতার সীমা আছে, অথচ আজ লাখো core-এর সুপারকম্পিউটার আর হাজার core-এর GPU সফল। এই দুই সত্য একসাথে কীভাবে টেকে?

Gene Amdahl ১৯৬৭ সালের একটি কম্পিউটার সম্মেলনে (AFIPS) একটি ছোট প্রবন্ধে যুক্তি দেন যে অনেক প্রসেসর জুড়ে দিলেই সমস্যার সমাধান হবে না — প্রতিটি প্রোগ্রামে কিছু অংশ থাকে যা ধারাবাহিকভাবেই চলতে হয় (ডেটা ভাগ করা, ফল জোড়া দেওয়া, হিসাব রাখা), আর সেই অংশটি speedup-এর ছাদ ঠিক করে দেয়। (প্রবন্ধে সূত্রটি আজকের রূপে সরাসরি লেখা ছিল না; পরে তাঁর যুক্তি থেকে গঠন করা হয়।) তাঁর উদ্দেশ্য ছিল একক দ্রুত প্রসেসরের পক্ষে যুক্তি দেওয়া। গাণিতিকভাবে যুক্তিটি নিখুঁত: সমস্যার আকার স্থির থাকলে ৫% serial অংশ মানে ২০ গুণের বেশি কখনো নয়।

১৯৮৮ সালে Sandia National Laboratories-এ John Gustafson ও তাঁর সহকর্মীরা ১০২৪-প্রসেসরের একটি nCUBE machine-এ কয়েকটি বৈজ্ঞানিক প্রোগ্রামে প্রায় হাজার গুণের কাছাকাছি speedup দেখান। Gustafson-এর “Reevaluating Amdahl's Law” প্রবন্ধের মূল কথা: বেশি প্রসেসর পেলে বিজ্ঞানীরা একই ছোট সমস্যা দ্রুত শেষ করেন না, বরং বড় grid, বেশি কণা, সূক্ষ্মতর সময়-ধাপ নিয়ে একই সময়ে বড় সমস্যা চালান। তখন serial অংশ মোটামুটি একই থাকে, আর সমান্তরাল অংশ প্রসেসরের সাথে বাড়ে — তাই serial ভগ্নাংশ ক্রমশ ছোট হয়ে যায়।

দুটি সূত্র তাই দুটি ভিন্ন প্রশ্নের উত্তর। আপনার ফোনে একটি অ্যাপ খুলতে কত সময় লাগবে — এটি স্থির-আকারের কাজ, এখানে Amdahl রাজা; এজন্যই ফোন ও ল্যাপটপে কয়েকটি শক্তিশালী “বড়” core রাখা হয়, শুধু অনেক ছোট core নয়। অন্যদিকে AI মডেলের training, আবহাওয়ার পূর্বাভাস বা ভিডিও render — কাজ যত বড় করা যায়, ততই বেশি core কাজে লাগে; এখানে Gustafson-এর দৃষ্টিভঙ্গি খাটে, আর GPU-র হাজার core সার্থক।

তবে বাস্তবে আরেকটি খরচ দুটি সূত্রেই অনুপস্থিত — যোগাযোগ আর সমন্বয়ের খরচ, যা core বাড়লে বাড়ে। হাজার core-এর মধ্যে ডেটা আদান-প্রদান, lock-এর অপেক্ষা, cache coherence-এর বার্তা (পর্ব ৫৩) — এগুলোর কারণে অনেক প্রোগ্রামে নির্দিষ্ট সংখ্যক core-এর পর speedup বাড়া তো দূরের কথা, কমতে শুরু করে।

মূল উপলব্ধি: সমান্তরালতার মূল্য নির্ভর করে কাজটি স্থির, না বাড়ানো যায় তার ওপর। নকশার আগে জিজ্ঞেস করুন — “আমার serial অংশ কত, আর সমস্যাটি core-এর সাথে বড় হবে কি না?”

প্র ০৪MIPS-এর দুর্বলতা জানা সত্ত্বেও embedded জগতে আজও “DMIPS” কেন দেখা যায়? Synthetic benchmark-এর ইতিহাস থেকে কী শেখার আছে?

১৯৭০-৮০-এর দশকে DEC-এর VAX-11/780 ছিল জনপ্রিয় একটি mini-computer, আর অনেকে তাকে “১ MIPS machine” ধরে অন্য কম্পিউটারের গতি তার সাপেক্ষে বলতেন — অর্থাৎ “VAX MIPS” আসলে instruction গোনা নয়, একটি আপেক্ষিক অনুপাত ছিল। ১৯৮৪ সালে Reinhold Weicker তৈরি করেন Dhrystone — একটি ছোট কৃত্রিম (synthetic) প্রোগ্রাম, যাতে সাধারণ প্রোগ্রামের মতো string কপি, function call আর integer হিসাবের মিশ্রণ রাখা হয়েছিল। VAX-11/780-এ এটি প্রতি সেকেন্ডে যতবার চলত, সেটিকে “১ DMIPS” ধরা হলো। আজও অনেক microcontroller-এর datasheet-এ “DMIPS/MHz” লেখা থাকে।

কেন টিকে আছে? কারণ এটি ছোট, যেকোনো ছোট chip-এ চালানো যায়, আর বহু দশকের তুলনার ইতিহাস আছে। কিন্তু সমস্যাগুলো গুরুতর: প্রোগ্রামটি এত ছোট যে পুরোটা cache-এ ঢুকে যায়, তাই বাস্তব মেমরি আচরণ ধরা পড়ে না; আর compiler সহজেই এর অংশবিশেষ হিসাব আগেই করে ফেলতে বা বাদ দিতে পারে — ফলে স্কোর অনেকটাই compiler-এর দক্ষতা মাপে, প্রসেসরের নয়। এর আগের Whetstone (floating point-এর জন্য) নিয়েও একই অভিযোগ ছিল।

এই সমস্যার জবাবে embedded শিল্পের consortium EEMBC ২০০৯ সালের দিকে CoreMark প্রকাশ করে — এটিও ছোট, কিন্তু এমনভাবে লেখা যাতে compiler ফল আগেই হিসাব করে ফেলতে না পারে, আর রিপোর্টের সাথে compiler ও flag জানাতে হয়। তবুও এটি একটি ছোট kernel; বাস্তব firmware-এর interrupt, peripheral আর মেমরি আচরণ এতে নেই।

বড় শিক্ষাটি হলো Goodhart-এর সূত্রের মতো: কোনো মাপকাঠি যখন লক্ষ্য হয়ে যায়, তখন সেটি আর ভালো মাপকাঠি থাকে না। Compiler আর hardware নকশাকারীরা জনপ্রিয় benchmark-এর জন্য অপ্টিমাইজ করেন — যা কখনো বাস্তব প্রোগ্রামকেও সাহায্য করে, কখনো শুধু স্কোর বাড়ায়।

মূল উপলব্ধি: Synthetic benchmark তুলনার একটি সূচনা-বিন্দু মাত্র; সিদ্ধান্ত নেওয়ার আগে আপনার আসল কাজের মতো প্রোগ্রাম, আপনার compiler আর আপনার ডেটা দিয়ে মাপুন।

প্র ০৫আজকের ডেটা সেন্টার আর ফোনে “performance per watt” কেন গতির চেয়েও বড় মাপকাঠি হয়ে উঠল?

প্রায় তিন দশক ধরে প্রতিটি নতুন transistor-প্রজন্মে আকার ছোট হলে voltage-ও কমানো যেত, তাই একই আকারের chip-এ power ঘনত্ব প্রায় একই থাকত — একে বলা হয় Dennard scaling (পর্ব ০২)। ২০০০-এর দশকের মাঝামাঝি এটি ভেঙে পড়ে: voltage আর তেমন কমানো যায় না, leakage বাড়ে। ফলে chip-এর পারফরম্যান্স এখন আর transistor-এর সংখ্যা দিয়ে সীমিত নয়, বরং তাপ বের করার ক্ষমতা দিয়ে সীমিত। নির্দিষ্ট power-সীমার মধ্যে যে নকশা বেশি কাজ করে, সে-ই দ্রুত — তাই performance per watt আসলে নতুন রূপে পারফরম্যান্সই।

ডেটা সেন্টারে বিদ্যুৎ আর ঠান্ডা রাখার খরচ hardware কেনার খরচের সাথে তুলনীয় হতে পারে বলে বড় কোম্পানিগুলো জানিয়েছে। তাই সার্ভারের CPU বাছাইয়ে প্রায়ই প্রশ্ন হয় “প্রতি watt-এ কতগুলো অনুরোধ?” — এজন্যই বিভিন্ন cloud কোম্পানি নিজেদের ARM-ভিত্তিক সার্ভার chip এনেছে, যেগুলোর মূল বিক্রয়-যুক্তি দক্ষতা। সুপারকম্পিউটারের জন্যও ২০০৭ সাল থেকে আলাদা Green500 তালিকা প্রকাশিত হয়, যা TOP500-এর machine-গুলোকে GFLOPS প্রতি watt দিয়ে সাজায়।

ফোনে সীমা আরও কড়া: পাখা নেই, ব্যাটারি ছোট, আর হাতে গরম লাগলে ব্যবহারকারী অস্বস্তি বোধ করেন। এজন্য ফোনের SoC-তে থাকে ভিন্ন আকারের core (বড় দ্রুত core আর ছোট দক্ষ core), আর OS কাজ বুঝে কোন core-এ চালাবে ঠিক করে। race-to-idle আর DVFS-এর মধ্যে বেছে নেওয়াও এই হিসাবের অংশ — এই পর্বের টেবিলে দেখেছেন, দ্রুত শেষ করা কখনো energy বাঁচায়, কখনো নষ্ট করে।

আরেকটি গভীর পরিণাম হলো “dark silicon”-এর ধারণা — একটি chip-এ এত transistor আছে যে সবগুলো একসাথে পূর্ণ গতিতে চালালে power-সীমা ছাড়িয়ে যাবে। তাই নকশাকারীরা বিশেষায়িত অংশ (ভিডিও encoder, AI accelerator) বসান, যা দরকারের সময় জ্বলে আর বাকি সময় বন্ধ থাকে; নির্দিষ্ট কাজে এগুলো সাধারণ core-এর চেয়ে অনেক কম energy-তে কাজ করে।

মূল উপলব্ধি: আজকের পারফরম্যান্স সমীকরণে একটি অদৃশ্য শর্ত আছে — “নির্দিষ্ট power-এর মধ্যে”। তাই প্রশ্ন শুধু “কত দ্রুত?” নয়, “প্রতি joule-এ কত কাজ?”

অনুশীলন

পরীক্ষার ধাঁচের সমস্যা — কাগজ-কলমে আগে নিজে করুন, তারপর পূর্ণ সমাধান মিলিয়ে নিন।

  1. দুই machine, একই ISA: একটি প্রোগ্রামে ৬ × ১০৯ instruction. Machine P: ৩ GHz, CPI ১.৫. Machine Q: ২.৫ GHz, CPI ১.০. (ক) প্রতিটিতে CPU time কত? (খ) কোনটি কতগুণ দ্রুত? (গ) P-কে Q-এর সমান দ্রুত করতে (CPI একই রেখে) clock কত লাগবে?

    (ক) TimeP = IC × CPI / f = ৬ × ১০৯ × ১.৫ / (৩ × ১০৯) = ৯ / ৩ = ৩.০ s. TimeQ = ৬ × ১০৯ × ১.০ / (২.৫ × ১০৯) = ২.৪ s.

    (খ) TimeP / TimeQ = ৩.০ / ২.৪ = ১.২৫ — Q, P-এর চেয়ে ১.২৫ গুণ দ্রুত, যদিও Q-এর clock কম।

    (গ) দরকার ২.৪ = ৬ × ১০৯ × ১.৫ / f, তাই f = ৯ × ১০৯ / ২.৪ = ৩.৭৫ GHz.

  2. Instruction mix আর নকশার সিদ্ধান্ত: একটি ২ GHz প্রসেসরে ALU (CPI ১), Load/Store (CPI ৩), Branch (CPI ২)। একটি প্রোগ্রামে ১০৯ instruction — ALU ৬০%, Load/Store ৩০%, Branch ১০%. (ক) CPI, CPU time আর MIPS বের করুন। (খ) দুটি বিকল্প: A — cache উন্নত করে Load/Store-এর CPI ৩ থেকে ২; B — clock ২ থেকে ২.২ GHz (CPI অপরিবর্তিত)। কোনটি ভালো, কত speedup?

    (ক) CPI = ০.৬ × ১ + ০.৩ × ৩ + ০.১ × ২ = ০.৬ + ০.৯ + ০.২ = ১.৭. Time = ১০৯ × ১.৭ / (২ × ১০৯) = ০.৮৫ s. MIPS = clock / (CPI × ১০৬) = ২০০০ / ১.৭ ≈ ১১৭৬.৫.

    (খ) বিকল্প A: CPI = ০.৬ + ০.৩ × ২ + ০.২ = ১.৪, time = ১.৪ × ১০৯ / (২ × ১০৯) = ০.৭ s, speedup = ০.৮৫ / ০.৭ ≈ ১.২১৪. বিকল্প B: time = ১.৭ × ১০৯ / (২.২ × ১০৯) ≈ ০.৭৭৩ s, speedup = ২.২ / ২ = ১.১. তাই A ভালো। কারণ Load/Store মোট সময়ের ০.৯ / ১.৭ ≈ ৫৩% নেয় — সবচেয়ে বড় অংশে হাত দেওয়াই লাভজনক। (A-তে MIPS ≈ ১৪২৮.৬, B-তে ≈ ১২৯৪.১ — এখানে একই ISA ও একই প্রোগ্রাম বলে MIPS-ও একই রায় দেয়।)

  3. Amdahl-এর সূত্র উল্টো দিক থেকে: (ক) একটি প্রোগ্রাম ১০০ s চলে, যার ৮০ s যায় গুণের কাজে। গুণকে কতগুণ দ্রুত করলে পুরো প্রোগ্রাম ৪ গুণ দ্রুত হবে? ৫ গুণ দ্রুত করা কি সম্ভব? (খ) একটি প্রোগ্রামের ১০% serial. কমপক্ষে কতগুলো core লাগবে ৮ গুণ speedup পেতে? ১০ গুণ কি সম্ভব?

    (ক) ৪ গুণ দ্রুত মানে নতুন সময় ১০০ / ৪ = ২৫ s. অপরিবর্তিত অংশ ২০ s থাকবেই, তাই ২৫ = ২০ + ৮০ / n → ৮০ / n = ৫ → n = ১৬. ৫ গুণের জন্য নতুন সময় ২০ s লাগবে: ২০ = ২০ + ৮০ / n → ৮০ / n = ০, যা কোনো সসীম n-এ হয় না — অসম্ভব (সীমা ১ / ০.২ = ৫, ছোঁয়া যায় না)।

    (খ) ১ / (০.১ + ০.৯ / N) = ৮ → ০.১ + ০.৯ / N = ০.১২৫ → ০.৯ / N = ০.০২৫ → N = ৩৬. ১০ গুণের জন্য ০.৯ / N = ০ লাগবে — অসীম core-এও সীমা ১ / ০.১ = ১০ শুধু ছোঁয়ার দিকে এগোয়, তাই কোনো সসীম N-এ সম্ভব নয়।

  4. আউটপুট অনুমান করুন: একটি single-thread প্রোগ্রাম প্রথমে ২ সেকেন্ড ঘুমায়, তারপর ১ সেকেন্ড হিসাব করে। আরেকটি প্রোগ্রাম ৪টি thread-এ মোট ৮ সেকেন্ডের CPU-কাজ সমানভাবে ভাগ করে চালায়, ৪টি ফাঁকা core-এ। দুটির জন্য time কমান্ড real, user, sys মোটামুটি কত দেখাবে? দ্বিতীয়টিতে ২টি core থাকলে কী বদলাবে?

    প্রথম প্রোগ্রাম: real ≈ ৩ s (ঘুম + হিসাব — ঘড়ি দুই সময়েই চলে), user ≈ ১ s (CPU শুধু হিসাবের সময় ব্যস্ত), sys ≈ ০ (প্রায় কোনো system call নেই)। এখানে real > user + sys, কারণ অপেক্ষা আছে।

    দ্বিতীয় প্রোগ্রাম, ৪ core: প্রতিটি thread ২ s কাজ করে, সব একসাথে — real ≈ ২ s, user ≈ ৮ s (চার core-এর CPU time-এর যোগফল), sys সামান্য (thread তৈরি)। এখানে user > real.

    ২ core হলে: একসাথে মাত্র ২টি thread চলতে পারে, তাই real ≈ ৮ / ২ = ৪ s; user এখনো ≈ ৮ s, কারণ মোট CPU-কাজ বদলায়নি। (বাস্তবে context switch-এর — এক thread থেকে আরেক thread-এ CPU বদলের — সামান্য বাড়তি খরচ যোগ হয়।) এটাই multicore-এর পারফরম্যান্স বোঝার মূল চাবি: CPU time মোট কাজ মাপে, wall time মাপে সমান্তরালভাবে কত দ্রুত শেষ হলো।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমInstruction mix থেকে দুই machine তুলনার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. প্রতিটি class-এর instruction সংখ্যা (বা ভগ্নাংশ) আর CPI লেখা
  2. প্রতিটি class-এর জন্য সংখ্যা × CPI গুণ করা
  3. সব যোগ করে মোট clock cycle পাওয়া
  4. মোট cycle-কে clock rate দিয়ে ভাগ করে CPU time পাওয়া
  5. দুই machine-এর সময়ের অনুপাত নিয়ে “কতগুণ দ্রুত” বলা

একটি প্রসেসরের clock rate 2.5 GHz. এর clock cycle time কত?

  • 2.5 ns
  • 0.4 ns
  • 4 ns
  • 40 ps

একটি প্রোগ্রামে 4 × 109 instruction, গড় CPI 1.25, প্রসেসরের clock 2 GHz. CPU time কত?

  • 2.0 s
  • 10 s
  • 2.5 s
  • 1.6 s

একটি প্রোগ্রামের 75% সময় এমন অংশে যায় যা যেকোনো পরিমাণ দ্রুত করা সম্ভব। Amdahl-এর সূত্র অনুযায়ী পুরো প্রোগ্রামের সর্বোচ্চ speedup কত?

  • 0.75
  • 1.33
  • অসীম
  • 4

একই প্রসেসরে একই প্রোগ্রামের জন্য compiler বদলালে CPU time-এর লৌহসূত্রের কোন গুণকটি সাধারণত বদলায় না?

  • Instruction count (IC)
  • CPI
  • Clock rate (বা clock cycle time)
  • তিনটিই বদলায়

SPEC CPU benchmark প্রতিটি প্রোগ্রামের SPECratio-র (reference time / measured time) geometric mean নেয়। এর প্রধান কারণ কী?

  • Reference machine বদলালেও দুই machine-এর আপেক্ষিক রায় বদলায় না
  • হিসাব করা সবচেয়ে সহজ
  • এটি সবসময় মোট execution time-এর সমান
  • এটি স্কোরকে সবসময় বড় দেখায়

Linux-এ একটি প্রোগ্রামের time আউটপুট: real 4.0 s, user 12.0 s, sys 0.3 s. এ থেকে সবচেয়ে যুক্তিসংগত সিদ্ধান্ত কোনটি?

  • প্রোগ্রামটি বেশিরভাগ সময় disk-এর জন্য অপেক্ষা করেছে
  • প্রোগ্রামটি একাধিক core-এ একসাথে চলেছে, গড়ে প্রায় ৩টি
  • প্রোগ্রামের বেশিরভাগ সময় kernel-এর ভেতরে গেছে
  • মাপ ভুল, কারণ user কখনো real-এর চেয়ে বেশি হতে পারে না

একটি 4 GHz machine-এ instruction class-এর CPI যথাক্রমে A = 1, B = 2, C = 3. Compiler X দেয় (A, B, C) = (5, 1, 1) × 109 instruction, Compiler Y দেয় (10, 1, 1) × 109. কোনটি সঠিক?

  • Y দ্রুত, কারণ তার MIPS বেশি
  • দুটির CPU time সমান
  • Y দ্রুত, কারণ তার CPI কম
  • X 1.5 গুণ দ্রুত, যদিও Y-এর MIPS বেশি

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. Performance = ১ / execution time; “X, Y-এর চেয়ে n গুণ দ্রুত” মানে Time_Y / Time_X = n. Latency আর throughput আলাদা প্রশ্ন।
  2. লৌহসূত্র: CPU time = IC × CPI × T_c = IC × CPI / f; weighted CPI = Σ CPI_i × F_i. একটি গুণক দেখে রায় নয়।
  3. MIPS IC বাদ দেয়, তাই ভিন্ন ISA বা compiler-এ ভুল রায় দিতে পারে; benchmark-এ geometric mean reference-নিরপেক্ষ।
  4. Amdahl: 1 / ((1 − f) + f/s), সীমা 1/(1 − f); multicore-এ serial অংশ ছাদ টানে, আর Gustafson বলেন বড় সমস্যায় scaled speedup ≈ N.
  5. Energy = power × time; মাপুন — wall বনাম CPU time, time-এর real/user/sys, আর performance counter থেকে IPC = ১/CPI.

পরের পর্বে: এতক্ষণ যে instruction আর cycle গুনলাম, কম্পিউটার সেগুলো রাখে 0 আর 1 দিয়ে। পরের পর্বে binary, octal আর hexadecimal — সংখ্যা লেখার পদ্ধতি আর এক পদ্ধতি থেকে আরেকটিতে রূপান্তর।

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

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান