সরাসরি পর্বে যান
পর্ব ৫৭ · ৬৪-এর মধ্যে · মডিউল ৯

পারফরম্যান্স আর observability — top, vmstat, perf আর তদন্তের পদ্ধতি

Performance & Observability — the USE and RED Methods, the 60-Second Checklist, Queueing, perf, Flame Graphs and eBPF

Taught in Bangla, this lesson teaches a method for performance and observability: the USE and RED methods, averages versus percentiles and histograms, queueing and why systems slow down near high utilization, reading CPU, memory, disk and network numbers, a ten-command first-60-seconds checklist, strace, ltrace, perf and hardware counters, flame graphs and eBPF tools, and a worked case study of a slow e-commerce API.

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

  • পারফরম্যান্স-তদন্তের পদ্ধতি — anti-method চেনা, baseline রাখা, USE আর RED ধাপে ধাপে প্রয়োগ
  • মাপের ভাষা — latency বনাম throughput, utilization বনাম saturation, গড় বনাম percentile বনাম histogram
  • Queueing-এর অঙ্ক — Little's law আর M/M/1 দিয়ে হাতে হিসাব, আর কেন ৯০% ব্যবহারে সিস্টেম হঠাৎ ধীর হয়
  • CPU, মেমরি, ডিস্ক আর নেটওয়ার্কের প্রতিটি সংখ্যার মানে — আর Linux, Windows ও macOS-এর টুলবক্স
  • perf, hardware counter, flame graph আর eBPF — আর একটি ধীর e-commerce API-র কারণ USE দিয়ে খুঁজে বের করা

১ · “সাইট স্লো” — অনুমান নয়, পদ্ধতি

রাত দশটা। ডেভেলপার রিয়ার ফোনে বার্তা: “চেকআউট পাতা খুলতে ১০ সেকেন্ড লাগছে!” এমন মুহূর্তে প্রায় সবাই যা মনে আসে তা-ই বদলান — সার্ভার রিস্টার্ট, cache বাড়ানো, “নিশ্চয়ই নেটওয়ার্কের দোষ।” কখনো সমস্যা চলে যায়, কিন্তু কেন গেল কেউ জানেন না, আর পরের সপ্তাহে সেটি ফিরে আসে।

এই পর্ব আপনাকে একটি অন্য অভ্যাস শেখাবে: পারফরম্যান্সকে তদন্ত হিসেবে দেখা। আর সেই তদন্তের জন্য OS যে জানালাগুলো খুলে রাখে — /proc, /sys, counter, tracepoint — সেগুলো পড়তে শেখা। একটি সিস্টেম বাইরে থেকে কতটা “পড়া যায়”, তার নাম observability.

জ্বর নিয়ে ডাক্তারের কাছে গেলে ভালো ডাক্তার প্রথমেই অ্যান্টিবায়োটিক লেখেন না। আগে জিজ্ঞেস করেন কবে থেকে, তারপর থার্মোমিটার, প্রেশার, স্টেথোস্কোপ — একটি নির্দিষ্ট ক্রমে। স্বাভাবিক মান মাথায় থাকে বলেই অস্বাভাবিকটা চোখে পড়ে; রক্ত পরীক্ষা বা এক্স-রে আসে পরে। সার্ভারেও তাই: আগে লক্ষণ, তারপর প্রতিটি সম্পদের দ্রুত পরীক্ষা, শেষে গভীর টুল।

যে পথে যাবেন না — তিনটি anti-method

পারফরম্যান্স-প্রকৌশলী Brendan Gregg তাঁর লেখা আর বইয়ে কিছু ভুল অভ্যাসের নাম দিয়েছেন — “anti-method”. নাম দেওয়াটা কাজের, কারণ নামওয়ালা ভুল চেনা সহজ।

  • Streetlight anti-method: পুরোনো কৌতুক — এক মাতাল রাতে ল্যাম্পপোস্টের নিচে চাবি খুঁজছেন। জিজ্ঞেস করা হলো, চাবি কি এখানেই হারিয়েছে? “না, পার্কে। কিন্তু আলো তো এখানে!” যে টুল আপনি চেনেন (ধরুন শুধু top), শুধু সেটি দিয়ে দেখলে যা সেই টুলে দেখা যায় না — ডিস্কের সারি, নেটওয়ার্কের retransmit — তা কখনো খুঁজে পাবেন না।
  • Random change anti-method: একটি সেটিং বদলান, দেখুন ভালো হলো কি না, না হলে আরেকটি। ফল: অনেক সময় নষ্ট, আর কিছু বদল যা আসলে ক্ষতি করছিল, সেগুলোও থেকে যায়, কারণ কেউ ফেরত দেননি।
  • Blame-someone-else: “নেটওয়ার্কের দোষ” — প্রমাণ ছাড়াই অন্য দলের দিকে ঠেলে দেওয়া।

যে পথে যাবেন — বৈজ্ঞানিক পদ্ধতি আর baseline

এর বদলে বিজ্ঞানের পরিচিত চক্রটি নিন: প্রশ্ন (কী ধীর, কার জন্য, কবে থেকে?) → অনুমান (hypothesis: “মেমরি কম পড়ায় swap হচ্ছে”) → ভবিষ্যদ্বাণী (“তাহলে vmstat-এ si/so শূন্য নয় হবে”) → পরীক্ষা (মাপুন) → বিশ্লেষণ (মিলল? না মিললে অনুমান বাদ)। একবারে একটিই পরিবর্তন করুন, আর পরিবর্তনের আগে-পরে একই সংখ্যা মাপুন। DBMS কোর্সের performance tuning পর্বে ডেটাবেসের জন্য ঠিক এই চক্রটি দেখেছেন; এখানে একই চিন্তা পুরো OS-এর জন্য।

সবচেয়ে অবহেলিত হাতিয়ার baseline — স্বাভাবিক দিনের সংখ্যা। load average ৬ কি বেশি? ৩২-CPU যন্ত্রে স্বাভাবিক, ২-CPU যন্ত্রে বিপদ। p99 ৩০০ ms খারাপ? গত মাসে সবসময় ২৮০–৩২০ থাকলে না। তাই নিয়মিত সংগ্রহ (sar-এর collector বা Prometheus-এর মতো monitoring) চালু রাখা নিজেই একটি পারফরম্যান্স-কাজ।

মূল ধারণা

Monitoring বলে “কিছু একটা খারাপ”; observability দেয় “কেন খারাপ” খোঁজার উপায়। এর জন্য চার ধরনের সংকেত লাগে: metric (সংখ্যা, সময়ের সাথে — CPU%, p99), log (ঘটনার লেখা — error, OOM), trace (একটি অনুরোধ কোন কোন অংশ ঘুরে গেল) আর profile (CPU-সময় কোন ফাংশনে গেল)। OS নিজে প্রথম তিনটির কাঁচামাল দেয়; profile আসে perf বা eBPF থেকে।

২ · USE আর RED — দুটি চেকলিস্ট, দুই দিক থেকে

বৈজ্ঞানিক পদ্ধতি সাধারণ; বাস্তবে শুরু করার জন্য একটি নির্দিষ্ট চেকলিস্ট লাগে। সবচেয়ে পরিচিত দুটি এখানে।

USE method — প্রতিটি সম্পদে তিনটি প্রশ্ন

Brendan Gregg ২০১২ সালের দিকে USE method প্রচার করেন। নিয়মটি ছোট: প্রতিটি সম্পদের জন্য তিনটি প্রশ্ন করুন।

  • U — Utilization: সম্পদটি সময়ের কত অংশ ব্যস্ত ছিল? (CPU ৭০% ব্যস্ত, ডিস্ক ৯৫% সময় কাজ করছিল)
  • S — Saturation: সম্পদটি যা সামলাতে পারে, তার বাড়তি কাজ কি সারিতে দাঁড়িয়ে আছে? (CPU-র অপেক্ষায় ১২টি thread, ডিস্কের সারিতে ৪০টি অনুরোধ)
  • E — Errors: কোনো error ঘটছে কি? (ডিস্কের I/O error, নেটওয়ার্কের প্যাকেট ফেলে দেওয়া, OOM kill)

শক্তিটা “প্রতিটি সম্পদ” কথাটায়: একটি তালিকা ধরে এগোলে (CPU, মেমরি, ডিস্ক, নেটওয়ার্ক, তারপর file descriptor, thread pool, lock) streetlight-এর ফাঁদে পড়েন না। কোথাও খারাপ পেলে সেখানে গভীরে যান; সব ঠিক থাকলে সমস্যা সম্ভবত সফটওয়্যারের ভেতরে (lock, অপেক্ষা, ভুল অ্যালগরিদম)।

সম্পদUtilizationSaturationErrors
CPUmpstat -P ALL 1: প্রতি CPU-র 100 − %idle; topvmstat 1-এর r > CPU-সংখ্যা; PSI /proc/pressure/cpuসাধারণত বিরল; dmesg-এ machine-check error
মেমরিfree -m: total − availablevmstat-এর si/so (swap), major fault, /proc/pressure/memorydmesg-এ OOM kill; /proc/vmstat-এর oom_kill
ডিস্কiostat -xz 1: %util (SSD-তে সাবধান), IOPS বনাম সীমাaqu-sz (গড় সারি), await বেড়ে যাওয়া, /proc/pressure/iodmesg-এ I/O error, smartctl
নেটওয়ার্কsar -n DEV 1: rx/tx kB/s বনাম লিংকের গতিsocket-এর সারি (ss -tn-এ Recv-Q/Send-Q), dropsar -n EDEV 1, ip -s link; retransmit (sar -n ETCP 1)

RED method আর golden signals — সার্ভিসের দিক থেকে

USE দেখে যন্ত্রের দিক থেকে। কিন্তু ব্যবহারকারী CPU দেখেন না, দেখেন তাঁর অনুরোধের উত্তর। সার্ভিসের দিক থেকে দেখার জন্য Tom Wilkie (Weaveworks-এ থাকাকালে, ২০১৫ সালের দিকে) প্রস্তাব করেন RED method: প্রতিটি সার্ভিসের Rate (সেকেন্ডে কত অনুরোধ), Errors (কতগুলো ব্যর্থ) আর Duration (কত সময় লাগছে, percentile-এ)। Google-এর Site Reliability Engineering বইয়ের (২০১৬) four golden signals — latency, traffic, errors, saturation — প্রায় একই চিন্তা, সাথে saturation যোগ করা।

বাস্তবে দুটি একসাথে চলে: RED বলে কোন সার্ভিস অসুস্থ, USE বলে কোন সম্পদের কারণে। একটি microservice-ব্যবস্থায় RED dashboard দেখে খুঁজে পান কোন সার্ভিসের p99 লাফিয়েছে; তারপর সেই সার্ভিসের যন্ত্রগুলোতে USE চালান।

৩ · মাপের ভাষা — গড়, percentile, histogram আর চারটি জোড়া শব্দ

ভুল সংখ্যা দেখে ঠিক সিদ্ধান্ত নেওয়া যায় না। পারফরম্যান্সের আলাপে চারটি জোড়া শব্দ প্রায়ই গুলিয়ে যায়।

⏱️ Latency বনাম throughput

Latency: একটি কাজে কত দেরি (ms). Throughput: একক সময়ে কতগুলো কাজ (অনুরোধ/s). একটি বাস বেশি যাত্রী নেয় (throughput), কিন্তু প্রতিটি স্টপে থামে (latency); মোটরসাইকেল উল্টো। batching throughput বাড়ায়, প্রায়ই latency-র দামে।

📊 Utilization বনাম saturation

Utilization: সম্পদ কতক্ষণ ব্যস্ত। Saturation: কত কাজ সারিতে অপেক্ষায়। ১০০% ব্যস্ত কিন্তু সারি শূন্য — সবচেয়ে দক্ষ অবস্থা। ৮০% ব্যস্ত কিন্তু মাঝে মাঝে লম্বা সারি — হঠাৎ-হঠাৎ ভিড়ের (burst) লক্ষণ।

📈 গড় বনাম percentile

৯০% অনুরোধ ২০ ms, ১০% অনুরোধ ২,০০০ ms হলে গড় ২১৮ ms — এমন একটি সংখ্যা যা কেউ পাননি। p50, p95, p99 বলে কতজন কতক্ষণ অপেক্ষা করলেন; tail latency (p99) বলে ধীরতম অভিজ্ঞতা।

🧮 Percentile বনাম histogram

একটি percentile একটি বিন্দু; histogram পুরো আকৃতি। দুই কুঁজওয়ালা বণ্টন (cache hit ১ ms, miss ৪০ ms) histogram-এ চোখে পড়ে, p50 বা গড়ে লুকায়। eBPF-এর biolatency ঠিক এই কারণে histogram ছাপে।

