পাঠ ০৩ · ৫১-এর মধ্যে · মডিউল ১
Home / Courses / System Design / Latency ও Throughput

Latency ও Throughput — পারফরম্যান্স মেট্রিক্স

Latency, throughput & percentiles
৮ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

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

  • Latency ও Throughput-এর সংজ্ঞা এবং এদের মধ্যেকার পার্থক্য
  • Percentile (p50/p95/p99/p99.9) কী এবং কেন এগুলো গড়ের চেয়ে বেশি তথ্যবহুল
  • একটি ওয়ার্কড উদাহরণ হাতে ও কোডে সমাধান করে দেখা কীভাবে টেইল লেটেন্সি লুকিয়ে থাকে
  • এভরি-ইঞ্জিনিয়ার-শুড-নো লেটেন্সি সংখ্যাগুলোর একটি রেফারেন্স টেবিল

১ · Latency বনাম Throughput

এই দুটো টার্ম প্রায়ই গুলিয়ে ফেলা হয়, কিন্তু তারা ভিন্ন জিনিস মাপে।

Latency
একটি একক রিকোয়েস্ট সম্পন্ন হতে কত সময় লাগে (ms)। "আমার রিকোয়েস্টের উত্তর কত দ্রুত এলো?"
Throughput
সিস্টেম প্রতি সেকেন্ডে মোট কতগুলো রিকোয়েস্ট সামলাতে পারে (QPS/RPS)। "সিস্টেমটি একসাথে কতজনকে সার্ভ করতে পারে?"

এই দুটো স্বাধীন নয় কিন্তু সমানুপাতিকও নয়। উদাহরণ — একটি সিস্টেমে প্রতিটি রিকোয়েস্টের লেটেন্সি বেশি (ধীর) হলেও, যদি অনেকগুলো রিকোয়েস্ট প্যারালালভাবে প্রসেস করা যায় (একাধিক সার্ভার, M3), তাহলে সামগ্রিক থ্রুপুট বেশি থাকতে পারে। উল্টোভাবে, একটি সিস্টেমের লেটেন্সি খুব কম হলেও যদি সিরিয়ালি (একটার পর একটা) প্রসেস করতে হয়, থ্রুপুট কম থাকবে।

মূল পার্থক্য

লেটেন্সি কমানো (দ্রুত উত্তর) এবং থ্রুপুট বাড়ানো (বেশি রিকোয়েস্ট একসাথে) — দুটোই লক্ষ্য, কিন্তু আলাদা কৌশল লাগে। লেটেন্সি কমাতে ক্যাশিং (M5), কাছাকাছি সার্ভার (CDN, L20) লাগে। থ্রুপুট বাড়াতে হরাইজন্টাল স্কেলিং (M3), লোড ব্যালেন্সিং (L11) লাগে।

২ · কেন গড় লেটেন্সি বিভ্রান্তিকর

ধরুন আপনার সার্ভিসের "গড় রেসপন্স টাইম ৩০ms" — শুনতে চমৎকার লাগে। কিন্তু ১০,০০০ ইউজারের মধ্যে যদি ১০০ জনের রেসপন্স টাইম ২ সেকেন্ড হয় (আর বাকিদের ১০ms), গড় হিসাবে সেটা লুকিয়ে যায় — অথচ ওই ১০০ জন ইউজারের অভিজ্ঞতা ভয়ংকর খারাপ। PercentilePercentileডেটাকে ছোট থেকে বড় সাজিয়ে, নির্দিষ্ট শতাংশ পয়েন্টে যে মান পাওয়া যায় — যেমন p99 মানে ৯৯% রিকোয়েস্ট এর চেয়ে দ্রুত বা সমান, মাত্র ১% এর চেয়ে ধীর। এই সমস্যার সমাধান — গড়ের বদলে "কত শতাংশ রিকোয়েস্ট X ms-এর মধ্যে শেষ হয়" জিজ্ঞেস করা।

p50 (মিডিয়ান)
৫০% রিকোয়েস্ট এর চেয়ে দ্রুত — "সাধারণ" ইউজারের অভিজ্ঞতা।
p95 / p99
৯৫%/৯৯% রিকোয়েস্ট এর চেয়ে দ্রুত — বাকি ৫%/১% "খারাপ অভিজ্ঞতা" পাওয়া ইউজার।
p99.9
৯৯.৯% রিকোয়েস্ট এর চেয়ে দ্রুত — সবচেয়ে "টেইল" আউটলায়ার ধরে ফেলে, বড় সিস্টেমে এই ০.১% হাজার হাজার ইউজার হতে পারে।

৩ · ওয়ার্কড উদাহরণ — গড় বনাম Percentile

