Android-ന്റെ build tools ഇപ്പോൾ Kotlin Coroutines-ന് കൂടുതൽ വേഗത നൽകുന്നു. Android Gradle Plugin (AGP) 9.2.0-ഉം അതിനൊപ്പം വരുന്ന R8 shrinker-ഉം ഉപയോഗിച്ച്, coroutine state handling-ന് സഹായിക്കുന്ന bytecode പുനർനിർമ്മിച്ചിരിക്കുന്നു. ഇത് സാധാരണ coroutine പ്രവർത്തനങ്ങളിൽ ഏകദേശം ഇരട്ടി വേഗത നൽകുന്നു. Coroutines ഉപയോഗിക്കുന്ന Android ആപ്പുകൾ വികസിപ്പിക്കുന്നവർക്ക്, ഒരു വരി പോലും Kotlin കോഡ് മാറ്റാതെ തന്നെ വേഗത്തിലുള്ള cold-start times, സ്ഥിരതയുള്ള frame rates, മികച്ച ബാറ്ററി ലൈഫ് എന്നിവ ലഭിക്കും.

എന്തുകൊണ്ടാണ് coroutine പെർഫോമൻസ് പ്രധാനപ്പെട്ടത്

പല threads-ൽ നിന്നും ഇന്റേണൽ ഫീൽഡുകൾ സുരക്ഷിതമായി മാറ്റുന്നതിനായി Kotlin Coroutines AtomicFieldUpdater ഒബ്‌ജക്റ്റുകൾ ഉപയോഗിക്കുന്നു. ഓരോ അപ്‌ഡേറ്റിനും പുതിയൊരു atomic ഒബ്‌ജക്റ്റ് മാറ്റിവെക്കുന്നത് ഒഴിവാക്കാൻ ഈ രീതി സഹായിക്കുന്നുണ്ടെങ്കിലും, ആൻഡ്രോയിഡിൽ ഇത് മൂന്ന് പ്രശ്നങ്ങൾ ഉണ്ടാക്കുന്നു:

  • Class-loading delay – updater റഫ്ലക്റ്റീവ് (reflectively) ആയിട്ടാണ് നിർമ്മിക്കുന്നത്, അതിനാൽ VM റൺടൈമിൽ ടാർഗെറ്റ് ഫീൽഡ് തിരയേണ്ടി വരുന്നു.
  • CPU waste – ഓരോ തവണ ആക്സസ് ചെയ്യുമ്പോഴും യഥാർത്ഥ ഫീൽഡിൽ എത്തുന്നതിന് മുമ്പ് updater-ന്റെ സ്റ്റേറ്റ് പരിശോധിക്കേണ്ടി വരുന്നു.
  • Inlining limits – കംപൈലറിന് updater കോളുകൾ inline ചെയ്യാൻ കഴിയില്ല, ഇത് ജനറേറ്റ് ചെയ്യപ്പെടുന്ന bytecode വലുതാക്കാനും വേഗത കുറയ്ക്കാനും കാരണമാകുന്നു.

പ്രായോഗികമായി, channel send/receive, mutex lock/unlock, StateFlow updates, coroutine dispatch തുടങ്ങിയ പ്രധാനപ്പെട്ട പ്രവർത്തനങ്ങളിൽ ഈ പ്രശ്നങ്ങൾ അനുഭവപ്പെടുന്നു. ഇതിന്റെ ഫലമായി, ആപ്പിന്റെ യഥാർത്ഥ പ്രവർത്തനത്തേക്കാൾ കൂടുതൽ CPU സമയം ഡാറ്റാ കൈകാര്യം ചെയ്യുന്നതിനായി (bookkeeping) ചിലവഴിക്കേണ്ടി വരുന്നു.

AGP 9.2.0-ലും R8-ലും എന്ത് മാറ്റമാണ് വന്നത്

AGP 9.2.0-നൊപ്പം വരുന്ന കോഡ്-shrink ചെയ്യാനുപയോഗിക്കുന്ന R8, ഇപ്പോൾ കംപൈൽ ചെയ്ത bytecode-ൽ സ്റ്റാൻഡേർഡ് AtomicFieldUpdater പാറ്റേൺ ഉണ്ടോ എന്ന് പരിശോധിക്കുന്നു. ഇത് കണ്ടെത്തിയാൽ, R8 നാല് മാറ്റങ്ങൾ വരുത്തുന്നു:

  1. Identify the field’s memory offset. ഒബ്‌ജക്റ്റ് ലേഔട്ടിനുള്ളിൽ ടാർഗെറ്റ് ഫീൽഡിന്റെ കൃത്യമായ സ്ഥാനം R8 കണക്കാക്കുന്നു.
  2. Drop the updater object. റഫ്ലക്റ്റീവ് റാപ്പർ ഒഴിവാക്കുന്നതിലൂടെ മെമ്മറി ലാഭിക്കാനും class-loading ജോലി കുറയ്ക്കാനും സാധിക്കുന്നു.
  3. Insert a direct sun.misc.Unsafe call. ഈ ലോ-ലെവൽ API ഉപയോഗിച്ച് ഒരു സിംഗിൾ atomic hardware instruction വഴി ഫീൽഡിലേക്ക് ഡാറ്റ എഴുതുന്നു.
  4. Replace every updater call പുതിയ unsafe instruction ഉപയോഗിച്ച് മാറ്റുന്നു, ഇത് JIT കംപൈലറിന് ഈ പ്രവർത്തനം inline ചെയ്യാൻ അനുവദിക്കുന്നു.

