জিটার-সম্পর্কিত জ্যাঙ্ক শনাক্ত করা

জিটার হল সিস্টেমের এলোমেলো আচরণ যা কাজকে চলতে বাধা দেয়। এই পৃষ্ঠায়, কীভাবে জিটার-সম্পর্কিত জ্যাঙ্ক সমস্যা শনাক্ত ও সমাধান করতে হয় তা বর্ণনা করা হয়েছে।

অ্যাপ থ্রেড শিডিউলার বিলম্ব

শিডিউলার বিলম্ব হল জিটারের সবচেয়ে স্পষ্ট লক্ষণ: কোনও প্রসেস যা চালানো উচিত তা চালানোর উপযুক্ত করা হয় কিন্তু উল্লেখযোগ্য সময়ের জন্য চালানো হয় না। বিলম্বের তাৎপর্য প্রসঙ্গ অনুযায়ী আলাদা আলাদা হয়। যেমন:

  • কোনও অ্যাপের র‍্যান্ডম হেল্পার থ্রেড সম্ভবত কোনও সমস্যা ছাড়াই অনেক মিলি সেকেন্ডের জন্য বিলম্বিত হতে পারে।
  • কোনও অ্যাপের UI থ্রেড ১-২ms জিটার সহ্য করতে পারে।
  • ড্রাইভার kthreads SCHED_FIFO হিসেবে রান করলে, রান করার আগে সেগুলি যদি ৫০০ মাইক্রোসেকেন্ডের জন্য রান করার উপযুক্ত হয়, তাহলে সমস্যা হতে পারে।

থ্রেডের রানিং সেগমেন্টের আগে নীল রঙের বার দ্বারা সিস্ট্রেসে রান করার সময় শনাক্ত করা যেতে পারে। এছাড়াও, কোনও থ্রেডের জন্য sched_wakeup ইভেন্ট এবং sched_switch ইভেন্টের মধ্যে সময়ের দৈর্ঘ্য থেকে রান করার উপযুক্ত সময় নির্ধারণ করা যেতে পারে। এই ইভেন্ট থ্রেড এক্সিকিউশন শুরু হওয়ার ইঙ্গিত দেয়।

অনেকক্ষণ ধরে চলা থ্রেড

অ্যাপের UI থ্রেড খুব বেশি সময় ধরে রান করলে সমস্যা হতে পারে। কম লেভেলের থ্রেড বেশি সময় ধরে রান করার কারণ সাধারণত আলাদা আলাদা হয়, কিন্তু UI থ্রেড রান করার সময়কে শূন্যের দিকে ঠেলে দেওয়ার চেষ্টা করলে, কম লেভেলের থ্রেড বেশি সময় ধরে রান করার কারণ হিসেবে একই ধরনের কিছু সমস্যা সমাধান করতে হতে পারে। দেরি হওয়ার সমস্যা কমাতে:

  1. থার্মাল থ্রটলিং-এ যেভাবে বর্ণনা করা হয়েছে সেইভাবে cpusets ব্যবহার করুন।
  2. CONFIG_HZ ভ্যালু বাড়ান।
    • ঐতিহাসিকভাবে, arm এবং arm64 প্ল্যাটফর্মে ভ্যালু ১০০-তে সেট করা হয়েছে। তবে, এটি একটি ঐতিহাসিক দুর্ঘটনা এবং ইন্ট্যার‍্যাক্টিভ ডিভাইসের জন্য ব্যবহার করার মতো ভাল ভ্যালু নয়। CONFIG_HZ=100-এর অর্থ হল, জিফি ১০ মিলিসেকেন্ড দীর্ঘ হয়, যার অর্থ হল সিপিইউয়ের মধ্যে লোড ব্যালেন্সিং হতে ২০ মিলিসেকেন্ড (দুটি জিফি) সময় লাগতে পারে। এটি লোড করা সিস্টেমে জ্যাঙ্ক হওয়ার ক্ষেত্রে উল্লেখযোগ্যভাবে অবদান রাখতে পারে।
    • সাম্প্রতিক ডিভাইস (Nexus 5X, Nexus 6P, Pixel, এবং Pixel XL) CONFIG_HZ=300 সহ শিপ করা হয়েছে। এর ফলে বিদ্যুতের খরচ খুব কম হবে এবং রান করার সময় উল্লেখযোগ্যভাবে উন্নত হবে। CONFIG_HZ পরিবর্তন করার পরে আপনি যদি পাওয়ার ব্যবহার বা পারফর্ম্যান্স সংক্রান্ত সমস্যা উল্লেখযোগ্যভাবে বৃদ্ধি পেতে দেখেন, তাহলে সম্ভবত আপনার কোনও একটি ড্রাইভার মিলি সেকেন্ডের পরিবর্তে কাঁচা জিফি ভিত্তিক টাইমার ব্যবহার করছে এবং জিফিতে কনভার্ট করছে। এটি সাধারণত সহজেই সমাধান করা যায় (Nexus 5X ও 6P-তে kgsl টাইমার সংক্রান্ত সমস্যা সমাধানের জন্য প্যাচ দেখুন, এটি CONFIG_HZ=300-এ কনভার্ট করার সময় করা হয়েছে)।
    • অবশেষে, আমরা Nexus/Pixel-এ CONFIG_HZ=1000 নিয়ে পরীক্ষা করেছি এবং দেখেছি যে RCU ওভারহেড কমে যাওয়ার ফলে এটি উল্লেখযোগ্য পারফর্ম্যান্স ও পাওয়ার রিডাকশন অফার করে।