Percentile যোগ বা গড় করা যায় না। চারটি সার্ভারের p99 যথাক্রমে ১০০, ১২০, ৯০ আর ৯০০ ms হলে পুরো সার্ভিসের p99 এদের গড় (৩০২.৫ ms) নয়। সঠিক পথ হলো সব মাপ (বা তাদের histogram) একসাথে করে নতুন করে percentile হিসাব করা। অনেক monitoring dashboard ঠিক এই ভুলটি করে। ডেটাবেসের প্রসঙ্গে percentile-ভিত্তিক alerting DBMS কোর্সের performance tuning পর্বে বিস্তারিত আছে।

৪ · সারির অঙ্ক — কেন ৯০% ব্যবহারে সিস্টেম হঠাৎ ধীর

পারফরম্যান্সের সবচেয়ে গুরুত্বপূর্ণ অন্তর্দৃষ্টি একটি অঙ্ক থেকে আসে। পর্ব ১৬-এ (CPU scheduling-এর ভিত্তি) Little's law দেখেছেন: L = λ × W — সিস্টেমে গড়ে কতগুলো কাজ (L) = আগমন-হার (λ) × প্রতিটির গড় সময় (W). এটি যেকোনো স্থিতিশীল সারিতে খাটে, বণ্টন যা-ই হোক। কিন্তু এটি বলে না ব্যবহার বাড়লে W কীভাবে বদলায়। তার জন্য একটি queueing model লাগে।

একটি ব্যাংকের টোকেন-কাউন্টারে একজন ক্যাশিয়ার, প্রতিজনকে গড়ে ১ মিনিট লাগে। ঘণ্টায় ৩০ জন এলে ক্যাশিয়ার অর্ধেক সময় ব্যস্ত, লাইন প্রায় থাকেই না। ঘণ্টায় ৫৪ জন এলে (৯০%) ক্যাশিয়ার তো মাত্র ৮০% বেশি কাজ করছেন, কিন্তু লাইন হয়ে যায় অনেক লম্বা। কেন? কারণ মানুষ সমান ফাঁকে আসেন না — কখনো একসাথে তিনজন, কখনো পাঁচ মিনিট কেউ না। অলস মিনিটগুলো আর ফেরত আসে না, আর ভিড়ের মিনিটের জমা কাজ শোধ করার মতো ফাঁকা সময় ৯০%-এ খুব কম।

M/M/1 — সবচেয়ে সরল মডেল, সবচেয়ে বড় শিক্ষা

M/M/1 মানে: আগমন এলোমেলো (Poisson, গড় হার λ), সেবা-সময় এলোমেলো (exponential, গড় হার μ — service rate), একজন সেবাদাতা। ব্যবহার ρ = λ/μ, আর গড় সেবা-সময় S = 1/μ. মূল ফলগুলো:

L = ρ / (1 − ρ)  ·  R = 1 / (μ − λ) = S / (1 − ρ)
p-তম percentile = −ln(1 − p) / (μ − λ)

এক লাইনের যুক্তি (পরীক্ষায় কাজে লাগে): M/M/1-এ সিস্টেমে ঠিক n জন থাকার স্থিতিশীল সম্ভাবনা P(n) = (1 − ρ)ρⁿ — প্রতিটি অবস্থা থেকে ওপরে ওঠার হার λ আর নিচে নামার হার μ সমান হতে হয় (balance), তাই P(n+1) = ρ·P(n). এই geometric বণ্টনের গড় L = ρ/(1 − ρ). তারপর Little's law: R = L/λ = ρ/(λ(1 − ρ)) = 1/(μ − λ). আরও জানা ফল: FIFO M/M/1-এ একটি অনুরোধের মোট সময় নিজেই exponential, হার (μ − λ) — তাই P(R > t) = e−(μ−λ)t, আর p95 পেতে e−(μ−λ)t = 0.05 সমাধান করলে t = −ln(0.05)/(μ − λ) ≈ 3.0/(μ − λ). p99-এ গুণক −ln(0.01) ≈ 4.6.

হাতে করা উদাহরণ: μ = ১০০/s (S = ১০ ms). ρ = ০.৫ হলে λ = ৫০, μ − λ = ৫০ ⇒ R = ১/৫০ s = ২০ ms, p95 = ৩.০০/৫০ ≈ ৬০ ms. ρ = ০.৯ হলে μ − λ = ১০ ⇒ R = ১০০ ms, p99 = ৪.৬১/১০ ≈ ৪৬১ ms. ρ = ০.৯৯ হলে μ − λ = ১ ⇒ R = ১,০০০ ms — সেবা-সময়ের ১০০ গুণ। নিচের ক্যালকুলেটর একই সূত্রে পুরো টেবিল বানায়:

// M/M/1 queue: random (Poisson) arrivals at rate lambda, exponential service at rate mu, one server, FIFO.
// rho = lambda/mu;  mean number in system L = rho/(1-rho);  mean response time R = 1/(mu-lambda).
// In M/M/1 the response time is exponential with rate (mu-lambda), so its p-quantile = -ln(1-p)/(mu-lambda).
const mu = 100;                                     // service rate 100 requests/s -> service time S = 10 ms
const ms = x => (1000 * x).toFixed(1).padStart(8);
console.log('  rho  lambda/s  L(in sys)   mean R    p50 ms    p95 ms    p99 ms    R/S');
for (const rho of [0.5, 0.7, 0.8, 0.9, 0.95, 0.99]) {
  const lam = rho * mu, gap = mu - lam;
  const L = rho / (1 - rho), R = 1 / gap;
  const q = p => -Math.log(1 - p) / gap;            // percentile of an exponential distribution
  console.log(`${rho.toFixed(2).padStart(5)} ${lam.toFixed(0).padStart(8)} ${L.toFixed(2).padStart(10)} ` +
    `${ms(R)}  ${ms(q(0.50))}  ${ms(q(0.95))}  ${ms(q(0.99))} ${(R * mu).toFixed(1).padStart(6)}`);
}
const lam = 90, R = 1 / (mu - lam);                 // Little's law check at rho = 0.9
console.log(`Little: lambda x R = ${lam}/s x ${R.toFixed(3)} s = ${(lam * R).toFixed(1)} requests in the system`);
for (const rho of [0.5, 0.8, 0.9]) {                // the same +10% traffic at three starting loads
  const before = 1 / (mu - rho * mu), after = 1 / (mu - 1.1 * rho * mu);
  console.log(`+10% traffic at rho=${rho}: mean R ${ms(before).trim()} -> ${ms(after).trim()} ms (x${(after / before).toFixed(2)})`);
}

আউটপুট (হিসাবটি নির্ধারিত, তাই প্রতিবার একই; সময়গুলো ms-এ):

  rho  lambda/s  L(in sys)   mean R    p50 ms    p95 ms    p99 ms    R/S
 0.50       50       1.00     20.0      13.9      59.9      92.1    2.0
 0.70       70       2.33     33.3      23.1      99.9     153.5    3.3
 0.80       80       4.00     50.0      34.7     149.8     230.3    5.0
 0.90       90       9.00    100.0      69.3     299.6     460.5   10.0
 0.95       95      19.00    200.0     138.6     599.1     921.0   20.0
 0.99       99      99.00   1000.0     693.1    2995.7    4605.2  100.0
Little: lambda x R = 90/s x 0.100 s = 9.0 requests in the system
+10% traffic at rho=0.5: mean R 20.0 -> 22.2 ms (x1.11)
+10% traffic at rho=0.8: mean R 50.0 -> 83.3 ms (x1.67)
+10% traffic at rho=0.9: mean R 100.0 -> 1000.0 ms (x10.00)

টেবিলটি বাঁ থেকে ডানে পড়ুন। R/S কলাম হলো 1/(1−ρ): ৫০%-এ ২ গুণ, ৮০%-এ ৫, ৯০%-এ ১০, ৯৯%-এ ১০০ গুণ। L কলাম সারির দৈর্ঘ্য: ৯০%-এ গড়ে ৯টি অনুরোধ সিস্টেমে, আর Little-এর যাচাই — ৯০/s × ০.১ s = ৯ — মিলে যায়। p99 গড়ের প্রায় ৪.৬ গুণ প্রতিটি সারিতে: ৯০%-এ গড় ১০০ ms হলেও ১% অনুরোধ ৪৬০ ms-এর বেশি অপেক্ষা করে। শেষ তিন লাইন সবচেয়ে ব্যবহারিক: একই +১০% ট্রাফিক ৫০%-এ চলা সিস্টেমে গড় ১১% বাড়ায়, ৮০%-এ ৬৭%, আর ৯০%-এ চলা সিস্টেমে (৯৯%-এ ঠেলে দিয়ে) ১০ গুণ। তাই latency-সংবেদনশীল সম্পদ টানা ৯০%-এ চালানো হয় না; অনেকে ৬০–৭০%-এর আশেপাশে সীমা রাখেন, যদিও সঠিক সীমা latency-লক্ষ্য আর ট্রাফিকের অনিয়মের ওপর নির্ভর করে।

এবার ধাপে ধাপে দেখুন, একই কাউন্টারে ব্যবহার বাড়লে সারির চেহারা কেমন বদলায়। প্রতিটি ধাপের সংখ্যা ওপরের সূত্র থেকে হিসাব করা (μ = ১০০/s, S = ১০ ms):

M/M/1, μ = 100/s (S = 10 ms)
ব্যবহার ρ
আগমন λ
সিস্টেমে গড়ে (L)
গড় সময় R
p99
সারির ছবি
  1. ρ = ৫০% — আরাম। সেকেন্ডে ৫০টি অনুরোধ, সার্ভার অর্ধেক সময় অলস। সিস্টেমে গড়ে ১টি অনুরোধ। গড় সময় ২০ ms — সেবার ১০ ms আর অপেক্ষা ১০ ms. p99 ৯২ ms.
  2. ρ = ৮০% — এখনো ঠিক আছে। ট্রাফিক ১.৬ গুণ, কিন্তু গড় সময় ২.৫ গুণ (৫০ ms). সিস্টেমে গড়ে ৪টি, p99 ২৩০ ms. টোকেন-কাউন্টারে মাঝে মাঝে ৪–৫ জনের লাইন।
  3. ρ = ৯০% — হাঁটুর কাছে। আরও মাত্র ১২.৫% ট্রাফিক, কিন্তু গড় সময় দ্বিগুণ হয়ে ১০০ ms, সিস্টেমে ৯টি। p99 ৪৬১ ms — ব্যবহারকারী ধীরতা টের পান, অথচ graph-এ “মাত্র ৯০%”.
  4. ρ = ৯৫% — খাড়া ঢাল। ট্রাফিক বাড়ল ৫.৬%, গড় সময় আবার দ্বিগুণ — ২০০ ms; সিস্টেমে ১৯টি। p99 প্রায় ১ সেকেন্ড। ছোট একটি burst এখানে মিনিটের পর মিনিট লাইন রেখে যায়।
  5. ρ = ৯৯% — প্রায় ভেঙে পড়া। গড় সময় ১ সেকেন্ড — সেবা-সময়ের ১০০ গুণ — সিস্টেমে ৯৯টি অনুরোধ জমা। p99 ৪.৬ সেকেন্ড; client-রা timeout দিয়ে আবার চেষ্টা করে λ আরও বাড়ায় (retry storm).
  6. ρ ≥ ১০০% — স্থিতিশীলতা নেই। আগমন সেবার সমান বা বেশি হলে সারি কেবলই লম্বা হয়, যতক্ষণ না মেমরি ফুরোয় বা অনুরোধ ফেলে দেওয়া হয়। উপায়: ক্ষমতা বাড়ানো, কাজ কমানো, বা ইচ্ছা করে কিছু অনুরোধ নাকচ করা (load shedding).

৫ · চার সম্পদের সংখ্যা পড়া — CPU, মেমরি, ডিস্ক আর নেটওয়ার্ক

CPU — user, system, iowait, steal

CPU-সময় কয়েকটি ভাগে দেখানো হয়। Linux এগুলো গোনে /proc/stat-এ, প্রতিটি CPU-র জন্য, “tick” এককে (সাধারণত সেকেন্ডে ১০০টি); দুটি মুহূর্তের পার্থক্য থেকে শতাংশ বানানো হয়।

ভাগ (top-এ)মানেবেশি হলে কী ভাববেন
us — useruser mode-এ প্রোগ্রামের নিজের কোডকোন প্রোগ্রাম? pidstat 1, তারপর profile (perf)
ni — niceকম অগ্রাধিকারের (nice > 0) কাজের user সময়সাধারণত ব্যাকআপ বা batch কাজ
sy — systemkernel mode — system call, page fault, driverখুব ঘন syscall? strace -c বা perf; মেমরি-চাপে page reclaim?
wa — iowaitCPU অলস, আর তখন অন্তত একটি I/O অপেক্ষায়CPU নয়, ডিস্ক দেখুন (iostat); এটি অলস সময়ের একটি ভাগ
hi / si — irq / softirqhardware interrupt আর তার পরের kernel-কাজপ্রচুর নেটওয়ার্ক প্যাকেট? একটি CPU-তে জমছে কি না (mpstat -P ALL)
st — stealVM চলতে চাইছিল, hypervisor অন্যকে CPU দিয়েছিলhost-এ ভিড়, noisy neighbor, বা burst-কোটা শেষ
id — idleকিছুই করার ছিল নাCPU সমস্যা নয় — অন্যত্র খুঁজুন