ഇതിന്റെ ഫലമായി, CPU-വിന് ഇനി റഫ്ലക്റ്റീവ് ലുക്കപ്പുകളോ റൺടൈം പരിശോധനകളോ നടത്തേണ്ടതില്ല; പകരം അത് ആറ്റോമിക് ഇൻസ്ട്രക്ഷൻ നേരിട്ട് പ്രവർത്തിപ്പിക്കുന്നു. ഒരു ഡെവലപ്പറെ സംബന്ധിച്ചിടത്തോളം ഈ മാറ്റം തിരിച്ചറിയാൻ കഴിയില്ല – coroutine API പഴയതുപോലെ തന്നെ പ്രവർത്തിക്കും – എന്നാൽ ഉള്ളിൽ കോഡ് “metal-level” വേഗതയിൽ പ്രവർത്തിക്കുന്നു.

അളക്കാവുന്ന നേട്ടങ്ങൾ

AGP 9.2.0 ഉപയോഗിച്ച്, R8 എനേബിൾ ചെയ്ത്, minification ഓൺ ചെയ്ത് ബിൽഡ് ചെയ്ത ഒരു സാധാരണ ആൻഡ്രോയിഡ് ഉപകരണത്തിലെ ബെഞ്ച്മാർക്കുകൾ താഴെ പറയുന്ന വേഗത വർദ്ധനവ് കാണിക്കുന്നു:

  • Channel send/receive: 2.01 × വേഗതയേറിയത്
  • Mutex lock/unlock: 1.90 × വേഗതയേറിയത്
  • StateFlow updates: 2.02 × വേഗതയേറിയത്
  • Coroutine dispatch: 1.68 × വേഗതയേറിയത്

ഈ കണക്കുകൾ ഉപയോക്താവിന്റെ അനുഭവത്തിൽ (user-experience) വലിയ മാറ്റങ്ങൾ കൊണ്ടുവരുന്നു. Coroutine synchronization-നായി കാത്തുനിൽക്കേണ്ടി വരുന്ന cold launch സമയം കുറയുന്നതിലൂടെ, ആദ്യത്തെ ഫ്രെയിം റെൻഡർ ചെയ്യാൻ UI thread-ന് കൂടുതൽ സമയം ലഭിക്കുന്നു. CPU ഉപയോഗം കുറയുന്നത് പ്രോസസ്സറിന് വേഗത്തിൽ സ്ലീപ്പ് മോഡിലേക്ക് പോകാൻ സഹായിക്കുന്നു, ഇത് ബാറ്ററി ലൈഫ് മെച്ചപ്പെടുത്തും.

ഈ നേട്ടം എങ്ങനെ പ്രയോജനപ്പെടുത്താം

കോഡിൽ മാറ്റങ്ങൾ ഒന്നും വരുത്തേണ്ടതില്ല. ഈ മാറ്റം നടപ്പിലാക്കാൻ നിങ്ങൾക്ക് താഴെ പറയുന്നവ ആവശ്യമാണ്:

  • AGP 9.2.0 അല്ലെങ്കിൽ പുതിയ പതിപ്പ് – അപ്‌ഡേറ്റ് ചെയ്ത R8 അടങ്ങിയ വേർഷൻ.
  • R8 – മുകളിൽ പറഞ്ഞ AGP ഉപയോഗിച്ച് ബിൽഡ് ചെയ്യുമ്പോൾ ഇത് ഓട്ടോമാറ്റിക്കായി ഉപയോഗിക്കപ്പെടും.
  • Kotlin Coroutines 1.8.0+ – ഒപ്റ്റിമൈസർ പ്രതീക്ഷിക്കുന്ന AtomicFieldUpdater പാറ്റേൺ അടങ്ങിയ ലൈബ്രറി വേർഷൻ.
  • റിലീസ് ബിൽഡ് ടൈപ്പിൽ isMinifyEnabled = true – minification ഓൺ ആണെങ്കിൽ മാത്രമേ R8 പ്രവർത്തിക്കുകയുള്ളൂ.