শুধুমাত্র এই দুটি পরিবর্তন করলে, লোড থাকা অবস্থায় UI থ্রেড রান করার সময় ডিভাইসের পারফর্ম্যান্স অনেক ভালো হওয়া উচিত।

sys.use_fifo_ui ব্যবহার করুন

sys.use_fifo_ui প্রপার্টিকে ১-এ সেট করে আপনি UI থ্রেড রান করার সময়কে শূন্যে নামিয়ে আনার চেষ্টা করতে পারেন।

সতর্কতা: আপনার কাছে ক্ষমতা-সচেতন RT শিডিউলার না থাকলে, হেটেরোজেনাস CPU কনফিগারেশনে এই বিকল্প ব্যবহার করবেন না। এবং, এই মুহূর্তে, বর্তমানে শিপিং করা কোনও RT শিডিউলার ক্যাপাসিটি-অ্যাওয়ার নয়। EAS-এর জন্য আমরা একটিতে কাজ করছি, তবে এটি এখনও উপলভ্য নেই। ডিফল্ট RT শিডিউলার সম্পূর্ণভাবে RT অগ্রাধিকারের উপর ভিত্তি করে তৈরি হয় এবং কোনও CPU-তে সমান বা বেশি অগ্রাধিকারের RT থ্রেড আগে থেকেই আছে কিনা তার উপর নির্ভর করে।

এর ফলে, ডিফল্ট RT শিডিউলার আপনার তুলনামূলকভাবে দীর্ঘ সময় ধরে চলা UI থ্রেডকে উচ্চ-ফ্রিকোয়েন্সি বিগ কোর থেকে ন্যূনতম ফ্রিকোয়েন্সির লিটল কোরে সরিয়ে দেবে, যদি একই বিগ কোরে উচ্চতর অগ্রাধিকারের FIFO kthread জেগে ওঠে। এর ফলে পারফর্ম্যান্স উল্লেখযোগ্যভাবে কমে যাবে। যেহেতু এই বিকল্পটি এখনও কোনও শিপিং Android ডিভাইসে ব্যবহার করা হয়নি, তাই আপনি এটি ব্যবহার করতে চাইলে, এটি যাচাই করার জন্য Android পারফর্ম্যান্স টিমের সাথে যোগাযোগ করুন।

sys.use_fifo_ui চালু করা থাকলে, ActivityManager টপ অ্যাপের UI থ্রেড ও RenderThread (দুটি সবচেয়ে UI-সংক্রান্ত গুরুত্বপূর্ণ থ্রেড) ট্র্যাক করে এবং সেইসব থ্রেডকে SCHED_OTHER-এর পরিবর্তে SCHED_FIFO করে। এটি UI ও RenderThreads থেকে কার্যকরভাবে জিটার সরিয়ে দেয়; এই বিকল্প চালু করে আমরা যে ট্রেস সংগ্রহ করেছি তা মিলি সেকেন্ডের পরিবর্তে মাইক্রো সেকেন্ডের ক্রম অনুযায়ী রান করার সময় দেখায়।

তবে, RT লোড ব্যালেন্সার ক্যাপাসিটি-অ্যাওয়ার না হওয়ার কারণে, অ্যাপ স্টার্ট-আপ পারফর্ম্যান্সে ৩০% হ্রাস ঘটেছিল, কারণ অ্যাপ চালু করার জন্য দায়ী UI থ্রেডটি 2.1GHz গোল্ড Kryo কোর থেকে 1.5GHz সিলভার Kryo কোরে সরানো হবে। ক্ষমতা-সচেতন RT লোড ব্যালেন্সারের সাথে, আমরা বাল্ক অপারেশনগুলিতে একই রকম পারফর্ম্যান্স এবং আমাদের UI বেঞ্চমার্কের অনেকগুলিতে ৯৫তম এবং ৯৯তম পার্সেন্টাইল ফ্রেম টাইমে ১০-১৫% হ্রাস দেখতে পাই।

ট্রাফিক ইন্টারাপ্ট করা

কারণ ARM প্ল্যাটফর্ম ডিফল্ট হিসেবে শুধুমাত্র CPU 0-তে ইন্টারাপ্ট ডেলিভার করে, তাই আমরা IRQ ব্যালেন্সার (Qualcomm প্ল্যাটফর্মে irqbalance বা msm_irqbalance) ব্যবহার করার সাজেশন দিই।