iowait-এর ফাঁদ: নামের কারণে অনেকে ভাবেন CPU I/O-র জন্য “ব্যস্ত”। আসলে এটি অলস সময়, শুধু তখন কোথাও একটি I/O অপেক্ষায় ছিল। একটি CPU-ভারী কাজ চালু করলে iowait কমে যাবে, অথচ ডিস্কের অবস্থা একটুও বদলায়নি! Linux kernel-এর ডকুমেন্টেশনও বলে বহু-CPU যন্ত্রে সংখ্যাটি নির্ভরযোগ্য নয়। এটি একটি সংকেত মাত্র: “ডিস্কের দিকে তাকান।”

Steal-এর কথা: পর্ব ৫০-এ (virtual machine আর hypervisor) দেখেছেন, একটি ভৌত CPU কয়েকটি VM-এর vCPU ভাগ করে চালাতে পারে। KVM বা Xen-এর মতো hypervisor guest kernel-কে জানায় কতক্ষণ তার vCPU চলতে চেয়েও পায়নি — সেটিই st. নিয়মিত কয়েক শতাংশের বেশি steal মানে host-এ ভিড়, বা burst-ধরনের instance-এ CPU credit শেষ (প্রদানকারীভেদে আলাদা) — নিজের কোড না বদলালেও কেন ধীর, তার একটি লুকানো উত্তর।

CPU-র saturation — run queue আর load average

Utilization ১০০% হলে প্রশ্ন: কতজন লাইনে? vmstat 1-এর r কলাম দেখায় এই মুহূর্তে কতগুলো thread চলছে বা চলার অপেক্ষায় (run queue). r নিয়মিত CPU-সংখ্যার চেয়ে বেশি মানে CPU saturated. পর্ব ১১-এ (Shell, ps, top আর /proc) load average-এর বিস্তারিত দেখেছেন; এখানে শুধু মনে রাখুন, Linux-এ load average-এ ডিস্কের অপেক্ষায় আটকে থাকা (D-অবস্থার) thread-ও গোনা হয় — তাই উচ্চ load average মানেই CPU-র ভিড় নয়। সরাসরি মাপ PSI: /proc/pressure/cpu-এর some avg10=35.00 মানে গত ১০ সেকেন্ডের ৩৫% সময় অন্তত একটি কাজ CPU-র অপেক্ষায় ছিল।

Context switch: vmstat-এর cs কলাম সেকেন্ডে কতগুলো context switch. পর্ব ৯-এ (context switch আর dispatcher) এর খরচ দেখেছেন। এর কোনো সর্বজনীন “ভালো” মান নেই — baseline-এর সাথে তুলনা করুন। pidstat -w 1 দুই ভাগে দেখায়: cswch/s (নিজে থেমেছে — I/O, lock, sleep) আর nvcswch/s (জোর করে সরানো — CPU-র ভিড়)। অনেক voluntary switch আর কম CPU-ব্যবহার একসাথে প্রায়ই lock contention-এর ইঙ্গিত।

মেমরি — RSS-এর বৃদ্ধি, page fault, swap আর OOM

  • Utilization: free -m-এর available কলাম (MemAvailable) — free নয়। Linux ফাঁকা RAM page cache-এ লাগিয়ে রাখে, তাই “free” কম দেখানো স্বাভাবিক। এই পার্থক্য পর্ব ৩৮-এ (Linux-এ মেমরি) বিস্তারিত আছে।
  • প্রসেসের বৃদ্ধি: কাজ একই থাকলেও RSS ঘণ্টার পর ঘণ্টা বাড়লে তা memory leak বা সীমাহীন cache-এর লক্ষণ; pidstat -r 1 দিয়ে কয়েক মিনিট পরপর তুলনা করুন।
  • Page fault: minor fault সস্তা, major fault দামি (ডিস্ক থেকে পড়া)। major fault-এর নিয়মিত স্রোত মানে কাজের মেমরি RAM-এ ধরছে না।
  • Saturation: vmstat-এর si/so — সেকেন্ডে কত KiB swap-এ ঢুকছে-বেরোচ্ছে। পুরোনো page swap-এ পড়ে থাকা (swpd বড়) সমস্যা নয়; চলমান si/so সমস্যা। /proc/pressure/memory-এর ব্যবহার পর্ব ৩৭-এ (thrashing আর working set) দেখেছেন।
  • Errors: OOM killer একটি প্রসেস মারলে dmesg-এ “Out of memory: Killed process …” লাইন আসে, আর /proc/vmstat-এর oom_kill বাড়ে (Linux 4.13 থেকে); container-এ cgroup-এর memory.events দেখুন।

ডিস্ক — IOPS, throughput, await আর %util-এর ফাঁদ

iostat -xz 1 প্রতিটি ডিস্কের এক লাইন দেয়। মূল কলামগুলো: r/s, w/s (IOPS), rkB/s, wkB/s (throughput), r_await, w_await (await — সারিতে অপেক্ষাসহ গড় সময়, ms), aqu-sz (গড় সারির দৈর্ঘ্য) আর %util. দুটি দ্রুত যাচাই কাজে লাগে: গড় I/O-র আকার = kB/s ÷ IOPS (৪ KiB মানে ছোট এলোমেলো I/O, প্রায়ই swap), আর Little's law = aqu-sz ≈ IOPS × await. পর্ব ৪০-এ (ডিস্ক আর SSD) HDD, SATA SSD আর NVMe-র IOPS-এর মাত্রা দেখেছেন।

%util ১০০% মানেই ডিস্ক ভরপুর নয়। %util শুধু বলে সময়ের কত অংশে অন্তত একটি অনুরোধ চলছিল। একটি HDD একবারে মূলত একটি কাজ করে, তাই সেখানে ১০০% প্রায় ভরপুর। কিন্তু একটি NVMe SSD একসাথে বহু অনুরোধ সমান্তরালে সামলায় (বহু queue, বহু flash chip) — সেখানে সারাক্ষণ একটি অনুরোধ থাকলেই ১০০%, অথচ ক্ষমতার সামান্য অংশ ব্যবহার হতে পারে। sysstat-এর নিজের manual-ও এই সতর্কতা দেয়। SSD/NVMe-তে saturation বুঝতে await বাড়ছে কি না, aqu-sz, আর ডিভাইসের পরিচিত IOPS-সীমা দেখুন; cloud ডিস্কে প্রদানকারীর দেওয়া IOPS/throughput সীমাই প্রায়ই আসল ছাদ।

নেটওয়ার্ক — সংক্ষেপে

sar -n DEV 1 প্রতিটি interface-এর rx/tx প্যাকেট আর kB/s দেখায়; লিংকের গতির সাথে তুলনা করে utilization (১ Gbit/s ≈ ১২৫,০০০ kB/s). sar -n TCP,ETCP 1-এ নতুন সংযোগের হার (active/s = এই যন্ত্র থেকে, passive/s = এই যন্ত্রে) আর retrans/s — TCP retransmission, অর্থাৎ পথে প্যাকেট হারানোর সংকেত; প্রতিটি retransmit একটি অনুরোধে শত ms যোগ করতে পারে। ip -s link বা sar -n EDEV 1-এ drop আর error. বিস্তারিত নেটওয়ার্ক-বিশ্লেষণ এই কোর্সের বিষয় নয়; USE-এর জন্য এটুকুই যথেষ্ট।

৬ · প্রথম ৬০ সেকেন্ড — দশটি কমান্ডের চেকলিস্ট

২০১৫ সালে Netflix-এর প্রকৌশল-ব্লগে Brendan Gregg আর Netflix-এর পারফরম্যান্স-দল একটি লেখা প্রকাশ করেন, যার মূল কথা: একটি ধীর Linux সার্ভারে লগইন করে প্রথম এক মিনিটে এই দশটি কমান্ড চালান। এগুলো প্রায় সব Linux-এ পাওয়া যায় (sysstat প্যাকেজ ইনস্টল থাকলে), আর মিলে USE-এর বেশিরভাগ ঘর ভরে দেয়:

#কমান্ডযা খুঁজবেন
১uptimeload average ১, ৫, ১৫ মিনিট — বাড়ছে না কমছে? CPU-সংখ্যার সাথে তুলনা
২dmesg | tailOOM kill, ডিস্ক বা নেটওয়ার্কের error, TCP-র সতর্কবার্তা — E-এর ঘর
৩vmstat 1r (run queue), b, si/so, us sy id wa st — প্রথম লাইন বাদ (বুটের পর থেকে গড়)
৪mpstat -P ALL 1প্রতি CPU — একটি core ১০০% আর বাকিরা অলস? একক-thread কাজ বা interrupt জমা
৫pidstat 1কোন প্রসেস CPU খাচ্ছে, সময়ের সাথে (top-এর মতো, কিন্তু স্ক্রল করা লগ)
৬iostat -xz 1IOPS, kB/s, await, aqu-sz, %util — প্রতি ডিস্ক
৭free -mavailable কত, swap কতটা ব্যবহার
৮sar -n DEV 1নেটওয়ার্ক throughput বনাম লিংকের সীমা
৯sar -n TCP,ETCP 1নতুন সংযোগের হার, retransmit
১০topসব একসাথে — আর আগের সংখ্যাগুলোর সাথে মিলিয়ে দেখা

এই পাতার রানারে sysstat নেই, root-ও নেই। কিন্তু প্রতিটি টুল যে কাঁচা সংখ্যা পড়ে, তা /proc-এ খোলা। নিচের স্ক্রিপ্ট সেখান থেকে একটি ছোট চেকলিস্ট বানায়: /proc/stat-এর দুটি নমুনা ১ সেকেন্ড ব্যবধানে নিয়ে CPU-র ভাগ (steal-সহ), /proc/vmstat-এর পার্থক্য থেকে প্রতি সেকেন্ডের page fault আর swap, /proc/meminfo থেকে মেমরি। সেকেন্ডটি যেন একেবারে অলস না থাকে, সেজন্য পেছনে একটি ছোট CPU-লুপ চলে:

# A mini "60-second checklist" built only from /proc (no sysstat tools needed)
snap() {
  read -r _ u n s i w q sq st _ < /proc/stat              # cpu  user nice system idle iowait irq softirq steal
  CPU="$u $n $s $i $w $q $sq $st"
  VM=$(awk '{v[$1] = $2} END {print v["pgfault"], v["pgmajfault"], v["pswpin"], v["pswpout"]}' /proc/vmstat)
  CS=$(awk '$1 == "ctxt" {print $2}' /proc/stat)
}
snap; c1=($CPU); v1=($VM); x1=$CS
timeout 1 sh -c 'while :; do :; done' &                    # some CPU work so the second is not idle
sleep 1; wait
snap; c2=($CPU); v2=($VM); x2=$CS
d=(); tot=0; for k in 0 1 2 3 4 5 6 7; do d[k]=$(( c2[k] - c1[k] )); tot=$(( tot + d[k] )); done
f=(); for k in 0 1 2 3; do f[k]=$(( v2[k] - v1[k] )); done
pct() { awk -v a="$1" -v t="$tot" 'BEGIN { printf "%.1f", (t ? 100 * a / t : 0) }'; }
read -r l1 l5 l15 runq _ < /proc/loadavg
echo "uptime : load $l1 $l5 $l15 (runnable/total threads $runq) on $(nproc) CPUs"
echo "cpu %  : user $(pct $(( d[0] + d[1] ))) sys $(pct $(( d[2] + d[5] + d[6] ))) iowait $(pct "${d[4]}") steal $(pct "${d[7]}") idle $(pct "${d[3]}")"
echo "sched  : $(( x2 - x1 )) context switches/s; procs_running $(awk '$1 == "procs_running" {print $2}' /proc/stat)"
mem=$(awk '{m[$1] = $2} END {printf "%d %d %.0f %d", m["MemTotal:"]/1024, m["MemAvailable:"]/1024,
  100 * (1 - m["MemAvailable:"] / m["MemTotal:"]), (m["SwapTotal:"] - m["SwapFree:"]) / 1024}' /proc/meminfo)
read -r mt ma mu sw <<< "$mem"
echo "memory : total $mt MiB, available $ma MiB, swap used $sw MiB"
echo "faults : ${f[0]} page faults/s (${f[1]} major), swap in/out ${f[2]}/${f[3]} pages/s"
echo "shell  : $(grep -E '^(VmRSS|voluntary_ctxt|nonvoluntary_ctxt)' /proc/$$/status | tr -s ' \t' ' ' | paste -sd ' ')"
echo "--- USE summary (whole machine, as /proc shows it) ---"
echo "CPU    : U=$(pct $(( tot - d[3] - d[4] )))%  S=runnable ${runq%/*} vs $(nproc) CPUs  E=see dmesg"
echo "Memory : U=${mu}%  S=major ${f[1]}/s, swap-out ${f[3]}/s  E=oom_kill $(awk '$1 == "oom_kill" {print $2}' /proc/vmstat) since boot"

