ভূমিকা
তাই আপনি একটি ইমেল পান যাতে বলা হয় যে নির্দিষ্ট সময় পরে আপনার ওয়েব-গেম / ওয়েব-অ্যাপ কীভাবে খারাপ পারফর্ম করছে, আপনি নিজের কোড খুঁটিয়ে দেখেন, Chrome-এর মেমরি পারফর্ম্যান্স টুল না খোলা পর্যন্ত এমন কিছু দেখতে পান না যা আলাদাভাবে চোখে পড়ে এবং এটি দেখতে পান:
আপনার একজন সহকর্মী হাসেন, কারণ তিনি বুঝতে পেরেছেন যে আপনার মেমরি-সম্পর্কিত পারফর্ম্যান্সের সমস্যা আছে।
মেমরি গ্রাফ ভিউতে, এই করাতের দাঁতের মতো প্যাটার্নটি পারফর্ম্যান্সের সম্ভাব্য গুরুতর সমস্যা সম্পর্কে অনেক কিছু বলে। আপনার মেমরির ব্যবহার যত বাড়বে, আপনি টাইমলাইন ক্যাপচারে চার্ট এরিয়াও তত বাড়তে দেখবেন। চার্ট হঠাৎ করে কমে গেলে, এটি এমন একটি উদাহরণ যখন গার্বেজ কালেক্টর রান করেছে এবং আপনার রেফারেন্স করা মেমরি অবজেক্টগুলি পরিষ্কার করেছে।
এই ধরনের গ্রাফে আপনি দেখতে পাবেন যে অনেক গার্বেজ কালেকশন ইভেন্ট ঘটছে, যা আপনার ওয়েব-অ্যাপের পারফর্ম্যান্সের জন্য ক্ষতিকর হতে পারে। এই নিবন্ধে আপনার মেমরির ব্যবহার কীভাবে নিয়ন্ত্রণ করবেন এবং পারফর্ম্যান্সের উপর এর প্রভাব কীভাবে কমাবেন সেই বিষয়ে আলোচনা করা হবে।
গার্বেজ সংগ্রহ ও পারফর্ম্যান্স সংক্রান্ত খরচ
JavaScript-এর মেমরি মডেল এমন একটি প্রযুক্তির উপর ভিত্তি করে তৈরি করা হয়েছে যা গার্বেজ কালেক্টর নামে পরিচিত। অনেক ভাষায়, সিস্টেমের মেমরি হিপ থেকে মেমরি বরাদ্দ করা এবং খালি করার জন্য প্রোগ্রামার সরাসরি দায়ী। তবে, একটি গার্বেজ কালেক্টর সিস্টেম প্রোগ্রামারের হয়ে এই কাজটি ম্যানেজ করে, যার অর্থ হল প্রোগ্রামার যখন কোনও অবজেক্টের রেফারেন্স সরিয়ে দেয়, তখন সেটি সরাসরি মেমরি থেকে মুক্ত হয় না, বরং পরে যখন GC-এর হিউরিস্টিকস সিদ্ধান্ত নেয় যে এটি করা উপকারী হবে, তখন সেটি করা হয়। এই সিদ্ধান্ত প্রক্রিয়ার জন্য GC-কে অ্যাক্টিভ এবং ইনঅ্যাক্টিভ অবজেক্টের উপর কিছু পরিসংখ্যানগত বিশ্লেষণ করতে হয়, যা করতে কিছুটা সময় লাগে।
গার্বেজ কালেকশনকে প্রায়শই ম্যানুয়াল মেমোরি ম্যানেজমেন্টের বিপরীত হিসেবে দেখানো হয়, যার জন্য প্রোগ্রামারকে নির্দিষ্ট করতে হয় যে কোন অবজেক্ট ডিলিট করতে হবে এবং মেমোরি সিস্টেমে ফিরিয়ে দিতে হবে
GC যে প্রসেসের মাধ্যমে মেমরি রিক্লেম করে তা ফ্রি নয়, এটি সাধারণত কাজ করার জন্য সময় ব্লক করে আপনার উপলভ্য পারফর্ম্যান্স কমিয়ে দেয়; এর পাশাপাশি, সিস্টেম নিজেই সিদ্ধান্ত নেয় যে কখন এটি রান করতে হবে। এই অ্যাকশনের উপর আপনার কোনও নিয়ন্ত্রণ নেই, কোড এক্সিকিউশনের সময় যেকোনও মুহূর্তে GC পালস হতে পারে, যা সম্পূর্ণ না হওয়া পর্যন্ত কোড এক্সিকিউশন ব্লক করে রাখবে। এই পালসের সময়সীমা সাধারণত আপনার জানা থাকে না; আপনার প্রোগ্রাম যেকোনও সময় কীভাবে মেমরি ব্যবহার করছে তার উপর নির্ভর করে এটি রান করতে কিছুটা সময় নেবে।
হাই পারফর্ম্যান্স অ্যাপ্লিকেশনগুলি ব্যবহারকারীদের জন্য একটি মসৃণ অভিজ্ঞতা নিশ্চিত করতে ধারাবাহিক পারফর্ম্যান্স সীমার উপর নির্ভর করে। গার্বেজ কালেক্টর সিস্টেম এই লক্ষ্যকে শর্ট সার্কিট করতে পারে, কারণ এটি যেকোনও সময় যেকোনও সময়ের জন্য চলতে পারে, যার ফলে অ্যাপ্লিকেশনটি তার পারফর্ম্যান্সের লক্ষ্য পূরণ করার জন্য প্রয়োজনীয় সময় নষ্ট করে।
মেমরির ব্যবহার বন্ধ করা কমান, গার্বেজ কালেকশন ট্যাক্স কমান
যেমন উল্লেখ করা হয়েছে, হিউরিস্টিক্সের একটি সেট যখন নির্ধারণ করে যে যথেষ্ট সংখ্যক ইনঅ্যাক্টিভ অবজেক্ট আছে এবং পাল্স কার্যকর হবে, তখন GC পাল্স ঘটবে। তাই, আপনার অ্যাপ্লিকেশন থেকে গার্বেজ কালেক্টরের সময় কমানোর মূল উপায় হল, যতটা সম্ভব অতিরিক্ত অবজেক্ট তৈরি ও রিলিজ করার ঘটনা কমানো। ঘনঘন অবজেক্ট তৈরি/মুক্ত করার এই প্রসেসকে "মেমরির ব্যবহার বন্ধ করা" বলা হয়। আপনার অ্যাপ্লিকেশনের লাইফটাইমে মেমরির ব্যবহার বন্ধ করার পরিমাণ কমাতে পারলে, আপনার এক্সিকিউশন থেকে GC-এর নেওয়া সময়ের পরিমাণও কমাতে পারবেন। এর অর্থ হল, আপনাকে তৈরি করা এবং ধ্বংস করা অবজেক্টের সংখ্যা কমাতে/কমাতে হবে, কার্যকরভাবে, আপনাকে মেমরি বরাদ্দ করা বন্ধ করতে হবে।
এই প্রসেস আপনার মেমরি গ্রাফ এখান থেকে সরিয়ে দেবে :
এতে পরিবর্তন করুন:
এই মডেলে আপনি দেখতে পাবেন যে গ্রাফে আর করাতের দাঁতের মতো প্যাটার্ন নেই, বরং শুরুতে অনেক বেশি বৃদ্ধি পেয়ে পরে ধীরে ধীরে বৃদ্ধি পেয়েছে। মেমরির ব্যবহার বন্ধ করার কারণে পারফর্ম্যান্স সংক্রান্ত সমস্যা হলে, আপনি এই ধরনের গ্রাফ তৈরি করতে চাইবেন।
স্ট্যাটিক-মেমরি জাভাস্ক্রিপ্টের দিকে অগ্রসর হওয়া
স্ট্যাটিক মেমরি জাভাস্ক্রিপ্ট হল এমন একটি টেকনিক যার মধ্যে আপনার অ্যাপের শুরুতে, এর লাইফটাইমের জন্য প্রয়োজনীয় সমস্ত মেমরি প্রি-অ্যালোকেট করা এবং অবজেক্টের আর প্রয়োজন না হলে এক্সিকিউশনের সময় সেই মেমরি ম্যানেজ করা অন্তর্ভুক্ত। কয়েকটি সহজ ধাপের মাধ্যমে আমরা এই লক্ষ্যে পৌঁছাতে পারি:
- ব্যবহারের বিভিন্ন পরিস্থিতিতে প্রয়োজনীয় লাইভ মেমরি অবজেক্টের (প্রতিটি প্রকারের জন্য) সর্বাধিক সংখ্যা কত তা নির্ধারণ করতে আপনার অ্যাপ্লিকেশনকে ইনস্ট্রুমেন্ট করুন
- সেই সর্বাধিক পরিমাণ আগে থেকে বরাদ্দ করতে আপনার কোড আবার প্রয়োগ করুন এবং তারপরে মূল মেমরিতে যাওয়ার পরিবর্তে ম্যানুয়ালি সেগুলি ফেচ/রিলিজ করুন।
বাস্তবে, #১ সম্পূর্ণ করতে আমাদের #২-এর কিছুটা করতে হবে, তাই সেখান থেকে শুরু করা যাক।
অবজেক্ট পুল
সহজ কথায়, অবজেক্ট পুলিং হল একই ধরনের অব্যবহৃত অবজেক্টের সেট রেখে দেওয়ার প্রসেস। আপনার কোডের জন্য নতুন অবজেক্টের প্রয়োজন হলে, সিস্টেম মেমরি হিপ থেকে নতুন একটি বরাদ্দ করার পরিবর্তে, আপনি পুল থেকে অব্যবহৃত অবজেক্টগুলির মধ্যে একটি রিসাইকেল করেন। অবজেক্টের সাথে এক্সটার্নাল কোড ব্যবহার করা হয়ে গেলে, মূল মেমরিতে তা রিলিজ করার পরিবর্তে পুলে ফিরিয়ে দেওয়া হয়। যেহেতু অবজেক্টটি কোড থেকে কখনও ডিরেফারেন্স (ওরফে মুছে ফেলা) করা হয় না, তাই এটি গার্বেজ কালেক্ট করা হবে না। অবজেক্ট পুল ব্যবহার করলে মেমরির নিয়ন্ত্রণ প্রোগ্রামারের হাতে ফিরে আসে, ফলে পারফর্ম্যান্সের উপর গার্বেজ কালেক্টরের প্রভাব কমে যায়।
যেহেতু কোনও অ্যাপ্লিকেশন বিভিন্ন ধরনের অবজেক্টের সেট ম্যানেজ করে, তাই অবজেক্ট পুলের সঠিক ব্যবহারের জন্য আপনার অ্যাপ্লিকেশনের রানটাইমের সময় উচ্চ-টার্নওভারের অভিজ্ঞতা আছে এমন প্রতিটি ধরনের জন্য একটি করে পুল থাকতে হবে।
var newEntity = gEntityObjectPool.allocate();
newEntity.pos = {x: 215, y: 88};
//..... do some stuff with the object that we need to do
gEntityObjectPool.free(newEntity); //free the object when we're done
newEntity = null; //free this object reference
বেশিরভাগ অ্যাপ্লিকেশনের ক্ষেত্রে, নতুন অবজেক্ট বরাদ্দ করার প্রয়োজনীয়তার দিক থেকে আপনি শেষ পর্যন্ত কিছু লেভেল-অফ পাবেন। আপনার অ্যাপ্লিকেশন একাধিকবার রান করার পরে, এই ঊর্ধ্বসীমা কী তা আপনি খুব ভালভাবে বুঝতে পারবেন এবং আপনার অ্যাপ্লিকেশন শুরু করার সময় সেই সংখ্যক অবজেক্ট আগে থেকে বরাদ্দ করতে পারবেন।
অবজেক্ট আগে থেকে বরাদ্দ করা হচ্ছে
আপনার প্রোজেক্টে অবজেক্ট পুলিং প্রয়োগ করলে, আপনার অ্যাপ্লিকেশন রানটাইমের সময় প্রয়োজনীয় অবজেক্টের সংখ্যার জন্য আপনি একটি তাত্ত্বিক সর্বাধিক সংখ্যা পাবেন। বিভিন্ন টেস্টিং সিনারিওতে আপনার সাইট চালানোর পরে, আপনি কী ধরনের মেমরি প্রয়োজন হবে সেই সম্পর্কে একটি ভালো ধারণা পেতে পারেন এবং সেই ডেটা কোথাও ক্যাটালগ করতে পারেন এবং আপনার অ্যাপ্লিকেশনের জন্য মেমরির প্রয়োজনীয়তার সর্বোচ্চ সীমা কী তা বোঝার জন্য এটি বিশ্লেষণ করতে পারেন।
তারপর, আপনার অ্যাপের শিপিং ভার্সনে, আপনি নির্দিষ্ট পরিমাণ পর্যন্ত সব অবজেক্ট পুল প্রি-ফিল করার জন্য ইনিশিয়ালাইজেশন ফেজ সেট করতে পারবেন। এই অ্যাক্ট আপনার অ্যাপের ফ্রন্টে সব অবজেক্ট ইনিশিয়ালাইজেশন পুশ করবে এবং এক্সিকিউশনের সময় ডায়নামিকভাবে হওয়া অ্যালোকেশনের পরিমাণ কমাবে।
function init() {
//preallocate all our pools.
//Note that we keep each pool homogeneous wrt object types
gEntityObjectPool.preAllocate(256);
gDomObjectPool.preAllocate(888);
}
আপনি যে পরিমাণ বেছে নেন তা আপনার অ্যাপ্লিকেশনের আচরণের সাথে অনেক বেশি সম্পর্কিত; কখনও কখনও তাত্ত্বিক সর্বোচ্চ পরিমাণ সেরা বিকল্প নাও হতে পারে। যেমন, গড় সর্বাধিক বেছে নিলে, পাওয়ার ইউজার নন এমন ব্যবহারকারীদের জন্য আপনি ছোট মেমরি ফুটপ্রিন্ট পেতে পারেন।
কোনও ম্যাজিক নয়
অ্যাপের সম্পূর্ণ শ্রেণীবিভাগ রয়েছে যেখানে স্ট্যাটিক মেমরি গ্রোথ প্যাটার্ন কাজে আসতে পারে। তবে, Chrome DevRel-এর Renato Mangini যেমন উল্লেখ করেছেন, এতে কিছু অসুবিধা আছে।
উপসংহার
JavaScript যে কারণে ওয়েবের জন্য আদর্শ তার অন্যতম কারণ হল এটি দ্রুত, মজাদার এবং সহজে শুরু করা যায়। সিনট্যাক্স সংক্রান্ত বিধিনিষেধের ক্ষেত্রে কম বাধা এবং আপনার হয়ে মেমরি সংক্রান্ত সমস্যা ম্যানেজ করার কারণে এটি মূলত হয়ে থাকে। আপনি কোডিং করতে পারেন এবং এটি আপনার হয়ে জটিল কাজগুলি করে দেবে। তবে, HTML5 গেম-এর মতো উচ্চ-পারফর্ম্যান্স সম্পন্ন ওয়েব অ্যাপ্লিকেশনের ক্ষেত্রে, GC প্রায়শই অত্যন্ত প্রয়োজনীয় ফ্রেম রেট কমিয়ে দেয়, যার ফলে ব্যবহারকারীর অভিজ্ঞতা খারাপ হয়। কিছুটা সতর্কতার সাথে ইন্সট্রুমেন্টেশন এবং অবজেক্ট পুল গ্রহণ করার মাধ্যমে, আপনি ফ্রেম রেটের উপর এই বোঝা কমাতে পারেন এবং আরও দুর্দান্ত জিনিসের জন্য সেই সময়টি আবার ব্যবহার করতে পারেন।
সোর্স কোড
ওয়েবে অবজেক্ট পুলের অনেক ইমপ্লিমেন্টেশন আছে, তাই আমি আপনাকে আরেকটি দিয়ে বিরক্ত করব না। পরিবর্তে, আমি আপনাকে এগুলির দিকে নির্দেশ করব, যার প্রতিটিতে নির্দিষ্ট প্রয়োগের সূক্ষ্মতা রয়েছে; যা গুরুত্বপূর্ণ, প্রতিটি অ্যাপ্লিকেশন ব্যবহারের নির্দিষ্ট প্রয়োগের প্রয়োজন হতে পারে তা বিবেচনা করে।
- Gamecore.js’ অবজেক্ট পুল
- Beej’s Object Pools
- Emehrkay-এর সুপার সিম্পল অবজেক্ট পুল
- স্টিভেন ল্যামবার্টের গেম-ফোকাসড অবজেক্ট পুল
- RenderEngine-এর objectPool সেট-আপ