Pixel ডেভেলপ করার সময়, আমরা জ্যাঙ্ক দেখেছি যা সরাসরি CPU 0-তে ইন্টারাপ্ট সহ ওভারহোয়েলমিংয়ের জন্য হয়েছে। যেমন, mdss_fb0 থ্রেড CPU ০-এ শিডিউল করা হলে, স্ক্যানআউটের ঠিক আগে ডিসপ্লে দ্বারা ট্রিগার করা ইন্টারাপ্টের কারণে জ্যাঙ্ক হওয়ার সম্ভাবনা অনেক বেশি থাকে। mdss_fb0 খুব কম সময়ের মধ্যে নিজের কাজ করতে ব্যস্ত থাকবে এবং তারপরে MDSS ইন্টারাপ্ট হ্যান্ডলারের জন্য কিছু সময় নষ্ট করবে। প্রাথমিকভাবে, আমরা mdss_fb0 থ্রেডের CPU অ্যাফিনিটি CPU 1-3-এ সেট করে এটি ঠিক করার চেষ্টা করেছি যাতে ইন্টারাপ্ট নিয়ে কোনও সমস্যা না হয়, কিন্তু তারপরে আমরা বুঝতে পেরেছি যে আমরা এখনও msm_irqbalance চালু করিনি। msm_irqbalance চালু করা থাকলে, mdss_fb0 এবং MDSS ইন্টারাপ্ট একই CPU-তে থাকলেও, অন্যান্য ইন্টারাপ্ট থেকে হওয়া কনটেনশন কমে যাওয়ার কারণে জ্যাঙ্ক উল্লেখযোগ্যভাবে উন্নত হয়েছে।

sched বিভাগ এবং irq বিভাগ দেখে এটি systrace-এ শনাক্ত করা যেতে পারে। sched বিভাগে কী শিডিউল করা হয়েছে তা দেখানো হয়, কিন্তু irq বিভাগে ওভারল্যাপ করা অঞ্চল মানে হল, স্বাভাবিকভাবে শিডিউল করা প্রসেসের পরিবর্তে সেই সময় ইন্টারাপ্ট চলছে। ইন্টারাপ্ট চলাকালীন যদি উল্লেখযোগ্য সময় ব্যয় হতে দেখেন, তাহলে আপনার বিকল্পের মধ্যে এগুলি অন্তর্ভুক্ত:

  • ইন্টারাপ্ট হ্যান্ডলার আরও দ্রুত করুন।
  • প্রথমেই বিঘ্ন ঘটানো আটকান।
  • ইন্টারাপ্টের ফ্রিকোয়েন্সি পরিবর্তন করুন যাতে এটি অন্যান্য নিয়মিত কাজের সাথে একই সময়ে না ঘটে (যদি এটি নিয়মিত ইন্টারাপ্ট হয়)।
  • সরাসরি ইন্টারাপ্টের সিপিইউ অ্যাফিনিটি সেট করুন এবং এটি ব্যালেন্স করা থেকে আটকান।
  • ইন্টারাপ্ট এড়াতে, ইন্টারাপ্ট যে থ্রেডকে প্রভাবিত করছে তার CPU অ্যাফিনিটি সেট করুন।
  • ইন্টারাপ্ট ব্যালেন্সারের উপর নির্ভর করে ইন্টারাপ্টকে কম লোড থাকা CPU-তে সরিয়ে নিয়ে যান।

CPU অ্যাফিনিটি সেট করা সাধারণত সাজেস্ট করা হয় না, তবে নির্দিষ্ট কিছু ক্ষেত্রে এটি কাজে লাগতে পারে। সাধারণত, বেশিরভাগ সাধারণ ইন্টারাপ্টের জন্য সিস্টেমের অবস্থা অনুমান করা খুব কঠিন, তবে আপনার কাছে যদি খুব নির্দিষ্ট কিছু শর্ত থাকে যা নির্দিষ্ট কিছু ইন্টারাপ্ট ট্রিগার করে যেখানে সিস্টেম স্বাভাবিকের চেয়ে বেশি সীমাবদ্ধ (যেমন VR), তাহলে এক্সপ্লিসিট CPU অ্যাফিনিটি একটি ভালো সমাধান হতে পারে।

দীর্ঘ softirqs

softirq চলাকালীন, এটি প্রিএম্পশন বন্ধ করে দেয়। softirq-কে কার্নেলের মধ্যে অনেক জায়গায় ট্রিগার করা যায় এবং এটি ব্যবহারকারীর প্রসেসের মধ্যে চলতে পারে। পর্যাপ্ত softirq অ্যাক্টিভিটি থাকলে, ব্যবহারকারীর প্রসেস softirq চালানো বন্ধ করে দেবে, এবং ksoftirqd, softirq চালানোর জন্য জেগে উঠবে এবং লোড ব্যালেন্স হবে। সাধারণত, এটি ঠিক আছে। তবে, একটিমাত্র খুব দীর্ঘ softirq সিস্টেমে বিপর্যয় ডেকে আনতে পারে।


ট্রেসের irq বিভাগের মধ্যে softirqs দেখা যায়, তাই ট্রেস করার সময় সমস্যাটি আবার তৈরি করা গেলে, সেগুলি সহজেই শনাক্ত করা যায়। যেহেতু একটি softirq ব্যবহারকারীর প্রসেসের মধ্যে চলতে পারে, তাই একটি খারাপ softirq কোনও স্পষ্ট কারণ ছাড়াই ব্যবহারকারীর প্রসেসের মধ্যে অতিরিক্ত রানটাইম হিসেবেও প্রকাশ পেতে পারে। আপনি যদি এটি দেখতে পান, তাহলে irq বিভাগটি চেক করুন দেখতে যে softirqs দায়ী কিনা।