একটি আসল রানের আউটপুট (প্রতিবার সংখ্যা আলাদা হবে — এটি একটি ভাগ-করা যন্ত্র):

uptime : load 2.60 2.66 2.42 (runnable/total threads 2/289) on 2 CPUs
cpu %  : user 80.3 sys 16.7 iowait 1.5 steal 0.0 idle 1.5
sched  : 4662 context switches/s; procs_running 2
memory : total 1961 MiB, available 1280 MiB, swap used 440 MiB
faults : 26713 page faults/s (4 major), swap in/out 0/0 pages/s
shell  : VmRSS: 3416 kB voluntary_ctxt_switches: 19 nonvoluntary_ctxt_switches: 24
--- USE summary (whole machine, as /proc shows it) ---
CPU    : U=97.0%  S=runnable 2 vs 2 CPUs  E=see dmesg
Memory : U=35%  S=major 4/s, swap-out 0/s  E=oom_kill 12 since boot

লাইন ধরে পড়ুন। uptime: ২টি CPU-র যন্ত্রে load ২.৬০ — যন্ত্রটি ব্যস্ত; 2/289 মানে এই মুহূর্তে ২টি thread চলার অবস্থায়, মোট ২৮৯টি। cpu %: user ৮০.৩ + sys ১৬.৭ — আমাদের লুপ একটি CPU (৫০%) নিয়েছে, বাকিটা অন্যদের কাজ; iowait ১.৫, steal এই রানে ০ (আরেকটি রানে ০.৫% দেখেছিলাম)। faults: সেকেন্ডে ২৬,৭১৩টি page fault, কিন্তু major মাত্র ৪ — প্রতিটি awk, grep চালাতে fork আর exec নতুন page ছোঁয়, সেগুলো minor fault. memory: swap-এ ৪৪০ MiB পুরোনো page পড়ে আছে, কিন্তু swap in/out ০/০ — তাই এখন মেমরির saturation নেই। shell: আমাদের bash-এর ১৯টি voluntary switch (sleep আর wait-এ নিজে থেমেছে) আর ২৪টি nonvoluntary (CPU ভরা ছিল বলে সরানো হয়েছে)।

USE সারাংশ: CPU-র U ৯৭%, run queue ২ = CPU ২ — সীমার ঠিক কিনারায়। মেমরির U ৩৫%, S নেই। কিন্তু E-এর ঘরে oom_kill 12 — বুটের পর এই যন্ত্রে ১২ বার OOM killer চলেছে (সম্ভবত অন্যদের পাঠানো মেমরি-খেকো প্রোগ্রাম); কাকে মেরেছে তা dmesg ছাড়া জানা যায় না, আর root ছাড়া সেটি বন্ধ। একটি বড় সতর্কতা: /proc/stat, /proc/loadavg আর /proc/meminfo namespace মানে না, তাই container-এর ভেতর থেকে এগুলো পুরো VM-এর ছবি দেখায় — container-এর নিজের সীমা আর ব্যবহার থাকে cgroup ফাইলে (পর্ব ৫১)। আপনার রানে প্রতিটি সংখ্যা আলাদা হবে।

৭ · গভীরে খোঁড়া — strace, ltrace, perf আর hardware counter

চেকলিস্ট বলে কোন সম্পদ বা প্রসেস; এবার প্রশ্ন প্রসেসটির ভেতরে কী। এখানে টুলগুলো দুই ধরনের: tracing (প্রতিটি ঘটনা ধরে — সঠিক, কিন্তু দামি) আর sampling (নির্দিষ্ট হারে নমুনা — সস্তা, পরিসংখ্যানভিত্তিক)।

strace আর ltrace — কার্যকর, কিন্তু সাবধানে

পর্ব ৫-এ strace দেখেছেন। পারফরম্যান্সে এর দুটি কাজের রূপ: strace -c -p PID (কোন system call কতবার, কত সময় — সারাংশ) আর strace -T -e trace=openat,read -p PID (প্রতিটি কলের সময়)। ltrace একই কাজ করে library ফাংশনের (malloc, strlen) জন্য।

strace প্রোগ্রামকে অনেক ধীর করতে পারে। প্রচলিত strace ptrace দিয়ে কাজ করে: প্রতিটি system call-এ লক্ষ্য-প্রসেস থামে, strace জাগে, তথ্য পড়ে, আবার চালু করে — প্রতি কলে কয়েকটি বাড়তি context switch. Brendan Gregg-সহ অনেকে মেপে দেখিয়েছেন, syscall-ভারী প্রোগ্রাম এতে কয়েক গুণ থেকে শত গুণ পর্যন্ত ধীর হতে পারে। production সার্ভারে strace লাগানো মানে ধীরতা আরও বাড়ানো — observer effect-এর পরিচিত উদাহরণ। নতুন strace-এর --seccomp-bpf (শুধু বাছাই করা syscall ধরলে) খরচ কমায়; perf trace বা eBPF-টুল সাধারণত অনেক কম ভার দেয়। নিয়ম: আগে test পরিবেশে, অল্প সময়ের জন্য।

perf — Linux-এর নিজস্ব profiler

perf (kernel-এর perf_events ব্যবস্থার টুল, Linux 2.6.31 থেকে, ২০০৯ সালের দিকে) kernel source-এর সাথেই আসে। তিনটি কাজ সবচেয়ে বেশি লাগে:

# ১. গোনা: পুরো প্রোগ্রামে কত cycle, instruction, cache miss
perf stat -e cycles,instructions,cache-misses ./myapp

# ২. নমুনা: সব CPU-তে ৯৯ Hz-এ call stack-সহ ৩০ সেকেন্ড, তারপর রিপোর্ট
perf record -F 99 -a -g -- sleep 30
perf report --stdio

# ৩. সরাসরি: এই মুহূর্তে কোন ফাংশনে CPU যাচ্ছে (top-এর মতো)
perf top

Hardware counter আর IPC: আধুনিক CPU-র ভেতরে কয়েকটি বিশেষ register থাকে — performance counter — যা cycle, শেষ হওয়া instruction, cache miss, branch miss গোনে। এদের অনুপাত IPC (instructions per cycle) একটি অসাধারণ সংকেত: আধুনিক CPU আদর্শ অবস্থায় প্রতি cycle-এ কয়েকটি নির্দেশ শেষ করতে পারে, তাই IPC ১-এর অনেক নিচে মানে CPU বেশিরভাগ সময় থেমে আছে — সাধারণত মেমরির অপেক্ষায় (cache miss). top এই দুই অবস্থার পার্থক্য দেখায় না: দুটোতেই “১০০% CPU”.

এই পাতার রানারে perf নেই, আর এটি একটি VM-এর ভেতরের container — অনেক cloud VM hardware counter guest-কে দেয়ই না। তাই নিচের প্রোগ্রাম সৎভাবে যা মাপা যায় শুধু তা-ই মাপে: wall-clock time আর CPU time, আর “প্রতি কাজে কত ns” — IPC-র একটি মোটা বদলি। তিনটি অংশ: registers-এ থাকা একটি হিসাব, ৬৪ MiB-এর এলোমেলো একটি চক্রে pointer অনুসরণ (প্রতিটি load আগেরটির ফলের ওপর নির্ভর, তাই cache miss লুকানো যায় না), আর শুধু অপেক্ষা:

#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

static double now(clockid_t c) { struct timespec t; clock_gettime(c, &t); return t.tv_sec + t.tv_nsec / 1e9; }
static double w0, c0;
static void start(void) { w0 = now(CLOCK_MONOTONIC); c0 = now(CLOCK_PROCESS_CPUTIME_ID); }
static void report(const char *name, long ops) {
    double w = now(CLOCK_MONOTONIC) - w0, c = now(CLOCK_PROCESS_CPUTIME_ID) - c0;
    printf("%-13s wall %.3f s  cpu %.3f s  cpu/wall %3.0f%%", name, w, c, 100 * c / w);
    if (ops > 0) printf("  %6.2f ns per op\n", 1e9 * w / ops); else printf("  (no work, only waiting)\n");
}
int main(void) {
    const long N = 1L << 23;                        /* 8M slots x 8 bytes = 64 MiB, far bigger than any cache */
    size_t *next = malloc(N * sizeof *next);
    if (!next) return 1;
    for (long i = 0; i < N; i++) next[i] = (size_t)i;
    unsigned long long s = 57;                      /* Sattolo shuffle: one random cycle through all slots */
    for (long i = N - 1; i > 0; i--) {
        s = s * 6364136223846793005ULL + 1442695040888963407ULL;
        long j = (long)((s >> 33) % (unsigned long long)i);
        size_t t = next[i]; next[i] = next[j]; next[j] = t;
    }
    unsigned long long x = 1; size_t p = 0; start(); /* 1. CPU-bound: the data lives in registers */
    for (long i = 0; i < 200000000; i++) x = x * 2862933555777941757ULL + 3037000493ULL;
    report("cpu-bound", 200000000);
    start();                                        /* 2. memory-bound: each load waits for the previous miss */
    for (long i = 0; i < 5000000; i++) p = next[p];
    report("pointer-chase", 5000000);
    start();                                        /* 3. off-CPU: waiting, not computing */
    nanosleep(&(struct timespec){0, 300000000}, NULL);
    report("sleep", 0);
    printf("checksums %llu %zu\n", x % 1000, p % 1000); free(next); return 0;
}

একটি আসল রানের আউটপুট (সময় প্রতিবার কিছুটা আলাদা হবে):

cpu-bound     wall 0.373 s  cpu 0.360 s  cpu/wall  96%    1.87 ns per op
pointer-chase wall 1.228 s  cpu 1.144 s  cpu/wall  93%  245.65 ns per op
sleep         wall 0.301 s  cpu 0.001 s  cpu/wall   0%  (no work, only waiting)
checksums 761 423

cpu-bound: ২০ কোটি গুণ-যোগে ০.৩৭৩ s — প্রতিটিতে ~১.৯ ns, অর্থাৎ ৩ GHz-এর কাছাকাছি ঘড়িতে কয়েকটি cycle. cpu/wall ৯৬%: বাকি ৪% সময় প্রসেসটি CPU পায়নি — ভাগ-করা যন্ত্রে অন্যদের পালা। pointer-chase: মাত্র ৫০ লাখ load-এ ১.২৩ s — প্রতিটিতে ২৪৫ ns, আগেরটির চেয়ে প্রায় ১৩০ গুণ ধীর। প্রতিটি load cache-এ miss করে RAM পর্যন্ত যায়, ৬৪ MiB-এর এলোমেলো ঠিকানায় TLB-ও miss করে, আর VM-এর ভেতরে page walk দুই স্তরের (পর্ব ৫০)। অথচ cpu/wall ৯৩% — top এই প্রসেসকে “CPU-ব্যস্ত” দেখাত, যদিও CPU প্রায় পুরো সময় মেমরির অপেক্ষায় থেমে ছিল। sleep: wall ০.৩০১ s, CPU প্রায় শূন্য — ব্যবহারকারীর কাছে দেরি, কিন্তু CPU-profiler-এ অদৃশ্য।

তিনটি মিলে মূল শিক্ষা: wall time = CPU-তে কাজ + CPU-তে থেমে থাকা (stall) + CPU-র বাইরে অপেক্ষা। CPU time প্রথম দুটি আলাদা করতে পারে না — সেজন্য IPC লাগে। ns-per-op এখানে IPC-র মোটা বদলি, আসল IPC নয়। শেষের checksums লাইন ফলগুলো ব্যবহার করে, যাতে compiler লুপগুলো মুছে না দেয়।

perf থাকা একটি সাধারণ যন্ত্রে perf stat সরাসরি এই পার্থক্য দেখাত। নিচের আউটপুট আনুমানিক উদাহরণ — ওপরের সময় আর ~৩ GHz ঘড়ি ধরে হিসাব করা, কোনো আসল রান নয়:

cpu-bound phase      1,050,000,000 cycles   2,000,000,000 instructions   #  1.90 insn per cycle
pointer-chase phase  3,210,000,000 cycles      45,000,000 instructions   #  0.01 insn per cycle
                                                4,900,000 cache-misses (about one per load)

দুটি অংশেই top একই “১০০% CPU” দেখাত। প্রথমটিতে CPU সত্যিই কাজ করছে; দ্বিতীয়টিতে প্রায় সব cycle মেমরির অপেক্ষায় নষ্ট। সমাধানও আলাদা: প্রথমটিতে কম নির্দেশ (ভালো অ্যালগরিদম), দ্বিতীয়টিতে ভালো locality (ডেটা পাশাপাশি রাখা, পর্ব ৪-এর memory hierarchy মনে করুন)।

