অ্যাপ সাইজ ও স্টার্টআপ টাইম অপ্টিমাইজেশন
এই পাঠে যা শিখবেন
- কোল্ড স্টার্ট ও ওয়ার্ম স্টার্টের মধ্যে ঠিক কী পার্থক্য, এবং
AppLifecycleস্টেট মেশিনের কোন ট্রানজিশন কোনটির সাথে মেলে - একটি বাস্তব স্টার্টআপ-সময় মডেল ($base\_init + n\_lazy\_modules \times per\_module$) দিয়ে কোল্ড স্টার্টের সময় হিসাব করা
- একাধিক পরিস্থিতির (প্রথমবার চালু, রিজিউম, kill হওয়ার পর আবার চালু) মধ্য দিয়ে
AppLifecycleচালিয়ে প্রতিটির প্রকৃত স্টার্টআপ সময় প্রিন্ট করা - অ্যাপের আকার (মডিউল সংখ্যা) কীভাবে সরাসরি কোল্ড স্টার্টের সময় বাড়িয়ে দেয়, অথচ ওয়ার্ম স্টার্টে তার কোনো প্রভাব নেই
১ · কোল্ড বনাম ওয়ার্ম স্টার্ট
M1 (L01/L04)-এর AppLifecycle স্টেট মেশিনে not_running, foreground,
background, ও killed — এই স্টেটগুলো ছিল। foreground-এ যাওয়ার প্রতিটি
ট্রানজিশন সমান নয় — এটি ঠিক কোন স্টেট থেকে ঘটছে তার উপর নির্ভর করে দুটি সম্পূর্ণ ভিন্ন বাস্তবতা তৈরি হয়:
not_running -> foreground — প্রসেস তৈরি, সব সার্ভিস/মডিউল ইনিশিয়ালাইজ করা লাগে।background -> foreground — প্রসেস ইতিমধ্যে সচল, শুধু UI দৃশ্যমান করা লাগে।
একটি অ্যাপ killed স্টেটে গেলে সেটি একটি টার্মিনাল স্টেট — পরের বার চালু হওয়া মানেই একটি নতুন
প্রসেস, অর্থাৎ আবার not_running -> foreground-এর মধ্য দিয়ে যাওয়া, ফলে সেটিও একটি কোল্ড স্টার্ট।
২ · একটি বাস্তব স্টার্টআপ-সময় মডেল
কোল্ড স্টার্টের সময়কে একটি সরল সূত্রে মডেল করা যায় — একটি স্থির বেস ইনিশিয়ালাইজেশন সময়, প্লাস প্রতিটি lazy-loaded মডিউল ইনিশিয়ালাইজ করার সময়:
$$\text{cold\_time} = \text{base\_init} + n\_lazy\_modules \times \text{per\_module}$$
ওয়ার্ম স্টার্টে এই মডিউল-নির্ভর হিসাবের দরকার নেই — প্রসেসটি আগে থেকেই ইনিশিয়ালাইজড, তাই সময় একটি ছোট,
প্রায় স্থির resume_time। নিচের কোড সেলে AppLifecycle স্টেট মেশিন চালিয়ে দুটি
পরিস্থিতির প্রকৃত সময় হিসাব করা হচ্ছে।
# M1-এর AppLifecycle স্টেট মেশিন পুনর্ব্যবহার করা হচ্ছে
class AppLifecycle:
ALLOWED = {
'not_running': {'foreground'},
'foreground': {'background'},
'background': {'foreground', 'killed'},
'killed': set(),
}
def __init__(self):
self.state = 'not_running'
def transition(self, new_state, reason):
if new_state in self.ALLOWED[self.state]:
old_state = self.state
self.state = new_state
print(f"{reason:40s} | {old_state} -> {new_state}")
return old_state
else:
print(f"{reason:40s} | {self.state} -> {new_state} (অবৈধ, প্রত্যাখ্যাত)")
return None
BASE_INIT_MS = 380 # যেকোনো কোল্ড স্টার্টে বাধ্যতামূলক বেস ইনিশিয়ালাইজেশন সময়
PER_MODULE_MS = 45 # প্রতিটি lazy-loaded মডিউল ইনিশিয়ালাইজ করার অতিরিক্ত সময়
RESUME_MS = 60 # ওয়ার্ম রিজিউমে একটি ছোট, স্থির সময়
def compute_startup_time(prev_state, n_lazy_modules=0):
if prev_state == 'not_running':
total = BASE_INIT_MS + n_lazy_modules * PER_MODULE_MS
return 'কোল্ড স্টার্ট', total
elif prev_state == 'background':
return 'ওয়ার্ম স্টার্ট', RESUME_MS
else:
raise ValueError(f"foreground-এ ট্রানজিশন সম্ভব না এই state থেকে: {prev_state}")
app = AppLifecycle()
# পরিস্থিতি A: ইউজার প্রথমবার অ্যাপ চালু করলো (not_running -> foreground)
prev = app.state
app.transition('foreground', 'ইউজার প্রথমবার অ্যাপ চালু করলো')
kind_a, time_a = compute_startup_time(prev, n_lazy_modules=6)
print(f"-> {kind_a}, ৬টি lazy module সহ, সময়: {time_a} ms\n")
app.transition('background', 'ইউজার হোম বাটন চাপলো')
# পরিস্থিতি B: ইউজার কিছুক্ষণ পর আবার অ্যাপে ফিরে এলো (background -> foreground)
prev = app.state
app.transition('foreground', 'ইউজার কিছুক্ষণ পর আবার অ্যাপে ফিরে এলো')
kind_b, time_b = compute_startup_time(prev)
print(f"-> {kind_b}, সময়: {time_b} ms")
prev_state ছিল not_running, তাই এটি কোল্ড স্টার্ট: $380 + 6 \times 45
= 650$ms। পরিস্থিতি B-তে prev_state ছিল background, তাই এটি ওয়ার্ম স্টার্ট: শুধু
$60$ms। একই অ্যাপ, শুধু আগের স্টেট ভিন্ন হওয়ায় সময়ের পার্থক্য প্রায় ১১ গুণ।
৩ · Kill হওয়ার পর আবার চালু করা — এবং চূড়ান্ত তুলনা
যেহেতু killed একটি টার্মিনাল স্টেট, তাই এই অবস্থা থেকে পুনরায় চালু হওয়া মানেই একটি নতুন
AppLifecycle ইনস্ট্যান্স — অর্থাৎ আরেকটি কোল্ড স্টার্ট, ওয়ার্ম স্টার্ট নয়। নিচের কোড সেলে এটি
যাচাই করে দুটি স্টার্ট টাইপের মধ্যে চূড়ান্ত সংখ্যাগত পার্থক্য বের করা হচ্ছে।
class AppLifecycle:
ALLOWED = {
'not_running': {'foreground'},
'foreground': {'background'},
'background': {'foreground', 'killed'},
'killed': set(),
}
def __init__(self):
self.state = 'not_running'
def transition(self, new_state, reason):
if new_state in self.ALLOWED[self.state]:
old_state = self.state
self.state = new_state
print(f"{reason:40s} | {old_state} -> {new_state}")
return old_state
else:
print(f"{reason:40s} | {self.state} -> {new_state} (অবৈধ, প্রত্যাখ্যাত)")
return None
BASE_INIT_MS = 380
PER_MODULE_MS = 45
RESUME_MS = 60
def compute_startup_time(prev_state, n_lazy_modules=0):
if prev_state == 'not_running':
total = BASE_INIT_MS + n_lazy_modules * PER_MODULE_MS
return 'কোল্ড স্টার্ট', total
elif prev_state == 'background':
return 'ওয়ার্ম স্টার্ট', RESUME_MS
else:
raise ValueError(f"foreground-এ ট্রানজিশন সম্ভব না এই state থেকে: {prev_state}")
# পরিস্থিতি C: অ্যাপ ব্যাকগ্রাউন্ডে থাকা অবস্থায় OS মেমোরি খালি করতে বন্ধ করে দিলো
app = AppLifecycle()
app.transition('foreground', 'অ্যাপ চালু করা হলো')
app.transition('background', 'হোম বাটন চাপলো')
app.transition('killed', 'OS মেমোরি খালি করতে ব্যাকগ্রাউন্ড অ্যাপ বন্ধ করে দিলো')
# 'killed' টার্মিনাল স্টেট -- নতুন করে চালু করতে নতুন AppLifecycle ইনস্ট্যান্স লাগে
relaunched_app = AppLifecycle()
prev = relaunched_app.state # 'not_running'
relaunched_app.transition('foreground', 'kill হওয়ার পর ইউজার আবার অ্যাপ চালু করলো')
kind_c, time_c = compute_startup_time(prev, n_lazy_modules=6)
print(f"-> {kind_c}, ৬টি lazy module সহ, সময়: {time_c} ms")
# চূড়ান্ত তুলনা
cold_time = time_c
warm_time = RESUME_MS
difference = cold_time - warm_time
ratio = cold_time / warm_time
print(f"\nকোল্ড স্টার্ট: {cold_time} ms")
print(f"ওয়ার্ম স্টার্ট: {warm_time} ms")
print(f"পার্থক্য: {difference} ms")
print(f"কোল্ড স্টার্ট ওয়ার্ম স্টার্টের চেয়ে {ratio:.2f}x ধীর")
relaunched_app-এর prev_state ছিল
not_running — তাই এটিও কোল্ড স্টার্ট, ৬৫০ms। ফলে চূড়ান্ত তুলনায় কোল্ড স্টার্ট (৬৫০ms) ওয়ার্ম
স্টার্টের (৬০ms) চেয়ে ৫৯০ms বেশি সময় নেয় — প্রায় ১০.৮৩ গুণ ধীর।
কোল্ড স্টার্টের সময় সরাসরি অ্যাপের আকারের (কতগুলো মডিউল ইনিশিয়ালাইজ করতে হয়) সাথে সমানুপাতিক — তাই অপ্রয়োজনীয়
মডিউল lazy-load থেকে বাদ দেওয়া বা দেরিতে (on-demand) লোড করা সরাসরি কোল্ড স্টার্টের সময় কমায়। ওয়ার্ম স্টার্ট
এই খরচ থেকে সম্পূর্ণ মুক্ত, কারণ প্রসেসটি ইতিমধ্যে সচল থাকে — এটিই AppLifecycle-এর আগের স্টেট
দিয়ে দুটি ভিন্ন বাস্তবতা নির্ভুলভাবে আলাদা করার কারণ।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
compute_startup_time ফাংশনটি prev_state == 'foreground' বা
'killed' হলে ValueError রেইজ করে কেন?
কারণ AppLifecycle.ALLOWED অনুযায়ী foreground বা killed থেকে সরাসরি
foreground-এ ট্রানজিশন কখনোই বৈধ নয় (একটি অ্যাপ যা ইতিমধ্যে ফোরগ্রাউন্ডে আছে তার আবার
"স্টার্ট" হওয়ার প্রশ্নই আসে না, আর killed থেকে সরাসরি foreground-এ যাওয়া অসম্ভব — আগে অবশ্যই একটি নতুন
not_running ইনস্ট্যান্স লাগবে)। তাই এই ফাংশনটি এমন একটি অবস্থার জন্য ডাকা হলে তা একটি প্রোগ্রামিং ভুল
নির্দেশ করে, যা ValueError দিয়ে স্পষ্টভাবে জানানো হয়।
প্র ০২
দ্বিতীয় কোড সেলে কেন সরাসরি প্রথম সেলের app ভ্যারিয়েবল আবার ব্যবহার না করে নতুন করে সবকিছু সংজ্ঞায়িত করা হলো?
প্রতিটি কোড সেল একটি স্বয়ংসম্পূর্ণ, স্বাধীন উদাহরণ হিসেবে ডিজাইন করা হয়েছে, যাতে যেকোনো একটি সেল আলাদাভাবে
চালালেও (আগের সেল না চালিয়ে) সঠিক ফলাফল পাওয়া যায়। বাস্তব কোডে অবশ্যই একই AppLifecycle
ক্লাস ও কনস্ট্যান্টগুলো একবারই সংজ্ঞায়িত হতো এবং পুরো অ্যাপ জুড়ে পুনর্ব্যবহৃত হতো।
প্র ০৩
একটি অ্যাপের n_lazy_modules কমালে ওয়ার্ম স্টার্টের সময়েও কি কোনো উন্নতি হবে?
না — কোড সেলে compute_startup_time-এর 'background' শাখাটি
n_lazy_modules প্যারামিটারটি একেবারেই ব্যবহার করে না, শুধু স্থির RESUME_MS
ফেরত দেয়। এটিই বাস্তবতার সাথে সামঞ্জস্যপূর্ণ — ওয়ার্ম স্টার্টে মডিউলগুলো ইতিমধ্যে মেমোরিতে ইনিশিয়ালাইজড
অবস্থায় আছে, তাই অ্যাপের আকার কমানো শুধু কোল্ড স্টার্টকেই দ্রুত করে, ওয়ার্ম স্টার্টকে নয়।
অনুশীলন
-
চিন্তা করুন: আপনার ফোনে এমন কোনো অ্যাপের কথা মনে করুন যা মাঝে মাঝে খুব দ্রুত খোলে, আবার
মাঝে মাঝে কয়েক সেকেন্ড সময় নেয় — এই দুটি অভিজ্ঞতার পেছনে কি কোল্ড/ওয়ার্ম স্টার্টের পার্থক্যই দায়ী হতে পারে?
হ্যাঁ, এটিই সবচেয়ে সাধারণ কারণ — যখন অ্যাপটি সম্প্রতি ব্যবহার করা হয়েছিল এবং OS এখনো সেটিকে মেমোরিতে রেখেছে, তখন খোলা মানে ওয়ার্ম স্টার্ট (দ্রুত)। কিন্তু অনেকক্ষণ পর, বা অনেক অ্যাপ ব্যবহারের ফলে OS মেমোরি বাঁচাতে অ্যাপটি ইতিমধ্যে
killedকরে দিয়েছে থাকলে, পরের বার খোলা মানে কোল্ড স্টার্ট (ধীর)। -
পরীক্ষা করুন: প্রথম কোড সেলে পরিস্থিতি A-তে
n_lazy_modules=6-এর বদলেn_lazy_modules=12ব্যবহার করুন (একটি বড়, বেশি ফিচারসমৃদ্ধ অ্যাপ কল্পনা করুন), তারপর নতুনtime_aমান হাতে হিসাব করে যাচাই করুন কোড কী প্রিন্ট করবে।নতুন
time_aহবে $380 + 12 \times 45 = 380 + 540 = 920$ms — অর্থাৎ মডিউল সংখ্যা দ্বিগুণ হওয়ায় কোল্ড স্টার্টের সময় ৬৫০ms থেকে বেড়ে ৯২০ms হয়ে যায় (২৭০ms বেশি), আবার প্রমাণ করে যে অ্যাপের আকার বাড়লে কোল্ড স্টার্ট সরাসরি ধীর হয়ে যায়।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ অ্যাপ লাইফসাইকেল, মোবাইল UI/UX, MVVM/MVI আর্কিটেকচার, নেভিগেশন, স্টেট ম্যানেজমেন্ট, লোকাল স্টোরেজ, নেটওয়ার্কিং, ডিভাইস ফিচার, পারফরম্যান্স, টেস্টিং ও ডিপ্লয়মেন্ট — সব একসাথে।
- পরের পাঠ: মোবাইল টেস্টিং পিরামিড L50 পরের মডিউলের শুরু — ইউনিট, ইন্টিগ্রেশন ও UI টেস্টের মধ্যে সঠিক অনুপাত কেন গুরুত্বপূর্ণ।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Programming Languages & Compiler Design, Software Engineering & Git, Theory of Computation, Engineering Economics, Full-Stack Web Frameworks ও Mobile App Development — সব এক জায়গায়।