നിങ്ങൾ ചെയ്യേണ്ട ഏക അധിക കാര്യം നിങ്ങളുടെ ProGuard (അല്ലെങ്കിൽ R8) റൂളുകൾ പരിശോധിക്കുക എന്നതാണ്. Volatile ഫീൽഡുകളെയോ updater ക്ലാസുകളെയോ നിലനിർത്തുന്ന തരത്തിലുള്ള വിപുലമായ -keep നിർദ്ദേശങ്ങൾ ഈ മാറ്റത്തെ തടയും. ആ ഫീൽഡുകൾ മാറ്റം വരുത്താൻ R8-നെ അനുവദിക്കുന്നുണ്ടെന്ന് ഉറപ്പാക്കുക; അല്ലാത്തപക്ഷം ഒപ്റ്റിമൈസർ പഴയ റഫ്ലക്റ്റീവ് രീതിയിലേക്ക് തന്നെ മടങ്ങും.

Android Studio-യിലെ APK Analyzer ഉപയോഗിച്ച് നിങ്ങൾക്ക് ഈ മാറ്റം പരിശോധിക്കാവുന്നതാണ്. കംപൈൽ ചെയ്ത APK തുറന്ന്, JobSupport പോലുള്ള ഒരു coroutine support ക്ലാസ് കണ്ടെത്തി, അതിന്റെ decompiled bytecode പരിശോധിക്കുക. മാറ്റം വിജയകരമായി നടന്നുവെങ്കിൽ, static updater ഫീൽഡുകൾ അവിടെ കാണില്ല, പകരം Unsafe-ലേക്ക് നേരിട്ടുള്ള കോളുകൾ കാണാം.

പരിമിതികളും മറ്റ് വശങ്ങളും

എല്ലാ പ്രോജക്റ്റുകൾക്കും ബാധകമല്ലാത്ത രണ്ട് നിബന്ധനകളെ അടിസ്ഥാനമാക്കിയാണ് ഈ ഒപ്റ്റിമൈസേഷൻ പ്രവർത്തിക്കുന്നത്:

  1. Minification പ്രവർത്തനസജ്ജമാക്കണം. Debug builds, അല്ലെങ്കിൽ ഡീബഗ്ഗിംഗ് സൗകര്യത്തിനായി minification ഓഫ് ചെയ്തിട്ടുള്ള release builds എന്നിവയ്ക്ക് ഇതിന്റെ ഗുണം ലഭിക്കില്ല.
  2. ProGuard rules permissive ആയിരിക്കണം. Coroutine internals-നായി കഠിനമായ -keep patterns ഉപയോഗിക്കുന്ന പ്രോജക്റ്റുകളിൽ ഈ rules-ൽ ഇളവ് വരുത്തേണ്ടി വന്നേക്കാം; ഇത് ശ്രദ്ധാപൂർവ്വം പരിശോധിച്ചില്ലെങ്കിൽ shrinking-മായി ബന്ധപ്പെട്ട bugs ഇന്റേണൽ ക്ലാസുകളിൽ ഉണ്ടാകാൻ കാരണമായേക്കാം.

നിങ്ങൾ ലക്ഷ്യമിടുന്ന വിവിധതരം devices-ൽ ടെസ്റ്റ് ചെയ്യുന്നത് നല്ലൊരു ശീലമാണ്.

അടുത്തതായി ശ്രദ്ധിക്കേണ്ടവ

Language abstractions-ന് പിന്നിൽ മറഞ്ഞിരിക്കുന്ന performance എങ്ങനെ build-time bytecode transformations-ലൂടെ പുറത്തെടുക്കാമെന്ന് ഈ rewrite കാണിച്ചുതരുന്നു. നിങ്ങളുടെ പ്രത്യേക workload-ൽ ഇതിന്റെ ഗുണങ്ങൾ ഉറപ്പാക്കാൻ, coroutine കൂടുതലായി ഉപയോഗിക്കുന്ന നിങ്ങളുടെ കോഡ് profile ചെയ്യാൻ ശ്രമിക്കുക.

Takeaway: AGP 9.2.0-ലേക്ക് upgrade ചെയ്യുകയും R8-ന്റെ minification enable ചെയ്യുകയും ചെയ്യുന്നത്, source code-ൽ മാറ്റം വരുത്താതെ തന്നെ, Kotlin coroutine കൂടുതലായി ഉപയോഗിക്കുന്ന apps-ന്റെ നിർണ്ണായകമായ synchronization speeds ഏകദേശം ഇരട്ടിയാക്കുന്നു—build configuration optimizer-നെ അതിന്റെ ജോലി ചെയ്യാൻ അനുവദിക്കുന്നുണ്ടെങ്കിൽ മാത്രം.