৮ · Flame graph, eBPF আর অন্য OS-এর টুল

perf report-এর লেখা আউটপুট হাজার লাইনের হতে পারে — প্রতিটি আলাদা call stack আর তার শতাংশ। ২০১১ সালে Brendan Gregg (তখন Joyent-এ, একটি MySQL পারফরম্যান্স-সমস্যা খুঁজতে গিয়ে) এই স্তূপকে একটি ছবিতে আনার উপায় বানান: flame graph. একটি sampling profiler (যেমন perf record -g) প্রতিটি নমুনায় একটি পুরো call stack নেয়; একই stack-গুলো গুনে একসাথে করা হয়, তারপর আঁকা হয়। নিচে একটি কাল্পনিক API সার্ভারের CPU flame graph — মোট ১০০০টি নমুনা — ধাপে ধাপে পড়ুন:

regex_compile
regex_match
pq_exec
compute_price
db_query
parse_json
render_json
gc
handle_request
log_write
main

প্রস্থ = নমুনার অনুপাত (মোট ১০০০) · নিচে root, ওপরে leaf · x-অক্ষ সময় নয়

এই ধাপে
দেখছি
মোট নমুনা (ফ্রেম + ওপরের সব)
নিজের (self) নমুনা
সিদ্ধান্ত
  1. নিচ থেকে শুরু। প্রতিটি আয়তক্ষেত্র একটি ফাংশন (stack frame); নিচের ফ্রেম ওপরেরটিকে ডেকেছে। সবচেয়ে নিচে main — প্রতিটি নমুনায় সে stack-এ ছিল, তাই পুরো প্রস্থ (১০০০/১০০০)। নমুনা ধরুন perf record -F 99 -g দিয়ে নেওয়া; ১০০ নয়, ৯৯ Hz, যাতে নিয়মিত টাইমারের সাথে তাল মিলে বিকৃতি না হয়।
  2. প্রস্থ মানে নমুনা, x-অক্ষ সময় নয়। main-এর ওপরে তিনটি ফ্রেম: gc ১০০, handle_request ৮৬০, log_write ৪০ — যোগফল ১০০০. এরা বাঁ থেকে ডানে বর্ণানুক্রমে সাজানো (g, h, l), ঘটনার ক্রমে নয় — “বাঁয়েরটা আগে ঘটেছে” পড়া ভুল। রং সাধারণত শুধু চেনার সুবিধার জন্য।
  3. সবচেয়ে চওড়া টাওয়ার ধরে ওপরে উঠুন। handle_request ৮৬% নমুনায় ছিল। তার ওপরে চারটি সন্তান: compute_price ৫২%, db_query ১৫%, parse_json ৯%, render_json ৮%; বাকি ২% (২০ নমুনা) handle_request-এর নিজের — তার ওপরে খালি জায়গা। সবচেয়ে চওড়া সন্তান compute_price-এ উঠুন।
  4. চ্যাপ্টা মাথা (plateau) খুঁজুন। একটি ফ্রেমের ওপরের প্রান্তে যতটা জায়গা খালি, ততটা সময় CPU ঠিক সেই ফাংশনের নিজের কোড চালাচ্ছিল (self time). regex_compile-এর ওপরে আর কিছু নেই, প্রস্থ ৩৮% — অর্থাৎ পুরো সার্ভারের CPU-র ৩৮% একটি regular expression compile করতে যাচ্ছে! কোড দেখে পাওয়া গেল: প্রতিটি অনুরোধে দামের নিয়ম একই pattern থেকে নতুন করে compile হচ্ছে।
  5. তুলনা করুন — আর যা দেখা যায় না তা মনে রাখুন। আসল মিলানো (regex_match) মাত্র ১০% — কাজটি সস্তা, প্রস্তুতিটি দামি। db_query → pq_exec ১৫%, কিন্তু এটি শুধু CPU-তে কাটানো সময় (query পাঠানো, ফল পড়া)। ডেটাবেসের উত্তরের অপেক্ষা CPU flame graph-এ থাকে না, কারণ অপেক্ষার সময় thread CPU-তে নেই। তার জন্য লাগে off-CPU analysis.
  6. সমাধান আর যাচাই। pattern একবার, শুরুতেই compile করে রেখে দেওয়া হলো। একই কাজে এখন প্রায় ১০০০ − ৩৮০ = ৬২০ নমুনার CPU লাগে। সার্ভারটি যদি CPU-তেই আটকে থাকে, ক্ষমতা বাড়ে প্রায় ১০০০/৬২০ ≈ ১.৬১ গুণ। আবার flame graph নিয়ে যাচাই করুন টাওয়ারটি উধাও হয়েছে কি না (আগে-পরের তুলনায় differential flame graph-ও আছে)।

বাস্তবে Gregg-এর FlameGraph স্ক্রিপ্ট (perf script | stackcollapse-perf.pl | flamegraph.pl > out.svg) বা নতুন টুল ছবিটি বানায়; কিছু টুলে ছবি উল্টো (icicle, root ওপরে), পড়ার নিয়ম একই।

eBPF-ভিত্তিক টুল — production-এ নিরাপদ, কম-খরচের tracing

eBPF kernel-এর ভেতরে যাচাই-করা ছোট প্রোগ্রাম চালাতে দেয়; প্রোগ্রামটি kernel-এর ভেতরেই গুনে বা histogram বানিয়ে শুধু সারাংশ user space-এ পাঠায়, তাই strace-এর মতো প্রতি ঘটনায় থামার খরচ নেই। এর ভেতরের যন্ত্র — verifier, map, hook — পর্ব ৫৫-এ (Linux kernel-এর ভেতর) আলোচিত; এখানে শুধু ব্যবহারকারীর দিক। দুটি জনপ্রিয় সংগ্রহ: BCC-এর তৈরি টুল আর bpftrace-এর এক লাইনের প্রোগ্রাম। দুটোরই root (বা সমতুল্য capability) আর নতুন kernel লাগে:

# BCC tools (Ubuntu/Debian names often end in -bpfcc, e.g. biolatency-bpfcc)
execsnoop          # every new process (short-lived ones that top never shows)
opensnoop          # which process opens which file
biolatency 10 1    # disk I/O latency as a histogram, one 10-second interval
runqlat 10 1       # how long threads waited in the CPU run queue (histogram)
tcpretrans         # each TCP retransmission, with addresses
offcputime -p PID 10   # where a process blocked, with stacks (off-CPU analysis)

# bpftrace one-liners
bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'   # syscalls per program
bpftrace -e 'profile:hz:99 { @[comm] = count(); }'                          # on-CPU samples per program

খেয়াল করুন, runqlat সরাসরি CPU-র saturation মাপে (thread কতক্ষণ লাইনে দাঁড়াল), আর biolatency ডিস্কের latency-কে histogram-এ দেখায় — অংশ ৩-এর “percentile বনাম histogram” শিক্ষার বাস্তব প্রয়োগ।

Windows আর macOS — একই প্রশ্ন, অন্য টুল

USE, RED আর queueing-এর অঙ্ক OS-নিরপেক্ষ; বদলায় টুল আর সংখ্যার সংজ্ঞা (Windows-এর “Processor Queue Length” আর Linux-এর load average এক জিনিস নয়)।

Task Manager (Ctrl+Shift+Esc): প্রসেস, CPU, মেমরি, ডিস্ক, নেটওয়ার্ক — দ্রুত এক নজরে। Resource Monitor (resmon): কোন প্রসেস কোন ফাইল পড়ছে-লিখছে, ডিস্কের response time, কোন প্রসেস কী নিয়ে অপেক্ষা করছে (“Analyze Wait Chain”). Performance Monitor (perfmon): শত শত counter গ্রাফে আর লগে — USE-এর জন্য কয়েকটি: \Processor(_Total)\% Processor Time (U), \System\Processor Queue Length (S), \Memory\Available MBytes আর \Memory\Pages/sec, \PhysicalDisk(*)\Avg. Disk sec/Read আর Current Disk Queue Length. গভীর বিশ্লেষণে WPR আর WPA: ETW-ভিত্তিক trace নিয়ে CPU sampling, wait, disk I/O, flame graph — Linux-এর perf-এর কাছাকাছি ভূমিকা। Windows-এর ভেতরের গঠন পর্ব ৫৬-এ (Windows-এর ভেতর)।

Get-Counter '\Processor(_Total)\% Processor Time','\System\Processor Queue Length','\Memory\Available MBytes' -SampleInterval 1 -MaxSamples 5
wpr -start CPU -start DiskIO
wpr -stop C:\traces\slow.etl      # then open the .etl file in WPA

Activity Monitor: CPU, Memory (memory pressure-এর রঙিন graph, compressed memory, swap), Energy, Disk, Network ট্যাব। Terminal-এ top -o cpu, vm_stat 1 (page-in/out, swap), iostat 1, sample PID 5 (একটি প্রসেসের call stack-এর নমুনা), আর sudo fs_usage (ফাইল-সিস্টেমের কাজ সরাসরি)। গভীর বিশ্লেষণে Xcode-এর Instruments: Time Profiler (sampling, call tree), Allocations, Leaks, System Trace — আর কমান্ড-লাইনে xctrace. macOS-এ DTrace-এর ঐতিহ্য আছে, কিন্তু System Integrity Protection চালু থাকলে এর অনেকটা সীমিত।