ড্রাইভাররা খুব বেশি সময়ের জন্য প্রি-এমপশন বা IRQ বন্ধ করে রাখছেন

প্রিএমশন বা ইন্টারাপ্ট অনেকক্ষণ (দশ মিলি সেকেন্ড) বন্ধ রাখলে জ্যাঙ্ক হয়। সাধারণত, জ্যাঙ্ক একটি থ্রেড হিসেবে প্রকাশ পায় যা রান করার উপযুক্ত হলেও নির্দিষ্ট CPU-তে রান করে না, এমনকি রান করার উপযুক্ত থ্রেডটি অন্য থ্রেডের চেয়ে উল্লেখযোগ্যভাবে উচ্চ অগ্রাধিকার (বা SCHED_FIFO) হলেও।

কিছু নির্দেশিকা:

  • রান করার উপযুক্ত থ্রেড SCHED_FIFO এবং রান করা থ্রেড SCHED_OTHER হলে, রান করা থ্রেডে প্রি-এম্পশন বা ইন্টারাপ্ট বন্ধ করা থাকে।
  • রান করার উপযুক্ত থ্রেডের অগ্রাধিকার (১০০) যদি রান করা থ্রেডের (১২০) থেকে অনেক বেশি হয়, তাহলে রান করা থ্রেডে সম্ভবত প্রিএম্পশন বা ইন্টারাপ্ট বন্ধ করা আছে, যদি রান করার উপযুক্ত থ্রেড দুই জিফির মধ্যে রান না করে।
  • রান করার উপযুক্ত থ্রেড এবং রান করা থ্রেডের একই অগ্রাধিকার থাকলে, রান করার উপযুক্ত থ্রেড ২০ মিলিসেকেন্ডের মধ্যে রান না করলে, রান করা থ্রেডের সম্ভবত প্রি-এমপশন বা ইন্টারাপ্ট বন্ধ করা আছে।

মনে রাখবেন, ইন্টারাপ্ট হ্যান্ডলার চালু থাকলে আপনি অন্য ইন্টারাপ্ট পরিচালনা করতে পারবেন না, এর ফলে প্রিএম্পশনও বন্ধ হয়ে যায়।


অবাধ্য অঞ্চল শনাক্ত করার আরেকটি বিকল্প হল preemptirqsoff tracer (ডায়নামিক ftrace ব্যবহার করা দেখুন)। এই ট্রেসারটি একটি নিরবচ্ছিন্ন অঞ্চলের মূল কারণ (যেমন ফাংশনের নাম) সম্পর্কে অনেক বেশি ইনসাইট দিতে পারে, তবে এটি চালু করার জন্য আরও ইনভেসিভ কাজ করতে হবে। এর ফলে পারফর্ম্যান্সের উপর প্রভাব পড়তে পারে, তবে এটি অবশ্যই চেষ্টা করে দেখা উচিত।

ওয়ার্ককিউয়ের ভুল ব্যবহার

ইন্টারাপ্ট হ্যান্ডলারকে প্রায়ই এমন কাজ করতে হয় যা ইন্টারাপ্ট কন্টেক্সটের বাইরেও চলতে পারে, যার ফলে কার্নেলের বিভিন্ন থ্রেডে কাজ ভাগ করে দেওয়া যায়। একজন ড্রাইভার ডেভেলপার লক্ষ্য করতে পারেন যে কার্নেলে খুব সুবিধাজনক সিস্টেম-ওয়াইড অ্যাসিঙ্ক্রোনাস টাস্ক ফাংশনালিটি আছে, যাকে ওয়ার্ককিউ বলা হয় এবং ইন্টারাপ্ট-সম্পর্কিত কাজের জন্য সেটি ব্যবহার করতে পারেন।

তবে, এই সমস্যার জন্য ওয়ার্ককিউ প্রায় সবসময়ই ভুল উত্তর কারণ সেগুলি সবসময়ই SCHED_OTHER হয়। অনেক হার্ডওয়্যার ইন্টারাপ্ট পারফর্ম্যান্সের গুরুত্বপূর্ণ পাথে থাকে এবং সেগুলি অবিলম্বে রান করতে হবে। Workqueue কখন রান করবে সেই ব্যাপারে কোনও গ্যারান্টি নেই। আমরা যখনই পারফর্ম্যান্সের ক্রিটিক্যাল পাথে ওয়ার্ককিউ দেখেছি, তখনই সেটি ডিভাইস নির্বিশেষে বিক্ষিপ্ত জ্যাঙ্কের সোর্স হিসেবে কাজ করেছে। Pixel-এ, ফ্ল্যাগশিপ প্রসেসরের সাথে, আমরা দেখেছি যে ডিভাইসটি লোড করা থাকলে, শিডিউলার বিহেভিয়ার এবং সিস্টেমে চলমান অন্যান্য জিনিসের উপর নির্ভর করে একটি সিঙ্গেল ওয়ার্ককিউ সর্বাধিক ৭ms পর্যন্ত বিলম্বিত হতে পারে।

