Zana za ujenzi (build tools) za Android zimepata ongezeko la kasi kwa ajili ya Kotlin Coroutines. Kwa kutumia Android Gradle Plugin (AGP) 9.2.0 na R8 shrinker iliyojumuishwa ndani yake, bytecode inayodhibiti usimamizi wa hali ya coroutine (coroutine state handling) imeandikwa upya ili kutoa takriban mara mbili ya utendaji katika operesheni za kawaida za coroutine. Watengenezaji wanaotuma programu za Android zinazotegemea coroutines wanaweza kuona muda mfupi wa kuanza programu (cold-start times), kasi thabiti ya fremu (frame rates), na uboreshaji mdogo wa maisha ya betri bila kubadilisha mstari mmoja wa kodi ya Kotlin.

Kwa nini utendaji wa coroutine ulikuwa muhimu

Kotlin Coroutines hutumia vitu vya AtomicFieldUpdater ili kubadilisha nyanja za ndani (internal fields) kwa usalama kutoka kwenye nyuzi (threads) nyingi. Mbinu hii inaepuka kutenga kitu kipya cha atomic kwa kila mabadiliko, lakini inaleta gharama tatu zilizofichika kwenye Android:

  • Ucheleweshaji wa kupakia darasa (Class-loading delay) – updater hutengenezwa kwa kutumia reflection, hivyo VM lazima itafute nyanja lengwa wakati wa utendaji (runtime).
  • Upotezaji wa CPU – kila ufikiaji unakagua hali ya updater kabla ya kufikia nyanja halisi.
  • Mipaka ya inlining – kilele (compiler) hakiwezi kuingiza (inline) wito wa updater, jambo linalofanya bytecode iliyotengenezwa kuwa kubwa na ya polepole.

Kiutendaji, gharama hizi hujitokeza wakati wa njia zenye kasi (hot paths) kama vile kutuma/kupokea kwenye channel, kufunga/kufungua mutex, mabadiliko ya StateFlow, na usambazaji wa coroutine (coroutine dispatch). Matokeo yake ni kiasi kikubwa cha muda wa CPU kinachotumika kwenye usimamizi (bookkeeping) badala ya kazi yenyewe ya programu.

Nini kimebadilika katika AGP 9.2.0 na R8

R8, kinyonyaji cha kodi (code-shrinker) kinachoambatana na AGP 9.2.0, sasa huchunguza bytecode iliyokamilika kwa mtindo wa kawaida wa AtomicFieldUpdater. Ikipata huo, inafanya mabadiliko manne:

  1. Tambua mkao wa kumbukumbu (memory offset) wa nyanja. R8 hupiga hesabu ya mahali halisi pa nyanja lengwa ndani ya mpangilio wa kitu (object layout).
  2. Ondoa kitu cha updater. Kizuizi cha reflection (reflective wrapper) huondoka, hivyo kuokoa kumbukumbu na kuondoa kazi ya kupakia darasa.
  3. Ingiza wito wa moja kwa moja wa sun.misc.Unsafe. API hii ya kiwango cha chini huandika kwenye nyanja kwa kutumia maelekezo mamoja ya hardware ya atomic.
  4. Badilisha kila wito wa updater kwa maelekezo mapya ya unsafe, hali inayoruhusu JIT compiler kuingiza (inline) operesheni hiyo.

Matokeo yake ni kwamba CPU haihitaji tena kufanya utafutaji wa reflection au ukaguzi wa wakati wa utendaji; inatekeleza maelekezo ya atomic moja kwa moja. Kwa mtazamo wa mtengenezaji, mabadiliko haya hayataonekana – API ya coroutine inafanya kazi vilevile – lakini kwa ndani, kodi inafanya kazi kwa kasi ya "kiwango cha chuma" (metal-level speed).

Mafanikio yanayopimika