এক নজরে: top / htop. প্রতি সেকেন্ড: vmstat, mpstat, pidstat, iostat, sar. কাঁচা উৎস: /proc/stat, /proc/vmstat, /proc/pressure/*, /sys-এর cgroup ফাইল (cpu.stat-এ throttling). গভীরে: strace, ltrace, perf, flame graph, BCC, bpftrace.

৯ · কেস স্টাডি — ছাড়ের দিনে একটি e-commerce API ধীর হয়ে গেল

এবার সব একসাথে। নিচের গল্পটি কাল্পনিক — কোনো আসল প্রতিষ্ঠানের ঘটনা নয় — কিন্তু এ ধরনের ঘটনা বাস্তবে পরিচিত। একটি বাংলাদেশি অনলাইন দোকান, ধরা যাক “দোকানঘর”, একটি বড় ছাড়ের দিনে সকাল ১০টায় অফার চালু করল। তাদের product আর checkout API চলে একটি cloud VM-এ: ৪টি vCPU, ৮ GiB RAM, root ডিস্কে ৪ GiB-এর একটি swapfile, আর প্রদানকারীর দেওয়া ডিস্ক-সীমা ধরা যাক ৩,০০০ IOPS. অ্যাপটি Python-এ লেখা, gunicorn-এর কয়েকটি worker প্রসেসে চলে।

১০:৪০-এ অভিযোগ আসতে শুরু করল। অন-কল প্রকৌশলী তানজিলা আন্দাজে কিছু না বদলে USE-এর তালিকা ধরে এগোলেন। টুল-আউটপুটগুলো উদাহরণ — বানানো, কিন্তু সংখ্যাগুলো পরস্পরের সাথে মিলিয়ে হিসাব করা; কিছু কলাম ছাঁটা:

  1. লক্ষণREDকী, কতটা
  2. CPUmpstatU · S · E
  3. মেমরিfreeU · S · E
  4. ডিস্কiostatU · S · E
  5. নেটওয়ার্কsar -nU · S · E
  6. মূল কারণpidstat -rকে, কেন
  7. সমাধানযাচাইআবার মাপা
USE-এর খাতা
সম্পদ / ধাপ
U
S
E
রায়
  1. লক্ষণ — আগে RED. তানজিলা প্রথমে লিখে নিলেন কী ঘটছে: অনুরোধ স্বাভাবিকের চার গুণ (৩৮০/s বনাম ৯৫/s), ৩.১% অনুরোধ 504 timeout, আর p99 ১৮০ ms থেকে ৪.২ সেকেন্ড। uptime-এ load average ১৭.৮ — ৪টি CPU-র যন্ত্রে, আর বাড়ছে (১৫ মিনিটের গড় ৯.৬)। প্রথম প্রলোভন: “VM বড় করি।” তিনি থামলেন — load average-এ D-অবস্থার thread-ও গোনা হয়।
  2. CPU — U, S, E. mpstat: user ১৮.২% + system ৯.১% ≈ ২৭% ব্যস্ত; %iowait ৫১.৬%, অলস ২০.৮%, steal প্রায় শূন্য — তাই প্রতিবেশীর দোষও নয়। vmstat-এ r = 3, CPU ৪টি: চলার অপেক্ষায় সারি নেই, তাই CPU-র saturation নেই। কিন্তু b = 14 — ১৪টি thread D-অবস্থায় আটকে; load average ১৭.৮-এর বড় অংশ এরাই (৩ + ১৪ = ১৭)। রায়: CPU দোষী নয়; iowait ৫২% বলছে I/O-র দিকে তাকান।
  3. মেমরি — সন্দেহভাজন পাওয়া গেল। free -m: ৭,৯৫১ MiB-এর মধ্যে available মাত্র ১৩৯ MiB, buff/cache-ও প্রায় শেষ (১৪৩ MiB), swap-এ ২,৫৬০ MiB. vmstat-এর si ৪,৮২০ আর so ৫,১৩০ kB/s: এই মুহূর্তে swap চলছে, দুই দিকেই। PSI-তে some avg10=61.20 — গত ১০ সেকেন্ডের ৬১% সময় কোনো না কোনো কাজ মেমরির জন্য আটকে; full ৩৮.৯% — প্রায় ৩৯% সময় সব কাজ একসাথে। E-এর ঘরে dmesg: ১০:৩৮-এ OOM killer একটি gunicorn worker মেরেছে।
  4. ডিস্ক — ভরপুর, কিন্তু কেন? iostat: সেকেন্ডে ১,৫৪৫টি পড়া আর ১,৪০০টি লেখা = ২,৯৪৫ IOPS, ডিস্কের ৩,০০০ সীমার ৯৮%. aqu-sz ১২১.৫, await প্রায় ৪০ ms — স্বাভাবিক দিনে ~১ ms. Little's law মিলিয়ে দেখুন: (১৫৪৫ × ০.০৩৮২) + (১৪০০ × ০.০৪৪৬) ≈ ৫৯ + ৬২ = ১২১. গড় আকার: ৬,১৮০ ÷ ১,৫৪৫ = ৪.০ KiB — ঠিক একটি page. আর vmstat-এর si+so (৯,৯৫০ kB/s) মোট ডিস্ক-ট্রাফিকের (১১,৭৮০ kB/s) প্রায় ৮৪%. রায়: ডিস্ক saturated, কিন্তু এটি মেমরি-সমস্যার ফল — swap-এর ছোট এলোমেলো I/O ডিস্কের সীমায় ঠেকেছে।
  5. নেটওয়ার্ক — বাদ দেওয়াও তদন্ত। eth0-তে tx ৯,৮১০ kB/s ≈ ৭৮ Mbit/s — ১০ Gbit/s লিংকের ০.৮%. drop শূন্য। retransmit সেকেন্ডে ০.৫টি — পাঠানো segment-এর তুলনায় নগণ্য। orsts/s ৩১ (RST পাঠানো) timeout-এ কাটা সংযোগের সাথে মেলে — লক্ষণ, কারণ নয়। রায়: নেটওয়ার্ক নির্দোষ।
  6. মূল কারণ — কে মেমরি খাচ্ছে, আর কেন? pidstat -r: ১৬টি gunicorn worker, প্রতিটির RSS প্রায় ৫২০ MiB (প্রতিটি মোট RAM-এর ৬.৫%) — মোট প্রায় ১৬ × ৫২০ = ৮,৩২০ MiB, যন্ত্রের ৭,৯৫১ MiB-এর চেয়ে বেশি। প্রতিটি worker সেকেন্ডে ~৭৫টি major fault করছে (১৬ × ৭৫ ≈ ১,২০০, vmstat-এর সাথে মেলে)। দুটি কারণ মিলে: ছাড়ের প্রস্তুতিতে কেউ worker ৮ থেকে ১৬ করেছিলেন (“বেশি worker = বেশি ক্ষমতা”), আর অ্যাপের ভেতরের product-cache-এর কোনো সীমা নেই — সকালে প্রতিটি worker ~১৮০ MiB ছিল, ট্রাফিকের সাথে কেবলই বেড়েছে। ৮ worker-এ (৮ × ৫২০ = ৪,১৬০ MiB) এটি ধরত; ১৬-তে ধরে না।
  7. সমাধান আর যাচাই। একসাথে সব নয়, একে একে: worker ৮-এ ফেরত (CPU-তে জায়গা ছিল, ২৭% মাত্র), cache-এ আকারের সীমা (LRU), আর বাড়তি ট্রাফিকের জন্য load balancer-এর পেছনে আরও দুটি VM. প্রতিটি worker গড়ে ~৪৫ ms-এ একটি অনুরোধ শেষ করলে প্রতি VM-এর ক্ষমতা ≈ ৮/০.০৪৫ ≈ ১৭৮/s, তিনটিতে ≈ ৫৩৩/s — ৩৮০/s-এ ব্যবহার প্রায় ৭১%, অংশ ৪-এর হাঁটুর আগে। পরে vmstat: si/so শূন্য, ফাঁকা RAM ~৩ GiB, iowait ১%; p99 ২১০ ms. শেষ কাজ: memory PSI আর swap-out-এ alert, আর config বদলের আগে মেমরির হিসাব।
কেস স্টাডির শিক্ষা

সবচেয়ে জোরালো লক্ষণ (load ১৭.৮, ডিস্ক ১০০%) মূল কারণ ছিল না। USE-এর তালিকা ধরে সব সম্পদ দেখায় দেখা গেল একটি সম্পদের saturation (মেমরি) আরেকটির (ডিস্ক) saturation তৈরি করছে। আর “CPU বাড়াই” বা “ডিস্ক দ্রুত করি” — দুটি আন্দাজই টাকা খরচ করত, সমস্যা মেটাত না।

আরও গভীরে — Kingman-এর সূত্র আর অনিয়মের দাম। M/M/1 ধরে নেয় আগমন আর সেবা “exponential” মাত্রায় এলোমেলো। সাধারণ G/G/1 সারির জন্য J. F. C. Kingman ১৯৬১ সালে একটি আনুমানিক সূত্র দেন: গড় অপেক্ষা ≈ (ρ/(1 − ρ)) × ((ca² + cs²)/2) × S, যেখানে ca আর cs হলো আগমন-ফাঁক আর সেবা-সময়ের coefficient of variation (মানক বিচ্যুতি ÷ গড়)। exponential-এ দুটোই ১, তাই M/M/1-এর ফল ফিরে আসে; সেবা-সময় স্থির হলে (cs = 0) অপেক্ষা অর্ধেক, আর burst-ভরা ট্রাফিকে (ca বড়) একই ρ-তে অপেক্ষা অনেক গুণ — Kingman's formula তাই বলে, “হাঁটু” কোথায় তা শুধু ব্যবহারের ওপর নয়, অনিয়মের ওপরও নির্ভর করে।

পরীক্ষা ও ইন্টারভিউতে এই বিষয়ে প্রায়ই যা জিজ্ঞেস করা হয়:

  • USE method কী? — প্রতিটি সম্পদের Utilization, Saturation আর Errors একে একে যাচাই; যেমন CPU-তে %busy, run queue > CPU-সংখ্যা, আর machine-check error.
  • M/M/1-এ μ = ৫০/s, λ = ৪০/s হলে গড় response time? — R = ১/(μ − λ) = ১/১০ s = ১০০ ms; ρ = ০.৮, L = ৪.
  • iowait বেশি মানে কি CPU ব্যস্ত? — না; CPU অলস ছিল আর তখন I/O অপেক্ষায় ছিল — ডিস্ক দেখুন।
  • “সার্ভার ধীর” — প্রথম কী করবেন? — লক্ষণ মাপা (rate, error, latency-র percentile), baseline-এর সাথে তুলনা, তারপর ৬০ সেকেন্ডের চেকলিস্ট আর USE; আন্দাজে বদল নয়।
  • Flame graph-এর x-অক্ষ কী দেখায়? — নমুনার অনুপাত (কত সময় CPU সেই stack-এ), সময়ের ক্রম নয়; চওড়া চ্যাপ্টা মাথা = বেশি self time.

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

AI একটি vmstat আউটপুটের কলাম বুঝিয়ে দিতে বা bpftrace-এর একটি লাইন লিখে দিতে সত্যিই কাজে লাগে। কিন্তু তার ভুলগুলো চেনা দরকার, কারণ ভুল সমাধান production সার্ভারে সরাসরি ক্ষতি করে।

AI-এর সাধারণ ভুলসমস্যাযাচাইয়ের পথ
লক্ষণ শুনেই “tuning”-এর তালিকা (swappiness, ulimit, sysctl)random change anti-methodআগে USE দিয়ে কারণ খুঁজুন; একবারে একটি বদল
“iowait বেশি, CPU বাড়ান”iowait অলস সময়iostat -xz-এ await, aqu-sz
NVMe-তে %util ১০০% দেখে “ডিস্ক ভরপুর”সমান্তরাল ডিভাইসে %util বিভ্রান্তিকরawait-এর প্রবণতা, IOPS বনাম ডিভাইস বা cloud-সীমা
production-এ strace -f চালাতে বলালক্ষ্য অনেক ধীর হতে পারেtest পরিবেশ, perf trace বা eBPF টুল, অল্প সময়
ভুল OS বা distro-র কমান্ড (macOS-এ free, Ubuntu-তে biolatency-এর ভুল নাম, পুরোনো bpftrace syntax)কমান্ড চলে না বা ভুল মানেman পাতা, টুলের --help, আপনার distro-র প্যাকেজ
queueing-এর হিসাবে ভুল (p99 = গড় × ২, percentile-এর গড়)ক্ষমতা-পরিকল্পনা ভুলসূত্র দিয়ে হাতে, বা ছোট স্ক্রিপ্টে আবার হিসাব
sudo দিয়ে echo 3 > drop_caches বা swap বন্ধ করার “দ্রুত সমাধান”লক্ষণ ঢাকে, cache হারিয়ে আরও ধীর, OOM-এর ঝুঁকিকারণ খুঁজুন; বদলের আগে-পরে মাপুন

AI-কে কাঁচা সংখ্যা দিন, সাথে আপনার যন্ত্রের পরিচয় আর baseline; আর তাকে দিয়েই অনুমান আর যাচাই আলাদা করান:

আমার Ubuntu 24.04 সার্ভার (VM, ৪ vCPU, ৮ GiB RAM, NVMe-ভিত্তিক cloud ডিস্ক) ধীর। স্বাভাবিক দিনে p99 ১৮০ ms, এখন ৪ সেকেন্ড। নিচে vmstat 1, iostat -xz 1, free -m আর dmesg-এর শেষ ২০ লাইন দিলাম। ১. USE method ধরে CPU, মেমরি, ডিস্ক, নেটওয়ার্ক — প্রতিটির U, S, E একটি টেবিলে দাও; কোন সংখ্যা থেকে কোন সিদ্ধান্ত, পাশে লেখো। ২. মূল কারণের ২–৩টি অনুমান দাও, প্রতিটির জন্য একটি যাচাই-কমান্ড আর প্রত্যাশিত ফল। ৩. প্রস্তাবিত প্রতিটি কমান্ড Ubuntu 24.04-এ চলবে কি না আর root লাগবে কি না বলো; production-এ ভারী কোনো টুল (strace) হলে সতর্ক করো। ৪. কোনো tuning-এর পরামর্শ দেওয়ার আগে কারণটি প্রমাণ করার পথ দেখাও।

AI-এর পারফরম্যান্স-পরামর্শ মানার আগে — চেকলিস্ট

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

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

প্র ০১প্রতিটি সার্ভারের p99 ভালো হলেও বড় সার্ভিসের ব্যবহারকারীরা কেন প্রায়ই ধীরতা টের পান?

২০১৩ সালে Google-এর Jeffrey Dean আর Luiz André Barroso Communications of the ACM-এ “The Tail at Scale” নামে একটি প্রবন্ধ লেখেন, যা এই প্রশ্নের ক্লাসিক উত্তর। একটি আধুনিক ওয়েব-পাতা বানাতে প্রায়ই একসাথে অনেক সার্ভারকে প্রশ্ন করা হয় — search-এর ক্ষেত্রে শত শত index-টুকরো, একটি e-commerce পাতায় দাম, স্টক, সুপারিশ, রিভিউ, বিজ্ঞাপনের আলাদা সার্ভিস। পাতাটি তৈরি হয় সবচেয়ে ধীর উত্তরটি আসার পরে।

অঙ্কটি সরল। ধরুন প্রতিটি সার্ভার ১% অনুরোধে ধীর (তার p99 খারাপ)। একটি পাতায় ১০০টি সার্ভার লাগলে অন্তত একটি ধীর হওয়ার সম্ভাবনা ১ − ০.৯৯১০০ ≈ ০.৬৩ — অর্থাৎ ৬৩% পাতাই ধীর! ১০টি সার্ভারে প্রায় ১০%. প্রতিটি সার্ভারের p99.9 খারাপ হলেও ১০০-র fan-out-এ প্রায় ১০% পাতা ধীর। একক সার্ভারের “বিরল” tail সিস্টেমের স্তরে সাধারণ অভিজ্ঞতা হয়ে যায়।

ধীরতার উৎসগুলো প্রায়ই OS-স্তরের: garbage collection-এর বিরতি, পেছনের কাজ (log compaction, backup), একটি CPU-তে জমা interrupt, cgroup-এর throttling, প্রতিবেশী VM-এর steal, swap-এ যাওয়া একটি page. প্রবন্ধটি এগুলো পুরো দূর করার বদলে “tail-tolerant” কৌশলের কথা বলে: একটি অনুরোধ একটু দেরি করলে আরেকটি কপিকে পাঠানো (hedged request), কাজ ছোট টুকরোয় ভাগ করা যাতে একটি ধীর টুকরো কম ক্ষতি করে, আর ধীর যন্ত্রকে সাময়িকভাবে সরিয়ে রাখা।

পারফরম্যান্স-মাপের জন্য শিক্ষা হলো: গড় তো নয়ই, একক সার্ভারের p99-ও যথেষ্ট নয়; ব্যবহারকারীর দিক থেকে পুরো অনুরোধের percentile মাপুন (RED-এর D), আর histogram দেখুন — tail-এর আকৃতি প্রায়ই দেখিয়ে দেয় কোন ধরনের বিরতি দায়ী।

মূল উপলব্ধি: fan-out-এর জগতে “বিরল” ঘটনা আর বিরল থাকে না। tail latency একটি সিস্টেম-স্তরের বৈশিষ্ট্য — একে মাপতে হয় ব্যবহারকারীর দিক থেকে, আর কমাতে হয় নকশা দিয়ে, শুধু টিউনিং দিয়ে নয়।

প্র ০২Flame graph এত দ্রুত সারা শিল্পে ছড়াল কেন? একটি ছবি কী এমন করল যা লেখা রিপোর্ট পারেনি?

২০১১ সালে Brendan Gregg একটি MySQL পারফরম্যান্স-সমস্যা দেখছিলেন। profiler-এর আউটপুট ছিল হাজার হাজার লাইনের লেখা — প্রতিটি আলাদা call stack, পাশে একটি সংখ্যা। কোন পথে বেশিরভাগ সময় যাচ্ছে, তা বুঝতে স্ক্রল করতে করতে দিন যায়। তিনি প্রথমে stack-গুলোকে এক লাইনে “ভাঁজ” করে (একই stack গুনে একসাথে) তারপর স্তূপের মতো আঁকার ধারণা আনেন — আর এর নাম দেন flame graph, কারণ উষ্ণ রঙে আঁকা স্তূপগুলো আগুনের শিখার মতো দেখায়। তাঁর ব্লগ আর খোলা স্ক্রিপ্ট (FlameGraph) দিয়ে এটি দ্রুত ছড়ায়।

ছবিটির শক্তি তিনটি নকশা-সিদ্ধান্তে। এক, প্রস্থ = নমুনার অনুপাত, তাই চোখ সাথে সাথে বড় খরচ খোঁজে — মানুষের দৃষ্টি দৈর্ঘ্যের তুলনায় খুব দক্ষ। দুই, x-অক্ষে সময় নয়, বর্ণানুক্রম; ফলে একই stack-গুলো পাশাপাশি জোড়া লাগে, আর একটি ফাংশনের সব ডাক একটি চওড়া খণ্ডে আসে। তিন, stack-এর পুরো প্রসঙ্গ রাখা: শুধু “regex_compile দামি” নয়, “compute_price থেকে ডাকা regex_compile দামি” — যা সমাধানের দিক দেখায়।

এরপর এসেছে অনেক রূপ: off-CPU flame graph (thread কোথায় অপেক্ষা করল), memory flame graph (কোথায় মেমরি বরাদ্দ), differential flame graph (দুই সংস্করণের তুলনা), আর icicle (উল্টো) রূপ। Java-র async-profiler, Chrome DevTools, Windows-এর WPA আর অনেক IDE ও cloud profiler এই চিত্ররূপ গ্রহণ করেছে। ধারণাটি নতুন সংকেত মাপেনি — perf, DTrace আগে থেকেই stack-নমুনা দিত — বরং বিদ্যমান ডেটাকে মানুষের বোঝার উপযোগী করেছে।

সীমাও আছে: on-CPU flame graph অপেক্ষা দেখায় না, নমুনা কম হলে ছোট খরচ হারিয়ে যায়, আর JIT-ভাষায় (Java, Node.js) symbol না পেলে ফ্রেমগুলো অর্থহীন ঠিকানা হয় — তখন বাড়তি সেটিং লাগে।

মূল উপলব্ধি: observability-তে ডেটা সংগ্রহ অর্ধেক কাজ; বাকি অর্ধেক হলো এমনভাবে দেখানো যাতে মানুষ সেকেন্ডে সিদ্ধান্ত নিতে পারেন। একটি ভালো visualisation নিজেই একটি পারফরম্যান্স-টুল।

প্র ০৩“CPU ১০০%” বা “CPU ৯০%” — এই সংখ্যা আসলে কতটা বিশ্বাসযোগ্য?

CPU utilization-এর সংজ্ঞা ঐতিহাসিক: সময়ের কত অংশ CPU idle task চালাচ্ছিল না। ১৯৭০-এর দশকের CPU-তে, যখন মেমরি আর CPU-র গতি কাছাকাছি ছিল, এটি “কতটা কাজ হচ্ছে”-র ভালো মাপ ছিল। কিন্তু গত কয়েক দশকে CPU-র গতি মেমরির চেয়ে অনেক বেশি বেড়েছে — একে প্রায়ই “memory wall” বলা হয়। আজ একটি cache miss-এ শত cycle নষ্ট হতে পারে, আর সেই থেমে থাকা সময়ও “busy” হিসেবে গোনা হয়। ২০১৭ সালে Brendan Gregg “CPU Utilization is Wrong” শিরোনামে একটি ব্লগ-লেখায় এই কথাটিই জোর দিয়ে বলেন, আর IPC দেখার পরামর্শ দেন — এই পর্বের C প্রোগ্রামে pointer-chase-এ cpu/wall ৯৩% অথচ প্রতি load-এ দুশো ns-এর বেশি, ঠিক এই ঘটনা।

আরও কয়েকটি ফাঁদ আছে। Hyper-threading (SMT)-এ দুটি logical CPU একটি core-এর অংশ ভাগ করে; দুটোই “১০০%” হলেও ক্ষমতা দ্বিগুণ নয়। CPU-র ঘড়ি নিজেই বদলায় (power বা তাপের কারণে turbo বা throttling) — একই ৫০%-এর মানে ভিন্ন দিনে ভিন্ন। VM-এ steal সময় অনেক টুল আলাদা দেখায়, কেউ কেউ দেখায়ই না। আর এক সেকেন্ডের গড় ১০ ms-এর burst লুকায়: এক সেকেন্ডে ৫০% মানে হতে পারে ৫০০ ms একটানা ১০০%, যার মধ্যে আসা অনুরোধ লাইনে দাঁড়িয়েছে।

queueing-এর দিক থেকেও “৯০% তো এখনো জায়গা আছে” ভুল ধারণা — অংশ ৪-এর হিসাবে ৯০%-এ গড় response time সেবা-সময়ের ১০ গুণ। তাই CPU utilization-এর সাথে সবসময় saturation (run queue, PSI, runqlat) আর ফল (latency) দেখুন।

তাহলে কি সংখ্যাটি অর্থহীন? না; এটি একটি প্রথম সংকেত, বিশেষ করে idle-এর দিক থেকে: CPU ২০% হলে CPU প্রায় নিশ্চিতভাবে সমস্যা নয়। কিন্তু বেশি মানের ব্যাখ্যায় IPC (CPU কি কাজ করছে নাকি অপেক্ষা), saturation (লাইন আছে কি), আর সময়ের সূক্ষ্মতা (burst) লাগে।

মূল উপলব্ধি: একটি সংখ্যার নামের চেয়ে তার সংজ্ঞা গুরুত্বপূর্ণ। “busy” মানে “কাজ হচ্ছে” নয়, আর “জায়গা আছে” মানে “latency ভালো থাকবে” নয় — প্রতিটি মাপকে তার পাশের মাপের সাথে মিলিয়ে পড়তে হয়।

প্র ০৪মাপতে গেলেই সিস্টেম বদলে যায় — DTrace থেকে eBPF পর্যন্ত tracing কীভাবে এই সমস্যার সাথে লড়েছে?

পদার্থবিদ্যায় observer effect-এর কথা সবাই জানেন; সফটওয়্যারে এটি আরও সরাসরি। যে বাগ debugger বা trace লাগালে উধাও হয়ে যায়, প্রোগ্রামাররা ঠাট্টা করে তাকে “Heisenbug” বলেন — প্রায়ই এটি race condition, যেখানে টুলের ধীরতা thread-গুলোর সময় বদলে দেয়। পারফরম্যান্সে বিপদ আরও বড়: strace-এর মতো ptrace-ভিত্তিক টুল প্রতিটি syscall-এ লক্ষ্যকে থামায়, তাই ধীর সার্ভার আরও ধীর হয়, আর মাপা সংখ্যা বাস্তব থেকে সরে যায়।

২০০৩–২০০৫ সালের দিকে Sun Microsystems-এ Bryan Cantrill, Mike Shapiro আর Adam Leventhal DTrace তৈরি করেন (২০০৪ সালের USENIX সম্মেলনে এর গবেষণাপত্র, Solaris 10-এ প্রকাশ)। মূল ধারণা: production সিস্টেমে চালু রাখা যায় এমন dynamic tracing — probe বন্ধ থাকলে খরচ প্রায় শূন্য, আর চালু হলে kernel-এর ভেতরেই গোনা আর সারাংশ বানানো, যাতে প্রতি ঘটনায় user space-এ যেতে না হয়। নিরাপত্তার জন্য probe-এর ভাষা সীমিত — লুপ নেই, kernel নষ্ট করার উপায় নেই। DTrace পরে FreeBSD আর macOS-এ যায়।

Linux-এ দীর্ঘদিন সমতুল্য কিছু মূলধারায় ছিল না (SystemTap ছিল, কিন্তু kernel module বানাত)। ২০১৪ সালের দিকে Alexei Starovoitov-এর কাজে পুরোনো BPF বড় হয়ে eBPF হয়, আর পরের কয়েক বছরে kprobe, tracepoint, perf event-এ যুক্ত হয়। verifier নিশ্চিত করে প্রোগ্রাম থামবে আর নিরাপদ মেমরি ছোঁবে; map-এ kernel-এর ভেতরেই গোনা আর histogram হয়। Brendan Gregg-সহ অনেকে BCC আর bpftrace-এ শত টুল লেখেন — bpftrace-এর ভাষা খোলাখুলিভাবে DTrace আর awk থেকে অনুপ্রাণিত।

তবু খরচ শূন্য নয়: প্রতি সেকেন্ডে লাখো ঘটনায় (প্রতিটি syscall, প্রতিটি প্যাকেট) probe লাগালে ভার টের পাওয়া যায়। তাই কৌশল হলো আগে sampling (সস্তা, নির্দিষ্ট হার), তারপর লক্ষ্যভেদী tracing — শুধু দরকারি ঘটনা, ফিল্টারসহ, অল্প সময়।

মূল উপলব্ধি: ভালো observability-টুলের মাপকাঠি শুধু “কী দেখায়” নয়, “দেখাতে গিয়ে কতটা বদলায়”। kernel-এর ভেতরে সারাংশ বানানো আর নিরাপত্তা-যাচাই — এই দুই ধারণাই production-এ নির্ভয়ে মাপা সম্ভব করেছে।

প্র ০৫“Monitoring” আর “observability” — শুধু নতুন বিপণনের শব্দ, নাকি আসল পার্থক্য আছে? শব্দটি এল কোথা থেকে?

শব্দটির উৎস control theory. ১৯৬০ সালে Rudolf E. Kálmán একটি গতিশীল ব্যবস্থার “observability” সংজ্ঞায়িত করেন: বাইরের output দেখে ভেতরের পুরো অবস্থা নির্ণয় করা যায় কি না। ২০১০-এর দশকের মাঝামাঝি সফটওয়্যার-জগতে শব্দটি জনপ্রিয় হয় — বিশেষ করে বিতরণকৃত ব্যবস্থা আর microservice-এর প্রসঙ্গে, Twitter-এর প্রকৌশলীদের লেখা আর পরে Charity Majors-এর মতো প্রকৌশলীদের প্রচারে।

পার্থক্যটা আসল। পুরোনো monitoring আগে থেকে জানা প্রশ্নের উত্তর দেয়: “CPU ৯০% পেরোলে alert”, “disk ভরলে alert”. একটি যন্ত্রে একটি অ্যাপ থাকলে এটি যথেষ্ট ছিল — সমস্যার ধরন সীমিত। কিন্তু শত সার্ভিসের ব্যবস্থায় প্রায় প্রতিটি নতুন সমস্যাই অভূতপূর্ব: “শুধু Android অ্যাপের, শুধু একটি অঞ্চলের, শুধু নতুন সংস্করণের ব্যবহারকারীদের checkout ধীর।” আগে বানানো dashboard-এ এই কাটাছেঁড়া নেই। Observability মানে এমন ধনী সংকেত রাখা (অনেক বৈশিষ্ট্যওয়ালা ঘটনা, trace, profile), যাতে আগে না ভাবা প্রশ্নও পরে জিজ্ঞেস করা যায়।

এর কারিগরি ভিত্তি এসেছে কয়েক ধাপে। ২০১০ সালে Google-এর Dapper গবেষণাপত্র বিতরণকৃত tracing-এর নকশা দেখায় — একটি অনুরোধ কোন কোন সার্ভিসে কতক্ষণ। Zipkin, Jaeger-এর মতো খোলা প্রকল্প আসে; ২০১৯ সালে OpenTracing আর OpenCensus মিলে OpenTelemetry হয়, যা metric, log আর trace সংগ্রহের একটি খোলা মানদণ্ড। আর এই পর্বে দেখা “চতুর্থ স্তম্ভ” — continuous profiling — eBPF-এর কারণে এখন production-এ সারাক্ষণ চালু রাখা সম্ভব।

তবু শব্দটিকে জাদুমন্ত্র ভাববেন না। যে দল baseline রাখে না, USE বা RED-এর মতো পদ্ধতি জানে না, তাদের কাছে প্রচুর ডেটাও শুধু শব্দ। আর প্রতিটি সংকেত সংগ্রহের খরচ আছে — সংরক্ষণ, নেটওয়ার্ক, গোপনীয়তা।

মূল উপলব্ধি: monitoring জানা প্রশ্নের উত্তর দেয়, observability অজানা প্রশ্নের উত্তর খোঁজার সামর্থ্য দেয়। দুটোই লাগে, আর দুটোর ভিত্তি একই — OS আর অ্যাপ যে সংকেত খুলে রাখে, আর সেগুলো পড়ার একটি শৃঙ্খলাবদ্ধ পদ্ধতি।

অনুশীলন

খাতায় হিসাব করুন বা নিজের Linux-এ চালান, তারপর উত্তর মেলান।

  1. হিসাব — M/M/1: একটি API সার্ভার (একক worker ধরে) প্রতিটি অনুরোধে গড়ে ২০ ms নেয়, আর অনুরোধ আসে এলোমেলোভাবে সেকেন্ডে গড়ে ৪০টি। (ক) ρ, গড় response time R, সিস্টেমে গড় অনুরোধ L আর p99 কত? Little's law দিয়ে L যাচাই করুন। (খ) ট্রাফিক বেড়ে সেকেন্ডে ৪৫টি হলে R আর p99?

    (ক) μ = ১/০.০২০ = ৫০/s, λ = ৪০/s ⇒ ρ = ০.৮. R = ১/(μ − λ) = ১/১০ s = ১০০ ms. L = ρ/(১ − ρ) = ০.৮/০.২ = ৪. যাচাই: λ × R = ৪০ × ০.১ = ৪ — মিলেছে। p99 = −ln(০.০১)/(μ − λ) = ৪.৬০৫/১০ s ≈ ৪৬১ ms. (p95 ≈ ৩.০/১০ = ৩০০ ms.)

    (খ) λ = ৪৫ ⇒ ρ = ০.৯, μ − λ = ৫ ⇒ R = ২০০ ms, p99 = ৪.৬০৫/৫ ≈ ৯২১ ms. ট্রাফিক মাত্র ১২.৫% বেড়েছে, কিন্তু গড় আর p99 দুটোই দ্বিগুণ — 1/(1−ρ)-এর বিস্ফোরণ।

  2. হাতে হিসাব — /proc/stat-এর পার্থক্য: একটি ৪-CPU VM-এ ১ সেকেন্ড ব্যবধানে /proc/stat-এর cpu লাইনের পার্থক্য (tick-এ) পাওয়া গেল: user ১২০, nice ০, system ৪০, idle ১৬০, iowait ৬০, irq ০, softirq ৪, steal ১৬. user%, sys% (system + irq + softirq), iowait%, steal%, idle% আর মোট ব্যস্ততা (U) কত? কী সিদ্ধান্ত নেবেন?

    মোট = ১২০ + ০ + ৪০ + ১৬০ + ৬০ + ০ + ৪ + ১৬ = ৪০০ tick (৪টি CPU × সেকেন্ডে ১০০ tick — মিলে গেছে)। user = ১২০/৪০০ = ৩০%, sys = (৪০ + ০ + ৪)/৪০০ = ১১%, iowait = ৬০/৪০০ = ১৫%, steal = ১৬/৪০০ = ৪%, idle = ৪০%. U (idle আর iowait বাদে সব) = (৪০০ − ১৬০ − ৬০)/৪০০ = ৪৫%.

    সিদ্ধান্ত: CPU ভরপুর নয় (৪৫%), তাই CPU বাড়ানোর কারণ নেই। iowait ১৫% ইঙ্গিত দিচ্ছে ডিস্ক দেখতে (iostat -xz 1). steal ৪% — কিছুটা প্রতিবেশীর ভিড়; নিয়মিত বাড়তে থাকলে প্রদানকারী বা instance-ধরন নিয়ে ভাবুন। এই পর্বের স্ক্রিপ্টও ঠিক এই হিসাব করে।

  3. কমান্ড লিখুন: sysstat বা root ছাড়া, শুধু /proc/vmstat পড়ে এমন একটি bash লুপ লিখুন, যা ৫ সেকেন্ড ধরে প্রতি সেকেন্ডে পুরো যন্ত্রের major page fault আর swap-out (page/s) ছাপে। কোন মান দেখলে চিন্তিত হবেন?
    prev=$(awk '$1=="pgmajfault"{m=$2} $1=="pswpout"{o=$2} END{print m, o}' /proc/vmstat)
    for i in 1 2 3 4 5; do
      sleep 1
      cur=$(awk '$1=="pgmajfault"{m=$2} $1=="pswpout"{o=$2} END{print m, o}' /proc/vmstat)
      set -- $prev $cur
      echo "major faults/s: $(( $3 - $1 ))   swap-out pages/s: $(( $4 - $2 ))"
      prev=$cur
    done

    counter বুটের পর থেকে বাড়তেই থাকে, তাই দুটি নমুনার পার্থক্য নিতে হয় — vmstat, sar ঠিক এটাই করে। চিন্তার সংকেত: swap-out নিয়মিত শূন্যের বেশি, সাথে major fault-এর স্থির স্রোত — page বের করে আবার আনা হচ্ছে, thrashing-এর পথে। প্রোগ্রাম চালুর সময় অল্প কিছু major fault স্বাভাবিক।

  4. আউটপুট পড়ুন — কোন ডিস্ক ভরপুর? দুটি যন্ত্রে iostat -xz 1: (ক) একটি NVMe SSD: r/s ২০,০০০, r_await ০.১ ms, aqu-sz ২.০, %util ১০০. (খ) একটি HDD: r/s ২০০, r_await ৪০ ms, aqu-sz ৮.০, %util ১০০. Little's law দিয়ে aqu-sz যাচাই করুন, আর বলুন কোনটি সত্যিই saturated.

    Little's law: aqu-sz ≈ IOPS × await. (ক) ২০,০০০ × ০.০০০১ s = ২.০ — মিলেছে। (খ) ২০০ × ০.০৪০ s = ৮.০ — মিলেছে।

    (ক) সম্ভবত ভরপুর নয়। %util ১০০ শুধু বলে প্রায় সবসময় অন্তত একটি অনুরোধ চলছিল; NVMe একসাথে বহু অনুরোধ সামলায়, আর await মাত্র ০.১ ms — কোনো লাইনের লক্ষণ নেই। ট্রাফিক বাড়ালে await প্রায় একই থেকে IOPS বাড়বে কি না, সেটিই আসল পরীক্ষা। (খ) ভরপুর। একটি HDD সাধারণত সেকেন্ডে কয়েকশো এলোমেলো I/O পারে (পর্ব ৪০); এখানে গড়ে ৮টি অনুরোধ লাইনে, প্রতিটি ৪০ ms অপেক্ষা — এর বেশি কাজ এলে await আরও বাড়বে।

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

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

ক্রমএকটি ধীর সার্ভিসের তদন্তের ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. 🚨 লক্ষণ মাপা — rate, error আর latency-র percentile (RED)
  2. 📏 baseline-এর সাথে তুলনা — কোন সংখ্যা অস্বাভাবিক
  3. 🧰 USE — প্রতিটি সম্পদের utilization, saturation, errors
  4. 🔬 অনুমান নিয়ে গভীর টুল — perf, flame graph, eBPF
  5. ✅ একটি পরিবর্তন, তারপর একই সংখ্যা আবার মাপা

USE method-এ CPU-র “saturation”-এর সবচেয়ে ভালো উদাহরণ কোনটি?

  • mpstat-এ %idle ৫%
  • dmesg-এ একটি machine-check error
  • vmstat-এর r কলাম নিয়মিত CPU-সংখ্যার চেয়ে বেশি
  • top-এ wa ৩০%

একটি M/M/1 সারিতে সেবা-হার স্থির রেখে ব্যবহার ρ = ০.৫ থেকে ০.৯ করা হলো। গড় response time কত গুণ হবে?

  • ১.৮ গুণ
  • ৫ গুণ
  • ১০ গুণ
  • অসীম, সারি ভেঙে পড়ে

একটি Linux সার্ভারে top দেখাচ্ছে: us ১০%, sy ৫%, wa ৬০%, id ২৫%. সবচেয়ে যুক্তিসঙ্গত পরের পদক্ষেপ কোনটি?

  • আরও CPU যোগ করা, কারণ CPU ৭৫% ব্যস্ত
  • strace দিয়ে সব system call ধরা
  • নেটওয়ার্ক দলকে জানানো
  • iostat -xz 1 দিয়ে ডিস্কের await আর সারি দেখা

একটি cloud VM-এ নিজের কোনো পরিবর্তন ছাড়াই অ্যাপ ধীর হয়েছে, আর top-এ st (steal) ২৫%. এর সবচেয়ে সম্ভাব্য অর্থ কী?

  • hypervisor ভৌত CPU অন্য VM-কে দিচ্ছে, তাই এই VM সময় হারাচ্ছে
  • অ্যাপের কোড বেশি CPU খাচ্ছে
  • ডিস্কের সারি লম্বা হয়েছে
  • kernel বেশি system call সামলাচ্ছে

একটি CPU flame graph-এ x-অক্ষ বরাবর একটি ফ্রেমের প্রস্থ কী বোঝায়?

  • ফাংশনটি কখন চলেছিল — বাঁয়ে আগে, ডানে পরে
  • ফাংশনটি stack-এর কত গভীরে ছিল
  • কত শতাংশ নমুনায় ফাংশনটি (বা তার ডাকা ফাংশন) stack-এ ছিল
  • ফাংশনটি কতবার ডাকা হয়েছিল

একটি NVMe SSD-তে iostat দেখাচ্ছে %util ১০০, কিন্তু r_await ০.১ ms আর aqu-sz ২. কোন সিদ্ধান্ত সবচেয়ে ঠিক?

  • ডিস্ক নিশ্চিতভাবে ভরপুর, এখনই বদলাতে হবে
  • %util এখানে বিভ্রান্তিকর; কম await আর ছোট সারি বলছে ক্ষমতা সম্ভবত বাকি
  • ডিস্কে hardware error হচ্ছে
  • CPU ১০০% ব্যস্ত

শব্দকোষ

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

মনে রাখুন

  1. পদ্ধতি আগে: streetlight আর random change নয়; প্রশ্ন → অনুমান → মাপ → একটি পরিবর্তন → আবার মাপ, আর হাতে সবসময় baseline.
  2. USE সম্পদের জন্য, RED সার্ভিসের জন্য: CPU, মেমরি, ডিস্ক, নেটওয়ার্কে utilization, saturation, errors; সার্ভিসে rate, errors, duration (percentile).
  3. Queueing: M/M/1-এ R = S/(1 − ρ), p-তম percentile = −ln(1 − p)/(μ − λ); ৯০%-এ গড় ১০ গুণ, ৯৯%-এ ১০০ গুণ — latency-সংবেদনশীল সম্পদে হাতে জায়গা রাখুন।
  4. সংখ্যার ফাঁদ: iowait অলস সময়, steal প্রতিবেশীর ভিড়, load average-এ D-অবস্থা, NVMe-তে %util, “CPU ১০০%”-এর ভেতরে মেমরির অপেক্ষা (IPC দেখুন), আর percentile-এর গড় হয় না।
  5. টুলের সিঁড়ি: ৬০ সেকেন্ডের চেকলিস্ট → pidstat/iostat → perf আর flame graph (চওড়া চ্যাপ্টা মাথা খুঁজুন) → eBPF; strace কার্যকর কিন্তু লক্ষ্যকে অনেক ধীর করতে পারে।

পরের পর্বে: এতক্ষণ বিশাল Linux-কে বাইরে থেকে মেপেছেন; এবার একটি ছোট, পুরোটা পড়ে ফেলা যায় এমন OS-এর ভেতরে ঢোকা। পর্ব ৫৮-এ xv6 — একটি শিক্ষণ-OS-এর ভেতরে ঘোরা।

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

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