ওয়ার্ককিউয়ের পরিবর্তে, যেসব ড্রাইভারকে আলাদা থ্রেডের মধ্যে ইন্টারাপ্ট-লাইক কাজ হ্যান্ডেল করতে হয়, তাদের নিজস্ব SCHED_FIFO kthread তৈরি করা উচিত। kthread_work ফাংশনের সাহায্যে এটি করার ব্যাপারে সহায়তা পেতে, এই প্যাচ দেখুন।

ফ্রেমওয়ার্ক লক কনটেনশন

ফ্রেমওয়ার্ক লক কনটেনশন, জ্যাঙ্ক বা অন্যান্য পারফর্ম্যান্স সংক্রান্ত সমস্যার কারণ হতে পারে। এটি সাধারণত ActivityManagerService লকের কারণে হয়, তবে অন্যান্য লকের ক্ষেত্রেও দেখা যেতে পারে। যেমন, PowerManagerService লক স্ক্রিনের পারফর্ম্যান্সের উপর প্রভাব ফেলতে পারে। আপনার ডিভাইসে এটি দেখতে পেলে, এর কোনও ভাল সমাধান নেই কারণ ফ্রেমওয়ার্কের আর্কিটেকচারে উন্নতি করার মাধ্যমেই শুধুমাত্র এটি উন্নত করা যেতে পারে। তবে, আপনি যদি system_server-এর মধ্যে রান করে এমন কোড পরিবর্তন করেন, তাহলে দীর্ঘ সময়ের জন্য লক হোল্ড করা এড়ানো গুরুত্বপূর্ণ, বিশেষ করে ActivityManagerService লক।

বাইন্ডার লক কনটেনশন

ঐতিহাসিকভাবে, বাইন্ডারে একটি গ্লোবাল লক থাকে। লক হোল্ড করার সময় কোনও বাইন্ডার ট্রানজ্যাকশন চালানো থ্রেড প্রিএম্পট করা হলে, আসল থ্রেড লক রিলিজ না করা পর্যন্ত অন্য কোনও থ্রেড বাইন্ডার ট্রানজ্যাকশন পারফর্ম করতে পারবে না। এটি খারাপ; বাইন্ডার কনটেনশন ডিসপ্লেতে UI আপডেট পাঠানো সহ সিস্টেমের সবকিছু ব্লক করতে পারে (UI থ্রেড বাইন্ডারের মাধ্যমে SurfaceFlinger-এর সাথে যোগাযোগ করে)।

Android 6.0-এ বাইন্ডার লক হোল্ড করার সময় প্রিএম্পশন বন্ধ করে এই আচরণ উন্নত করার জন্য একাধিক প্যাচ অন্তর্ভুক্ত করা হয়েছে। এটি নিরাপদ ছিল কারণ বাইন্ডার লকটি প্রকৃত রানটাইমের কয়েক মাইক্রোসেকেন্ডের জন্য ধরে রাখা উচিত। এর ফলে, কোনও প্রতিদ্বন্দ্বিতা না থাকলে পারফর্ম্যান্স অনেক উন্নত হয় এবং বাইন্ডার লক হোল্ড করা থাকাকালীন বেশিরভাগ শিডিউলার সুইচ প্রতিরোধ করার মাধ্যমে প্রতিদ্বন্দ্বিতা প্রতিরোধ করা হয়। তবে, বাইন্ডার লক হোল্ড করার সম্পূর্ণ রানটাইমের জন্য প্রি-এমপশন বন্ধ করা যায়নি, যার অর্থ হল, স্লিপ করতে পারে এমন ফাংশনের জন্য প্রি-এমপশন চালু করা হয়েছিল (যেমন, copy_from_user), যা আসল কেসের মতো একই প্রি-এমপশনের কারণ হতে পারে। আমরা যখন আপস্ট্রিম প্যাচ পাঠালাম, তারা সঙ্গে সঙ্গে আমাদের জানাল যে এটি ইতিহাসের সবচেয়ে খারাপ আইডিয়া। (আমরা তাদের সাথে একমত হয়েছি, কিন্তু আমরা জ্যাঙ্ক প্রতিরোধ করার জন্য প্যাচের কার্যকারিতা সম্পর্কেও কোনও যুক্তি দিতে পারিনি।)

কোনও প্রসেসের মধ্যে fd কনটেনশন

এটি খুব কম দেখা যায়। আপনার জ্যাঙ্ক সম্ভবত এর কারণে হয়নি।

তবে, কোনও প্রসেসের মধ্যে একাধিক থ্রেড একই fd লিখলে, এই fd-তে কনটেনশন দেখা যেতে পারে, তবে Pixel বুট-আপের সময় আমরা এটি শুধুমাত্র একটি পরীক্ষার সময় দেখেছি যেখানে কম-অগ্রাধিকারযুক্ত থ্রেডগুলি সমস্ত CPU টাইম দখল করার চেষ্টা করছিল, একই প্রসেসের মধ্যে একটি উচ্চ-অগ্রাধিকারযুক্ত থ্রেড চলছিল। সব থ্রেডই ট্রেস মার্কার fd-তে লিখছিল এবং কম অগ্রাধিকারের থ্রেড fd লক হোল্ড করে থাকলে এবং তারপরে প্রিএম্পট করা হলে, বেশি অগ্রাধিকারের থ্রেড ট্রেস মার্কার fd-তে ব্লক হয়ে যেতে পারত। কম অগ্রাধিকারের থ্রেড থেকে ট্রেসিং বন্ধ করা হলে পারফর্ম্যান্স সংক্রান্ত কোনও সমস্যা হয়নি।