ধরুন ১০০০টি রিকোয়েস্টের একটি ডেটাসেট — ৯৯০টি রিকোয়েস্ট ১০ms নেয়, আর ১০টি রিকোয়েস্ট (কোনো কারণে ধীর — হয়তো ক্যাশ মিস বা GC পজ) ২০০০ms নেয়। হাতে হিসাব করলে —

  • গড় = (৯৯০ × ১০ + ১০ × ২০০০) / ১০০০ = (৯৯০০ + ২০০০০) / ১০০০ = ২৯.৯ms
  • p50 = সাজানো তালিকার ৫০০তম মান = ১০ms (কারণ প্রথম ৯৯০টিই ১০ms)
  • p99 = সাজানো তালিকার ৯৯০তম মান = ১০ms (ঠিক সীমানায়, তখনো "১০ms" গ্রুপের মধ্যে)
  • p99.9 = সাজানো তালিকার ৯৯৯তম মান = ২০০০ms (এটি শেষের ১০টি ধীর রিকোয়েস্টের মধ্যে পড়ে যায়)

লক্ষ্য করুন — গড় (২৯.৯ms) এবং এমনকি p99 (১০ms) দুটোই ধীর ১% রিকোয়েস্টের প্রকৃত ভয়াবহতা (২ সেকেন্ড!) লুকিয়ে ফেলে। শুধু p99.9 গিয়েই সেই টেইল ধরা পড়ে — এটাই "tail latency" বোঝার গুরুত্ব।

Python
import math

# ৯৯০টি রিকোয়েস্ট ১০ms, ১০টি রিকোয়েস্ট ২০০০ms — মোট ১০০০টি
latencies = [10] * 990 + [2000] * 10
n = len(latencies)

average = sum(latencies) / n
sorted_latencies = sorted(latencies)

def percentile(sorted_data, p):
    # p শতাংশে সাজানো তালিকার (1-indexed) অবস্থান বের করা
    rank = math.ceil((p / 100) * len(sorted_data))
    idx = min(rank, len(sorted_data)) - 1   # 0-indexed
    return sorted_data[idx]

p50 = percentile(sorted_latencies, 50)
p99 = percentile(sorted_latencies, 99)
p999 = percentile(sorted_latencies, 99.9)

print(f"মোট রিকোয়েস্ট: {n}")
print(f"গড় লেটেন্সি: {average:.1f}ms")
print(f"p50: {p50}ms")
print(f"p99: {p99}ms")
print(f"p99.9: {p999}ms")

assert round(average, 1) == 29.9
assert p50 == 10 and p99 == 10 and p999 == 2000

    
বাস্তব সিস্টেমে (গুগল, নেটফ্লিক্স, আমাজন) সবসময় SLO/SLA (L42) নির্দিষ্ট করা হয় p99 বা p99.9-এর ভিত্তিতে, গড়ের ভিত্তিতে নয় — কারণ যেকোনো একজন ইউজারের একটি ধীর রিকোয়েস্টও "সিস্টেম ধীর" এই ধারণা তৈরি করতে পারে, বিশেষ করে যখন লাখো ইউজার প্রতিদিন হাজারো রিকোয়েস্ট করে (০.১% আউটলায়ারও তখন হাজার হাজার প্রকৃত মানুষ)।

৪ · এভরি-ইঞ্জিনিয়ার-শুড-নো লেটেন্সি সংখ্যা

এই আনুমানিক সংখ্যাগুলো (শিল্পে সুপরিচিত, নির্দিষ্ট মেশিন-নির্ভর নয়) মাথায় থাকলে কোন অপারেশন কত "দামি" তা দ্রুত বোঝা যায়:

L1 cache reference
~১ ন্যানোসেকেন্ড (ns)
মেইন মেমরি (RAM) রেফারেন্স
~১০০ ns
SSD র‍্যান্ডম রিড
~১০০ মাইক্রোসেকেন্ড (০.১ms)
একই ডেটাসেন্টার রাউন্ড ট্রিপ
~০.৫ms
ডিস্ক সিক (HDD)
~১০ms
ক্রস-কন্টিনেন্ট রাউন্ড ট্রিপ
~১৫০ms

এই সংখ্যাগুলো থেকেই বোঝা যায় কেন ক্যাশিং (RAM/SSD থেকে সার্ভ করা) ডিস্ক বা নেটওয়ার্ক কলের চেয়ে বহুগুণ দ্রুত, এবং কেন CDN (L20) ইউজারের কাছাকাছি সার্ভার থেকে সার্ভ করে ক্রস-কন্টিনেন্ট রাউন্ড ট্রিপের বিশাল খরচ এড়ায়।

মূল কথা · Key takeaway

গড় লেটেন্সি একটি একক সংখ্যায় পুরো বিতরণ (distribution) সংকুচিত করে ফেলে এবং আউটলায়ার লুকিয়ে ফেলে। Percentile, বিশেষত টেইল percentile (p99, p99.9), আসল ইউজার অভিজ্ঞতা বুঝতে অনেক বেশি নির্ভরযোগ্য। L42-এ আমরা দেখব কীভাবে এই percentile-গুলোর উপর ভিত্তি করে SLO নির্ধারণ করা হয়।

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

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

প্র ০১ একটি সিস্টেমের p50 এবং p99.9 লেটেন্সির মধ্যে বিশাল ব্যবধান থাকলে (যেমন p50=10ms, p99.9=2000ms) এর সম্ভাব্য কারণ কী হতে পারে?