Vipimo (Benchmarks) kwenye kifaa cha kawaida cha Android vinaonyesha ongezeko la kasi lifuatalo baada ya kujenga kwa kutumia AGP 9.2.0, R8 ikiwa imewashwa, na minification ikiwa imewashwa:

  • Kutuma/kupokea kwenye channel: 2.01 × haraka zaidi
  • Kufunga/kufungua mutex: 1.90 × haraka zaidi
  • Mabadiliko ya StateFlow: 2.02 × haraka zaidi
  • Usambazaji wa coroutine (Coroutine dispatch): 1.68 × haraka zaidi

Namba hizi zinatafsiriwa kuwa maboresho yanayoonekana katika uzoefu wa mtumiaji. Uzinduzi wa baridi (cold launch) uliotumia sehemu ya sekunde kusubiri usawazishaji wa coroutine sasa unamalizika haraka zaidi, ukitoa nafasi zaidi kwa UI thread kuchora fremu ya kwanza. Kupungua kwa ushindani wa CPU (CPU contention) pia kunaruhusu kichakataji (processor) kurudi kwenye hali ya kulala haraka, jambo ambalo linaweza kuboresha maisha ya betri.

Jinsi ya kupata faida hii

Hakuna mabadiliko ya kodi yanayohitajika. Ili kuamsha mabadiliko haya unahitaji:

  • AGP 9.2.0 au mpya zaidi – toleo linalojumuisha R8 iliyosasishwa.
  • R8 – hutumiwa moja kwa moja unapojenga kwa kutumia AGP iliyotajwa hapo juu.
  • Kotlin Coroutines 1.8.0+ – toleo la maktaba (library) linalokuja na mtindo wa AtomicFieldUpdater ambao optimiza unatarajia.
  • isMinifyEnabled = true katika aina yako ya ujenzi wa toleo la mwisho (release build type) – R8 hufanya kazi tu wakati minification imewashwa.

Hatua pekee ya ziada ni kukagua sheria zako za ProGuard (au R8). Maelekezo mapana ya -keep yanayohifadhi nyanja za volatile au madarasa ya updater yenyewe yanazuia mabadiliko haya. Hakikisha sheria hizo zinairuhusu R8 kubadilisha nyanja hizo; vinginevyo, optimiza itarudi kwenye utekelezaji wa awali wa reflection.

Unaweza kuhakiki mabadiliko hayo kwa kutumia APK Analyzer ya Android Studio. Fungua APK iliyojengwa, tafuta darasa la msaada wa coroutine kama vile JobSupport, na kagua bytecode iliyofunguliwa (decompiled bytecode). Ikiwa mabadiliko yamefanikiwa, nyanja za static updater hazitakuwepo na utaona wito wa moja kwa moja wa Unsafe badala yake.

Tahadhari na hoja za kinyume

Uboreshaji huu unategemea masharti mawili ambayo si kila mradi unayafikia:

  1. Minification lazima iwashwe. Build za debug, au build za release zinazozima minification kwa ajili ya urahisi wa debugging, hazitapata faida hii.
  2. Sheria za ProGuard lazima ziwe huria. Miradi yenye mifumo ya -keep yenye ukali kwa ajili ya mambo ya ndani ya coroutine inaweza kuhitaji kulegeza sheria hizo, jambo ambalo linaweza kuweka madarasa ya ndani (internal classes) hatarini kupata makosa yanayohusiana na shrinking ikiwa haitafanyiwa majaribio kwa uangalifu.

Inabaki kuwa mazoea mazuri kufanya majaribio kwenye aina mbalimbali za vifaa unavyolenga.

Nini cha kuangalia baadaye

Uandishi upya huu unaonyesha jinsi mabadiliko ya bytecode wakati wa build yanavyoweza kutoa utendaji uliojificha nyuma ya uabstrakti wa lugha. Fikiria kufanya profiling kwenye kodi yako inayotumia coroutine kwa wingi ili kuthibitisha faida hizo kwenye kazi yako mahususi.

Jambo la kuzingatia: Kupandisha toleo hadi AGP 9.2.0 na kuwashia minification ya R8 kunazipa programu za Kotlin zinazotumia coroutine kwa wingi kasi ya usawazishaji (synchronization) muhimu inayokaribia mara mbili, yote bila kugusa kodi chanzo—mradi tu usanidi wa build uruhusu optimizer kufanya kazi yake.