আমরা অন্য কোনও পরিস্থিতিতে এটি আবার তৈরি করতে পারিনি, তবে ট্রেস করার সময় পারফর্ম্যান্স সংক্রান্ত সমস্যার সম্ভাব্য কারণ হিসেবে এটি উল্লেখ করা যেতে পারে।

অপ্রয়োজনীয় সিপিইউ আইডল ট্রানজিশন

IPC, বিশেষ করে মাল্টি-প্রসেস পাইপলাইন নিয়ে কাজ করার সময়, নিম্নলিখিত রানটাইম আচরণে পরিবর্তন দেখা যাওয়া সাধারণ ব্যাপার:

  1. থ্রেড 'ক' সিপিইউ ১-এ রান করে।
  2. থ্রেড 'ক' থ্রেড 'খ'-কে জাগিয়ে তোলে।
  3. থ্রেড B, CPU 2-তে রান করা শুরু করে।
  4. থ্রেড A সঙ্গে সঙ্গে স্লিপ মোডে চলে যায়, থ্রেড B তার বর্তমান কাজ শেষ করলে থ্রেড A জেগে ওঠে।

ওভারহেডের একটি সাধারণ সোর্স হল ধাপ ২ এবং ৩-এর মধ্যে। CPU ২ আইডল থাকলে, থ্রেড B রান করার আগে সেটিকে অ্যাক্টিভ স্টেটে ফিরিয়ে আনতে হবে। SOC এবং আইডল কতটা গভীর তার উপর নির্ভর করে, থ্রেড B চলা শুরু করার আগে এটি কয়েক মাইক্রোসেকেন্ড হতে পারে। IPC-এর প্রতিটি দিকের প্রকৃত রানটাইম যদি ওভারহেডের কাছাকাছি হয়, তাহলে CPU আইডল ট্রানজিশনের কারণে সেই পাইপলাইনের সামগ্রিক পারফর্ম্যান্স উল্লেখযোগ্যভাবে কমে যেতে পারে। Android-এ এটি হওয়ার সবচেয়ে সাধারণ জায়গা হল বাইন্ডার ট্রানজ্যাকশন এবং বাইন্ডার ব্যবহার করে এমন অনেক পরিষেবা উপরে বর্ণিত পরিস্থিতির মতো হয়ে যায়।

প্রথমে, আপনার কার্নেল ড্রাইভারগুলিতে wake_up_interruptible_sync() ফাংশন ব্যবহার করুন এবং যেকোনও কাস্টম শিডিউলার থেকে এটি সাপোর্ট করুন। এটি একটি প্রয়োজনীয়তা হিসেবে বিবেচনা করুন, কোনও ইঙ্গিত হিসেবে নয়। Binder এটি আজ ব্যবহার করে এবং এটি সিঙ্ক্রোনাস বাইন্ডার ট্রানজ্যাকশনের ক্ষেত্রে অনেক সাহায্য করে। এর ফলে সিপিইউকে অপ্রয়োজনীয় আইডল ট্রানজিশন এড়িয়ে যেতে সাহায্য করে।

দ্বিতীয়ত, আপনার cpuidle ট্রানজিশন টাইম বাস্তবসম্মত কিনা এবং cpuidle গভর্নর এগুলি সঠিকভাবে বিবেচনা করছে কিনা তা নিশ্চিত করুন। আপনার SOC যদি সবচেয়ে গভীর আইডল স্টেট থেকে বেরিয়ে আসে এবং আবার সেখানে যায়, তাহলে সবচেয়ে গভীর আইডল স্টেটে গিয়ে আপনি পাওয়ার সেভ করতে পারবেন না।

লগিং

সিপিইউ সাইকেল বা মেমরির জন্য লগিং ফ্রি নয়, তাই লগ বাফার স্প্যাম করবেন না। আপনার অ্যাপে (সরাসরি) এবং লগ ডেমন-এ লগিং খরচ চক্র। আপনার ডিভাইস শিপিং করার আগে যেকোনও ডিবাগিং লগ সরিয়ে দিন।

I/O সংক্রান্ত সমস্যা