এটি সাধারণত ইঙ্গিত দেয় কিছু রিকোয়েস্ট একটি ভিন্ন, ধীর পথ দিয়ে যাচ্ছে — যেমন ক্যাশ মিস (M5, ধীর ডেটাবেস কল লাগছে), গার্বেজ কালেকশন পজ, নির্দিষ্ট একটি "hot" শার্ডে ওভারলোড (L17), বা নেটওয়ার্কের একটি নির্দিষ্ট রুটে সাময়িক কনজেশন। p50 আর p99.9-এর মধ্যে বড় ফারাক থাকা মানে সমস্যা "সবার জন্য সমান" নয় — বরং একটি নির্দিষ্ট উপসেট রিকোয়েস্টে ঘটছে, যা ডিবাগ করার জন্য ডিস্ট্রিবিউটেড ট্রেসিং (L41) দরকার হয়।

প্র ০২ থ্রুপুট বাড়ানোর জন্য বেশি সার্ভার যোগ করলে কি লেটেন্সিও স্বয়ংক্রিয়ভাবে কমে যায়?

না, সবসময় নয়। বেশি সার্ভার মানে সিস্টেম একসাথে বেশি রিকোয়েস্ট সামলাতে পারবে (থ্রুপুট বাড়ে), কিন্তু একটি একক রিকোয়েস্টের লেটেন্সি (যেমন একটি ডেটাবেস কোয়েরি সম্পন্ন হতে কত সময় লাগে) তার উপর নির্ভর করে না — বরং নির্ভর করে ওই একক রিকোয়েস্টের পথে থাকা প্রতিটি ধাপের গতির (নেটওয়ার্ক, ডেটাবেস, ক্যাশ) উপর। লেটেন্সি কমাতে ভিন্ন কৌশল (ক্যাশিং, দ্রুততর অ্যালগরিদম, কাছাকাছি সার্ভার) লাগে, শুধু বেশি সার্ভার যোগ করলেই হয় না।

প্র ০৩ ইন্টারভিউতে "আমাদের সিস্টেমের গড় লেটেন্সি ২০ms" বললে একজন ভালো ইন্টারভিউয়ার কী প্রশ্ন করবেন?

সম্ভবত জিজ্ঞেস করবেন "p99 কত?" বা "টেইল লেটেন্সি কেমন?" — কারণ শুধু গড় বলাটা সিস্টেমের প্রকৃত আচরণ সম্পর্কে অসম্পূর্ণ তথ্য দেয়। একজন ভালো ক্যান্ডিডেট নিজে থেকেই percentile-ভিত্তিক সংখ্যা উপস্থাপন করবেন (যেমন "p50=10ms, p99=80ms, p99.9=500ms"), যা দেখায় তিনি বোঝেন গড় কীভাবে আউটলায়ার লুকিয়ে ফেলে।

অনুশীলন

  1. হিসাব করুন: একটি ডেটাসেটে ১০০টি রিকোয়েস্ট — ৯৫টি ৫ms নেয়, ৫টি ৫০০ms নেয়। হাতে গড়, p50, p95, এবং p99 হিসাব করুন (মনে রাখুন p95-এ ৯৫তম মান খুঁজতে হবে)।

    গড় = (৯৫×৫ + ৫×৫০০)/১০০ = (৪৭৫+২৫০০)/১০০ = ২৯.৭৫ms। p50 = সাজানো তালিকার ৫০তম মান = ৫ms। p95 = ৯৫তম মান — ঠিক সীমানায়, এখনো "৫ms" গ্রুপের শেষ মান, তাই ৫ms। p99 = ৯৯তম মান — এটি শেষের ৫টি ধীর রিকোয়েস্টের মধ্যে পড়ে, তাই ৫০০ms। লক্ষ্য করুন এখানে p95-ই টেইল ধরে ফেলছে (আগের উদাহরণে p99.9 লেগেছিল), কারণ ধীর রিকোয়েস্টের অনুপাত এখানে বেশি (৫% বনাম ১%)।

  2. চিন্তা করুন: উপরের কোড সেলে latencies লিস্টে ৯০০টি ১০ms ও ১০০টি ৫০ms দিয়ে বদলে দিন (মোট ১০০০)। গড়, p50, p90, p99 নতুন করে হাতে অনুমান করুন, তারপর কোডে চালিয়ে মিলিয়ে দেখুন।

    গড় = (৯০০×১০ + ১০০×৫০)/১০০০ = (৯০০০+৫০০০)/১০০০ = ১৪ms। p50 = ৫০০তম মান = ১০ms। p90 = ৯০০তম মান — ঠিক সীমানায়, এখনো "১০ms" গ্রুপ, তাই ১০ms। p99 = ৯৯০তম মান — এটি শেষের ১০০টি "৫০ms" মানের মধ্যে পড়ে (অবস্থান ৯০১-১০০০), তাই ৫০ms। এখানে ধীর রিকোয়েস্টের অনুপাত অনেক বড় (১০%) হওয়ায় p90-ই সেটা ধরে ফেলতে শুরু করে, p99 পর্যন্ত অপেক্ষা করতে হয় না।

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

আগের পাঠ
ব্যাক-অফ-দ্য-এনভেলপ এস্টিমেশন