পারফরম্যান্স অপ্টিমাইজেশন (High-Performance PHP Engineering)
PHP স্লো—এই ধারণা অনেক আগেই ভুল প্রমাণিত হয়েছে। ফেসবুক, স্ল্যাক এবং উইকিপিডিয়া পিএইচপি দিয়েই কোটি কোটি রিকোয়েস্ট হ্যান্ডেল করছে। পার্থক্য হলো তারা Performance Engineering বোঝে।
এই অধ্যায়ে আমরা শিখব কীভাবে পিএইচপি কোডকে রকেটের গতিতে চালানো যায়।
১৩.১ লুপ অপ্টিমাইজেশন (Execution Speed Boost)
লুপ পিএইচপির সবচেয়ে কমন স্ট্রাকচার, আর এখানেই সবচেয়ে বেশি সিপিইউ সাইকেল নষ্ট হয়।
❌ ১. লুপ কন্ডিশনে ফাংশন কল (The Silent Killer)
for ($i = 0; $i < count($largeArray); $i++) { ... }
এখানে প্রতিবার লুপ ঘোরার সময় count() ফাংশনটি কল হচ্ছে। এটি O(n²) কমপ্লেক্সিটি তৈরি করে।
✅ Senior Fix:
$len = count($largeArray); // ক্যালকুলেশন লুপের বাইরে নিয়ে আসুন
for ($i = 0; $i < $len; $i++) { ... }
⚠️ ২. foreach এবং রেফারেন্স বাগ
foreach পিএইচপিতে সবচেয়ে ফাস্ট লুপ (ইন্টারনাল পয়েন্টার ব্যবহার করে)। তবে রেফারেন্স ব্যবহারে সাবধান।
foreach ($arr as &$item) {
$item *= 2;
}
unset($item); // ⚠️ এটা না করলে পরবর্তী কোডে ভয়াবহ বাগ হবে!
🔍 কেন unset? লুপ শেষ হওয়ার পরেও $item মেমোরিতে অ্যারের শেষ এলিমেন্টকে পয়েন্ট করে থাকে।
✅ ৩. SplFixedArray (Memory Save)
সাধারণ পিএইচপি অ্যারে মেমোরি বেশি খায় কারণ এটি হ্যাশটেবল। যদি আপনি জানেন অ্যারের সাইজ কত হবে, তবে SplFixedArray ব্যবহার করুন।
$arr = new SplFixedArray(100000);
// এটি সাধারণ অ্যারের চেয়ে ৩০-৪০% কম মেমোরি নেয় এবং ফাস্ট।
১৩.২ ফাংশন কল ওভারহেড কমানো
পিএইচপিতে প্রতিটি ফাংশন কলের একটি খরচ আছে (Stack Frame Creation, Parameter Copying)।
❌ ছোট লুপে ইনলাইন লজিক (Inlining)
যদি লুপ ১ মিলিয়ন বার চলে, তবে তার ভেতর ছোট ফাংশন কল না করে সরাসরি লজিক লেখা ভালো।
// ❌ Slower (1 Million function calls)
function double($n) { return $n * 2; }
foreach ($data as $v) { $res[] = double($v); }
// ✅ Faster (Zero function overhead)
foreach ($data as $v) { $res[] = $v * 2; }
✅ রিকারশন বনাম ইটারেশন
রিকারশন দেখতে স্মার্ট, কিন্তু পিএইচপিতে এটি মেমোরি স্ট্যাক পূর্ণ করে ফেলে। সবসময় while বা for লুপ ব্যবহার করা পারফরম্যান্সের জন্য ভালো।
১৩.৩ অ্যারে অপ্টিমাইজেশন স্ট্র্যাটেজি
❌ array_merge এর বিপদ
লুপের ভেতর array_merge ব্যবহার করা মানে নিজের পায়ে কুড়াল মারা।
// ❌ Disaster: O(n²) Complexity
foreach ($chunks as $chunk) {
$result = array_merge($result, $chunk);
// প্রতিবার পুরো $result অ্যারে কপি করে নতুন মেমোরি বানায়
}
// ✅ Correct: O(n)
foreach ($chunks as $chunk) {
foreach ($chunk as $item) {
$result[] = $item;
}
}
// অথবা PHP 7.4+ Spread Operator
$result = array_merge(...$chunks);
✅ SplQueue এবং SplStack
array_shift খুব স্লো (O(n)), কারণ বাকি সব উপাদানকে এক ঘর করে বামে সরতে হয়। SplQueue বা SplDoublyLinkedList ব্যবহার করলে এটি O(1) হয়।
১৩.৪ OPcache — প্রোডাকশন টিউনিং
আমরা অধ্যায় ৭-এ OPcache দেখেছি, এখানে আমরা টিউনিং দেখব।
📦 Preloading (PHP 7.4+)
সার্ভার স্টার্ট হওয়ার সময় ফ্রেমওয়ার্কের সব জরুরি ক্লাস (Laravel/Symfony Core) মেমোরিতে লোড করে রাখা যায়। এতে প্রতি রিকোয়েস্টে ক্লাস লোডিং টাইম বাঁচে।
⚙️ Best Config:
opcache.validate_timestamps=0 ; ফাইল সিস্টেম চেক বন্ধ
opcache.preload=/path/to/project/preload.php ; প্রি-লোডিং চালু
opcache.jit_buffer_size=100M ; PHP 8 JIT (Just In Time) Compiler
১৩.৫ রেডিস ক্যাশিং প্যাটার্ন (Cache-Aside)
ডাটাবেস সবসময় স্লো, মেমোরি (Redis) সবসময় ফাস্ট।
🔄 Cache-Aside Pattern:
- আগে রেডিসে চেক করো
- পেলে রিটার্ন করো (Cache Hit)
- না পেলে ডিবি থেকে আনো এবং রেডিসে সেট করো (Cache Miss)
$key = "product:{$id}";
if ($data = $redis->get($key)) {
return json_decode($data, true);
}
// Cache Miss - Expensive DB Call
$data = $db->query("...")->fetch();
$redis->setex($key, 3600, json_encode($data)); // ১ ঘণ্টা মেয়াদ
return $data;
📊 রেজাল্ট: ডিবি লোড ৯০% কমে যায়, রেসপন্স টাইম ৫০০ms → ৫০ms
১৩.৬ কুয়েরি অপ্টিমাইজেশন (Database Level)
❌ ১. SELECT * নিষিদ্ধ
শুধু প্রয়োজনীয় কলাম নিন। কম ডাটা ট্রান্সফার → কম মেমোরি খরচ।
✅ ২. কুয়েরি ক্যাশিং
স্ট্যাটিক ডাটা (যেমন: দেশের তালিকা, ক্যাটাগরি) বারবার না এনে রেডিস বা ফাইলে ক্যাশ করুন।
১৩.৭ পেজিনেশন অপ্টিমাইজেশন (Cursor Method)
আমরা আগে দেখেছি OFFSET কেন খারাপ। এখন দেখি সলিউশন।
✅ Cursor Based Pagination:
SELECT * FROM orders
WHERE id < :last_seen_id
ORDER BY id DESC
LIMIT 10;
এটি যেকোনো সাইজের টেবিলে মিলি-সেকেন্ডে কাজ করে কারণ এটি ইনডেক্স ব্যবহার করে সরাসরি জাম্প করে।
১৩.৮ লেজি লোডিং (Generators)
১ জিবি লগ ফাইল রিড করতে চাইলে পুরোটা ভেরিয়েবলে নিলে মেমোরি ক্র্যাশ করবে। yield কিওয়ার্ড ব্যবহার করুন।
function readLines($file) {
$fp = fopen($file, 'r');
while ($line = fgets($fp)) {
yield $line; // এক লাইন মেমোরিতে রাখে, কাজ শেষে ফেলে দেয়
}
fclose($fp);
}
foreach (readLines("huge.log") as $line) {
// Process line
}
📊 মেমোরি ব্যবহার: ১ জিবি ফাইলের জন্য মাত্র কয়েক কেবি!
১৩.৯ কনকারেন্সি (একসাথে অনেক কাজ)
পিএইচপি সিঙ্গেল থ্রেডেড। কিন্তু IO Blocking (যেমন API Call) এর সময় আমরা বসে না থেকে অন্য কাজ করতে পারি।
🔧 curl_multi (Parallel API Requests)
৩টি API থেকে ডাটা আনতে: সাধারণ → 3s, Parallel → ~1s
$mh = curl_multi_init();
// ... multiple handles add ...
do {
curl_multi_exec($mh, $running); // একসাথে সব রিকোয়েস্ট পাঠাবে
} while ($running > 0);
১৩.১০ প্যারালাল পিএইচপি (Multi-Threading)
CPU Intensive কাজের জন্য (যেমন ইমেজ প্রসেসিং, রিপোর্ট জেনারেশন) এটি প্রতিটি CPU কোর ব্যবহার করতে পারে।
use parallelRuntime;
$runtime = new Runtime();
$future = $runtime->run(function(){
// এই কোডটি আলাদা থ্রেড/কোর-এ রান করবে
return complexCalculation();
});
// মেইন থ্রেড অন্য কাজ করতে পারে...
echo $future->value(); // রেজাল্ট পাওয়ার অপেক্ষা
🎯 Senior Developer Interview Questions (Chapter 13)
Q: লুপের ভেতর array_merge ব্যবহার করলে কেন পারফরম্যান্স খারাপ হয়?
array_merge প্রতি ইটারেশনে পুরনো অ্যারে এবং নতুন অ্যারে কপি করে একটি নতুন অ্যারে তৈরি করে। এর কমপ্লেক্সিটি O(n²)। এর বদলে [] দিয়ে অ্যাপেন্ড করা বা array_merge(...$chunks) ব্যবহার করা ভালো।
Q: OFFSET বেসড পেজিনেশন কেন স্লো? বিকল্প কী?
OFFSET ডাটাবেসকে বলে "প্রথম ১ লাখ ডাটা পড়ো এবং ফেলে দাও", যা ডিস্ক I/O বাড়ায়। বিকল্প হলো Cursor-based পেজিনেশন (WHERE id > last_id)।
Q: Generator (Yield) ব্যবহার করলে মেমোরি কীভাবে বাঁচে?
জেনারেটর পুরো ডেটাসেট একসাথে মেমোরিতে লোড করে না। এটি একটি ইটারেটর হিসেবে কাজ করে এবং যখন যেটা প্রয়োজন শুধু সেই ভ্যালুটি তৈরি করে।
Q: কনকারেন্সি (Concurrency) এবং প্যারালালিজম (Parallelism) এর পার্থক্য কী?
কনকারেন্সি হলো একই সময়ে একাধিক কাজ হ্যান্ডেল করা (যেমন curl_multi দিয়ে ওয়েটিং টাইমে অন্য কাজ করা)। প্যারালালিজম হলো সত্যিকার অর্থে একাধিক CPU কোর ব্যবহার করে একই সাথে একাধিক কাজ এক্সিকিউট করা (যেমন parallel এক্সটেনশন)।
🎯 অধ্যায় ১৩ এর সারাংশ (Summary)
এই অধ্যায়ে আমরা শিখলাম:
- ✓Loop Optimization: count() বাইরে নিন, foreach use reference with unset()
- ✓Function Call Overhead: Inline small operations in loops
- ✓Array Optimization: Avoid array_merge in loops, use SplFixedArray, SplQueue
- ✓OPcache: validate_timestamps=0, Preload, JIT (PHP 8)
- ✓Redis Caching: Cache-Aside pattern reduces DB load by 90%
- ✓Cursor Pagination: O(1) performance, no OFFSET
- ✓Generators (yield): Process GB-scale files with KB memory
- ✓Concurrency: curl_multi for parallel API calls
✨ এখন আপনি শুধু কোডার নন, আপনি একজন পারফরম্যান্স ইঞ্জিনিয়ার।
🚀 PHP Senior Journey: 13/13 Chapters Completed! 🎉
🏆 Congratulations! You are now a PHP Architect!