ইনপুট/আউটপুট অপারেশন হল জিটারের সাধারণ সোর্স। কোনও থ্রেড যদি মেমরি-ম্যাপ করা ফাইল অ্যাক্সেস করে এবং পৃষ্ঠাটি যদি পৃষ্ঠা ক্যাশেতে না থাকে, তাহলে এটি ফল্ট করে এবং ডিস্ক থেকে পৃষ্ঠাটি পড়ে। এটি থ্রেডকে ব্লক করে (সাধারণত ১০+ মিলিসেকেন্ডের জন্য) এবং এটি যদি UI রেন্ডারিংয়ের ক্রিটিক্যাল পাথে ঘটে, তাহলে এর ফলে জাঙ্ক হতে পারে। এখানে আলোচনা করার জন্য I/O অপারেশন সংক্রান্ত অনেক কারণ আছে, তবে I/O আচরণ উন্নত করার চেষ্টা করার সময় নিম্নলিখিত লোকেশনগুলি চেক করুন:

  • PinnerService. Android 7.0-এ যোগ করা হয়েছে, PinnerService ফ্রেমওয়ার্ককে পেজ ক্যাশেতে কিছু ফাইল লক করার সুবিধা দেয়। এর ফলে অন্য কোনও প্রসেস ব্যবহার করার জন্য মেমরি মুছে যায়, কিন্তু যদি এমন কিছু ফাইল থাকে যেগুলি নিয়মিত ব্যবহার করা হয় বলে আগে থেকেই জানা থাকে, তাহলে সেইসব ফাইল mlock করলে তা কার্যকর হতে পারে।

    Android 7.0 ভার্সনে চলা Pixel এবং Nexus 6P ডিভাইসে আমরা চারটি ফাইল mlock করেছি:
    • /system/framework/arm64/boot-framework.oat
    • /system/framework/oat/arm64/services.odex
    • /system/framework/arm64/boot.oat
    • /system/framework/arm64/boot-core-libart.oat
    এইসব ফাইল বেশিরভাগ অ্যাপ ও system_server সবসময় ব্যবহার করে, তাই এগুলি পেজ আউট করা উচিত নয়। বিশেষ করে, আমরা দেখেছি যে এগুলির মধ্যে কোনও একটি পেজ আউট করা হলে, সেটি আবার পেজ ইন করা হবে এবং কোনও ভারী অ্যাপ থেকে পাল্টানোর সময় জ্যাঙ্ক তৈরি হবে।
  • এনক্রিপশন। I/O সমস্যার আরেকটি সম্ভাব্য কারণ। আমরা দেখেছি যে সিপিইউ-ভিত্তিক এনক্রিপশন বা ডিএমএ-এর মাধ্যমে অ্যাক্সেসযোগ্য হার্ডওয়্যার ব্লক ব্যবহারের তুলনায় ইনলাইন এনক্রিপশন সবচেয়ে ভালো পারফর্ম্যান্স অফার করে। সবচেয়ে গুরুত্বপূর্ণ হল, ইনলাইন এনক্রিপশন I/O-এর সাথে যুক্ত জিটার কমায়, বিশেষ করে যখন CPU-ভিত্তিক এনক্রিপশনের সাথে তুলনা করা হয়। কারণ, পৃষ্ঠা ক্যাশে থেকে ডেটা ফেচ করা প্রায়ই UI রেন্ডারিংয়ের ক্রিটিক্যাল পাথে থাকে। CPU-ভিত্তিক এনক্রিপশন ক্রিটিক্যাল পাথে অতিরিক্ত CPU লোড যোগ করে, যা শুধু I/O ফেচের চেয়ে বেশি জিটার যোগ করে।

    DMA-ভিত্তিক হার্ডওয়্যার এনক্রিপশন ইঞ্জিনে একই ধরনের সমস্যা হয়, কারণ কার্নেলকে সেই কাজ ম্যানেজ করার জন্য সাইকেল খরচ করতে হয়, এমনকি যদি চালানোর জন্য অন্য কোনও গুরুত্বপূর্ণ কাজ উপলভ্য থাকে। আমরা নতুন হার্ডওয়্যার তৈরি করা যেকোনও SOC ভেন্ডরকে ইনলাইন এনক্রিপশনের জন্য সহায়তা অন্তর্ভুক্ত করার জন্য বিশেষভাবে সাজেস্ট করি।

ছোট ছোট টাস্কের আগ্রাসী প্যাকিং

কিছু শিডিউলার ছোট ছোট কাজকে একটি CPU কোরে প্যাক করার সুবিধা দেয় যাতে আরও বেশি CPU-কে দীর্ঘ সময় ধরে অলস রেখে বিদ্যুৎ খরচ কমানো যায়। থ্রুপুট ও বিদ্যুৎ খরচের ক্ষেত্রে এটি ভালভাবে কাজ করলেও, লেটেন্সির ক্ষেত্রে এটি বিপর্যয়কর হতে পারে। UI রেন্ডারিংয়ের ক্রিটিকাল পাথে বেশ কিছু ছোটো থ্রেড আছে যেগুলিকে ছোটো হিসেবে বিবেচনা করা যেতে পারে; এই থ্রেডগুলি যদি অন্যান্য সিপিইউতে ধীরে ধীরে মাইগ্রেট করার কারণে দেরি হয়, তাহলে এটি জ্যাঙ্ক তৈরি করবে। আমরা খুব সাবধানে ছোট টাস্ক প্যাকিং ব্যবহার করার সাজেশন দিই।

পেজ ক্যাশে থ্র্যাশিং

