Android ના બિલ્ડ ટૂલ્સને Kotlin Coroutines માટે સ્પીડ-બૂસ્ટ મળ્યું છે. Android Gradle Plugin (AGP) 9.2.0 અને તેની સાથે આવતા R8 shrinker દ્વારા, coroutine સ્ટેટ હેન્ડલિંગ માટે વપરાતા બાઈટકોડને ફરીથી લખવામાં આવ્યો છે, જે સામાન્ય coroutine કામગીરીમાં લગભગ બમણી સ્પીડ આપે છે. જે ડેવલપર્સ Kotlin કોડની એક પણ લાઇન બદલ્યા વગર coroutines પર આધારિત Android એપ્સ બનાવે છે, તેઓ ઝડપી કોલ્ડ-સ્ટાર્ટ ટાઇમ્સ, સ્થિર ફ્રેમ રેટ્સ અને બેટરી લાઇફમાં થોડો સુધારો જોઈ શકે છે.
Why coroutine performance mattered
Kotlin Coroutines મલ્ટીપલ થ્રેડ્સમાંથી ઇન્ટરનલ ફીલ્ડ્સને સુરક્ષિત રીતે મોડિફાય કરવા માટે AtomicFieldUpdater ઓબ્જેક્ટ્સનો ઉપયોગ કરે છે. આ અભિગમ દરેક અપડેટ માટે નવો એટમિક ઓબ્જેક્ટ ફાળવવાનું ટાળે છે, પરંતુ તે Android પર ત્રણ છુપા ખર્ચ (costs) લાવે છે:
- Class-loading delay – અપડેટર રિફ્લેક્શન દ્વારા બનાવવામાં આવે છે, તેથી VM એ રનટાઇમ પર ટાર્ગેટ ફીલ્ડ શોધવું પડે છે.
- CPU waste – દરેક એક્સેસ વાસ્તવિક ફીલ્ડ સુધી પહોંચતા પહેલા અપડેટરની સ્થિતિ તપાસે છે.
- Inlining limits – કમ્પાઈલર અપડેટર કોલ્સને ઇનલાઇન કરી શકતું નથી, જેના કારણે જનરેટ થયેલ બાઈટકોડ મોટો અને ધીમો રહે છે.
વ્યવહારમાં, આ ખર્ચ channel send/receive, mutex lock/unlock, StateFlow અપડેટ્સ અને coroutine dispatch જેવા હોટ પાથ્સ (hot paths) દરમિયાન જોવા મળે છે. પરિણામે, એપ્લિકેશનના પોતાના કામ કરવાને બદલે બુકકીપિંગ પર ઘણો CPU સમય ખર્ચાય છે.
What changed in AGP 9.2.0 and R8
R8, જે AGP 9.2.0 સાથે આવે છે, તે હવે સ્ટાન્ડર્ડ AtomicFieldUpdater પેટર્ન માટે કમ્પાઈલ કરેલા બાઈટકોડને સ્કેન કરે છે. જ્યારે તેને આ પેટર્ન મળે છે, ત્યારે તે ચાર રૂપાંતરણો (transformations) કરે છે:
- Identify the field’s memory offset. R8 ઓબ્જેક્ટ લેઆઉટની અંદર ટાર્ગેટ ફીલ્ડનું ચોક્કસ સ્થાન ગણે છે.
- Drop the updater object. રિફ્લેક્ટિવ રેપર અદૃશ્ય થઈ જાય છે, જેનાથી મેમરી બચે છે અને ક્લાસ-લોડિંગનું કામ દૂર થાય છે.
- Insert a direct
sun.misc.Unsafecall. આ લો-લેવલ API સિંગલ એટમિક હાર્ડવેર ઇન્સ્ટ્રક્શનનો ઉપયોગ કરીને ફીલ્ડમાં લખે છે. - Replace every updater call દરેક અપડેટર કોલને નવી unsafe ઇન્સ્ટ્રક્શન સાથે બદલે છે, જેનાથી JIT કમ્પાઈલરને ઓપરેશન ઇનલાઇન કરવાની મંજૂરી મળે છે.
આનો મુખ્ય ફાયદો એ છે કે CPU ને હવે રિફ્લેક્ટિવ લુકઅપ અથવા રનટાઇમ ચેક્સ કરવાની જરૂર નથી; તે સીધી એટમિક ઇન્સ્ટ્રક્શન એક્ઝિક્યુટ કરે છે. ડેવલપરના દૃષ્ટિકોણથી આ ફેરફાર અદ્રશ્ય છે – coroutine API સમાન રીતે કામ કરે છે – પરંતુ અંદરથી કોડ "metal-level" સ્પીડ પર ચાલે છે.
Measurable gains
સામાન્ય Android ડિવાઇસ પરના બેન્ચમાર્ક AGP 9.2.0, R8 ઇનેબલ અને મિનિફિકેશન ચાલુ કરીને બનાવ્યા પછી નીચે મુજબની સ્પીડ-અપ બતાવે છે:
- Channel send/receive: 2.01 × ઝડપી
- Mutex lock/unlock: 1.90 × ઝડપી
StateFlowupdates: 2.02 × ઝડપી- Coroutine dispatch: 1.68 × ઝડપી
આ આંકડાઓ ટેન્જીબલ યુઝર-એક્સપિરિયન્સ સુધારામાં રૂપાંતરિત થાય છે. કોલ્ડ લોન્ચ જે પહેલા coroutine સિંક્રનાઇઝેશન માટે સેકન્ડનો થોડો ભાગ રાહ જોતો હતો, તે હવે વહેલો પૂરો થાય છે, જેનાથી UI થ્રેડને પ્રથમ ફ્રેમ રેન્ડર કરવા માટે વધુ સમય મળે છે. ઓછો CPU કન્ટેન્શન પ્રોસેસરને ઝડપથી સ્લીપ મોડમાં જવા દે છે, જે બેટરી લાઇફ સુધારી શકે છે.
How to reap the benefit
કોઈ કોડ ફેરફારની જરૂર નથી. આ રૂપાંતરણને સક્રિય કરવા માટે તમારે જરૂર છે:
- AGP 9.2.0 અથવા નવું – જે વર્ઝનમાં અપડેટેડ R8 હોય.
- R8 – જ્યારે તમે ઉપરના AGP સાથે બિલ્ડ કરો છો ત્યારે આપમેળે વપરાય છે.
- Kotlin Coroutines 1.8.0+ – લાઈબ્રેરીનું એ વર્ઝન જે
AtomicFieldUpdaterપેટર્ન સાથે આવે છે જે ઓપ્ટિમાઇઝર અપેક્ષા રાખે છે. - તમારા રિલીઝ બિલ્ડ ટાઇપમાં
isMinifyEnabled = true– R8 ત્યારે જ ચાલે છે જ્યારે મિનિફિકેશન ચાલુ હોય.
એકમાત્ર વધારાનું પગલું તમારા ProGuard (અથવા R8) નિયમોનું ઓડિટ કરવાનું છે. વાઇલેટ ફીલ્ડ્સ અથવા અપડેટર ક્લાસને જ સાચવતા વ્યાપક -keep ડાયરેક્ટિવ્સ આ રૂપાંતરણને રોકે છે. ખાતરી કરો કે નિયમો R8 ને તે ફીલ્ડ્સ મોડિફાય કરવાની મંજૂરી આપે છે; અન્યથા ઓપ્ટિમાઇઝર મૂળ રિફ્લેક્ટિવ ઇમ્પ્લીમેન્ટેશન પર પાછું જશે.
તમે Android Studio ના APK Analyzer સાથે આ રૂપાંતરણ ચકાસી શકો છો. કમ્પાઈલ કરેલી APK ખોલો, JobSupport જેવો coroutine સપોર્ટ ક્લાસ શોધો, અને ડીકમ્પાઈલ કરેલા બાઈટકોડનું નિરીક્ષણ કરો. જો રૂપાંતરણ સફળ રહ્યું હોય, તો સ્ટેટિક અપડેટર ફીલ્ડ્સ ગેરહાજર હશે અને તેના બદલે તમને Unsafe ના સીધા કોલ્સ જોવા મળશે.
Caveats and counter-points
ઓપ્ટિમાઇઝેશન બે શરતો પર આધારિત છે જે દરેક પ્રોજેક્ટ પૂરી નથી કરતો:
- Minification સક્ષમ હોવું જોઈએ. Debug builds, અથવા release builds જે ડિબગિંગની સુવિધા માટે minification બંધ રાખે છે, તેમને આ લાભ મળશે નહીં.
- ProGuard rules permissive હોવા જોઈએ. Coroutine internals માટે આક્રમક
-keepપેટર્ન ધરાવતા પ્રોજેક્ટ્સમાં તે નિયમોને ලිલા (relax) કરવાની જરૂર પડી શકે છે, જે જો સાવચેતીપૂર્વક ટેસ્ટ કરવામાં ન આવે તો shrinking-સંબંધિત બગ્સ સામે internal classes ને ખુલ્લા કરી શકે છે.
તમે જે ઉપકરણો (devices) ને લક્ષ્ય બનાવો છો તેના પર ટેસ્ટ કરવું એ એક સારી પદ્ધતિ છે.
આગળ શું જોવું
આ rewrite દર્શાવે છે કે કેવી રીતે build-time bytecode transformations ભાષાના abstractions પાછળ છુપાયેલું performance મેળવી શકે છે. તમારા ચોક્કસ workload પર આ લાભોની પુષ્ટિ કરવા માટે તમારા પોતાના coroutine-heavy કોડનું profiling કરવાનું વિચારો.
Takeaway: AGP 9.2.0 માં અપગ્રેડ કરવાથી અને R8 ના minification ને સક્ષમ કરવાથી Kotlin coroutine-heavy એપ્સને ક્રિટિકલ synchronization સ્પીડમાં લગભગ બમણો વધારો મળે છે, તે પણ સોર્સ કોડને અડક્યા વગર—જો બિલ્ડ કોન્ફિગરેશન ઓપ્ટિમાઇઝરને તેનું કામ કરવા દેશે.