পর্যাপ্ত ফ্রি মেমরি না থাকা ডিভাইস, নতুন অ্যাপ খোলার মতো দীর্ঘ সময় ধরে চলা কোনও অপারেশন করার সময় হঠাৎ অত্যন্ত ধীরগতির হয়ে যেতে পারে। অ্যাপের ট্রেস থেকে দেখা যেতে পারে যে, নির্দিষ্ট কোনও রান চলাকালীন I/O-তে এটি ক্রমাগত ব্লক হয়ে যাচ্ছে, যদিও প্রায়শই এটি I/O-তে ব্লক হয় না। এটি সাধারণত পেজ ক্যাশে থ্র্যাশিংয়ের লক্ষণ, বিশেষ করে কম মেমরি থাকা ডিভাইসে।

এটি শনাক্ত করার একটি উপায় হল, pagecache ট্যাগ ব্যবহার করে একটি systrace নেওয়া এবং system/extras/pagecache/pagecache.py-এ স্ক্রিপ্টে সেই ট্রেস ফিড করা। pagecache.py, প্রতিটি ফাইলের জন্য মোট পরিসংখ্যান পেতে, পেজ ক্যাশেতে ম্যাপ করা ফাইলগুলির জন্য আলাদা আলাদা অনুরোধ ট্রান্সলেট করে। আপনি যদি দেখেন যে কোনও ফাইলের মোট সাইজের চেয়ে বেশি বাইট ডিস্কে রিড করা হয়েছে, তাহলে আপনি নিশ্চিতভাবেই পেজ ক্যাশে থ্র্যাশিংয়ের সম্মুখীন হয়েছেন।

এর অর্থ হল, আপনার ওয়ার্কলোডের জন্য প্রয়োজনীয় ওয়ার্কিং সেট (সাধারণত একটি অ্যাপ ও system_server) হল আপনার ডিভাইসে উপলভ্য মেমরির পরিমাণের চেয়ে বেশি। এর ফলে, ওয়ার্কলোডের একটি অংশ পেজ ক্যাশেতে প্রয়োজনীয় ডেটা পেয়ে গেলে, অন্য একটি অংশ যা অদূর ভবিষ্যতে ব্যবহার করা হবে তা সরিয়ে দেওয়া হবে এবং আবার ফেচ করতে হবে, এর ফলে লোড সম্পূর্ণ না হওয়া পর্যন্ত সমস্যাটি আবার হতে থাকবে। ডিভাইসে পর্যাপ্ত মেমরি উপলভ্য না থাকলে, পারফর্ম্যান্স সংক্রান্ত সমস্যার এটিই হল মূল কারণ।

পেজ ক্যাশে থ্র্যাশিং ঠিক করার কোনও নিশ্চিত উপায় নেই, তবে কোনও নির্দিষ্ট ডিভাইসে এটি উন্নত করার চেষ্টা করার কয়েকটি উপায় আছে।

  • দীর্ঘস্থায়ী প্রসেসে কম মেমরি ব্যবহার করুন। পারসিস্টেন্ট প্রসেস যত কম মেমরি ব্যবহার করবে, অ্যাপ ও পৃষ্ঠা ক্যাশের জন্য তত বেশি মেমরি উপলভ্য হবে।
  • আপনার ডিভাইসের জন্য কার্ভ-আউট অডিট করুন যাতে আপনি অপ্রয়োজনীয়ভাবে OS থেকে মেমরি সরিয়ে না দেন তা নিশ্চিত করতে পারেন। আমরা এমন পরিস্থিতি দেখেছি যেখানে ডিবাগিংয়ের জন্য ব্যবহৃত কার্ভআউট শিপিং কার্নেল কনফিগারেশনে ভুলবশত থেকে গেছে, যার ফলে কয়েক মেগাবাইট মেমরি খরচ হয়ে গেছে। এর ফলে, বিশেষ করে কম মেমরি থাকা ডিভাইসে, পেজ ক্যাশে থ্র্যাশিং হিট করা ও না করার মধ্যে পার্থক্য তৈরি হতে পারে।
  • আপনি যদি গুরুত্বপূর্ণ ফাইলে system_server-এ পৃষ্ঠা ক্যাশে থ্র্যাশিং দেখতে পান, তাহলে সেই ফাইলগুলি পিন করার কথা বিবেচনা করুন। এর ফলে অন্য কোথাও মেমরির উপর চাপ বাড়বে, কিন্তু এটি থ্র্যাশিং এড়াতে আচরণকে যথেষ্ট পরিবর্তন করতে পারে।
  • আরও মেমরি খালি রাখার জন্য lowmemorykiller রিটিউন করুন। lowmemorykiller-এর থ্রেশহোল্ড সম্পূর্ণ খালি মেমরি ও পৃষ্ঠা ক্যাশে, দুটির উপরেই নির্ভর করে, তাই যে থ্রেশহোল্ডে নির্দিষ্ট oom_adj লেভেলে প্রসেস বন্ধ করা হয়, সেটি বাড়ালে ব্যাকগ্রাউন্ড অ্যাপ বন্ধ হওয়ার সংখ্যা বাড়লেও, আরও ভালো পারফর্ম্যান্স পাওয়া যেতে পারে।
  • ZRAM ব্যবহার করে দেখুন। Pixel-এ ৪ জিবি RAM থাকলেও আমরা ZRAM ব্যবহার করি, কারণ এটি কখনও কখনও ব্যবহৃত ডার্টি পেজের ক্ষেত্রে সাহায্য করতে পারে।