3. diel - Výkon coroutine aplikácií v Kotlin
V minulej lekcii, Tvorba Coroutines v Kotline , sme si ukázali jednotlivé spôsoby, ako tvoriť coroutines a popísali si rozdiely medzi nimi.
Vítam vás pri ďalšom diele tutoriálu zameraného na Kotlin coroutines, ktoré nám umožňujú tvoriť asynchrónne aplikácie. Z minulých lekcií už vieme, ako tvoriť jednotlivé coroutines. Bohužiaľ som si neukázali v praxi, o koľko sa nám zvýši ich výkon, pokiaľ v nich coroutines použijeme. Dnes sa teda zameriame na výkon coroutine aplikácií.
Čas výkonu aplikácie
Najprv začneme porovnaním kódu bez coroutines s asynchrónnym kódom av
oboch budeme sledovať čas potrebný na výkon programu. Aby sme mohli
simulovať nejakú časovo nákladnú operáciu, použijeme v prvom prípade
Thread.sleep()
a pre kód s coroutines delay()
. Ako
nákladnú operáciu si môžeme predstaviť napríklad získanie dát z
externého zdroja, napríklad z internetu. Táto operácia bude v našej
ukážke trvať jednu sekundu.
Príklad bez coroutines
Najprv si ukážeme kód bez coroutines.
Vytvorme si nový projekt a pridajme doň potrebné závislosti. Do
vygenerovanej triedy Main
najskôr pridáme metódu
ulozDataNaServer()
, ktorá sa bude vykonávať jednu sekundu:
fun ulozDataNaServer() { Thread.sleep(1000) }
Aby sme mohli merať, ako dlho trvá vykonanie operácií, budeme používať
blok measureTimeMillis
. Ten nám vráti čas, ktorý bol potrebný
na vykonanie operácií v ňom volaných. My v bloku
measureTimeMillis
dvakrát zavoláme metódu
ulozDataNaServer()
a budeme tak simulovať dve operácie
nahrávania dát na server. Metódu main()
upravíme
nasledovne:
fun main() { val casVykonavani = measureTimeMillis { ulozDataNaServer() ulozDataNaServer() } println(casVykonavani) }
Potom, čo kód spustíme, dostaneme nasledovný výstup:
2000
V príkladoch, ktoré budeme uvádzať, sa môže mierne líšiť čas vykonávania. Na rôznych počítačoch totiž budú tieto operácie vykonané rôznou rýchlosťou. Výstup sa ale určite nebude líšiť o 500 ms alebo viac.
Výkon coroutines tvorených
pomocou launch
V ďalšom príklade, ktorý si predstavíme, použijeme na tvorbu coroutines
blok launch
.
Ako vieme z minulých lekcií, launch
nám slúži
na vykonanie kódu asynchrónne.
Do nášho projektu si pridajme novú metódu
ulozDataNaServerDelay()
, ktorá bude namiesto
Thread.sleep()
používať delay()
. Aby sme v nej
mohli používať funkciu delay()
, budeme ju musieť pridať
modifikátor suspend
. Implementácia metódy bude teda vyzerať
nasledovne:
suspend fun ulozDataNaServerDelay() { delay(1000) }
Presunieme sa do metódy main()
a zmeníme v nej blok
measureTimeMillis
. V ňom tentokrát vytvoríme pomocou bloku
runBlocking
a launch
dve coroutines, ktoré zavolajú
našu metódu ulozDataNaServerDelay()
:
fun main() { val casVykonavani = measureTimeMillis { runBlocking { launch { ulozDataNaServerDelay() } launch { ulozDataNaServerDelay() } } } println(casVykonavani) }
Potom, čo tento kód spustíme, dostaneme nasledujúci výstup:
1035
Ako vidíme, čas vykonávania sa znížil skoro na polovicu. Je to preto,
že sa oba bloky launch
vykonávali paralelne.
Podobný výsledok by sme dosiahli, keby sme vytvorili
dve vlákna. Keby sme ale mali napríklad stotisíc volaní
metódy ulozDataNaServer()
a pre každé tvorili vlastné vlákno,
došla by nám pamäť. V prípade coroutines by nám pamäť nedošla, pretože
sú mnohonásobne menej náročné na systémové prostriedky ako vlákna.
Príklady pre získanie hodnoty
Poďme si teraz výkon aplikácie porovnať pri príkladoch, v ktorých budeme chcieť získať nejaké hodnoty. Predstavme si napríklad spracovanie dát z externého zdroja vrátane matematických operácií u nich vykonávaných.
Výkon aplikácie bez coroutines
Začneme opäť ukážkou bez coroutines. V projekte si vytvoríme dve
metódy bez modifikátora suspend
. Obe opäť iba uspíme pomocou
Thread.sleep()
, aby sme simulovali oneskorenie jednu sekundu.
Následne vrátime hodnotu. Metódy sme pre ilustráciu pomenovali
zjistiMedian()
a sectiPrvocisla()
. Výsledný kód
bude vyzerať nasledovne:
fun zjistiMedian(): Int { Thread.sleep(1000) return 10 } fun sectiPrvocisla(): Int { Thread.sleep(1000) return 20 }
Teraz obe metódy zavoláme v metóde main()
, kde rovnako ako v
predchádzajúcich príkladoch použijeme blok measureTimeMillis
.
Tiež potom vypíšeme súčet oboch hodnôt. Kód v main()
bude
vyzerať nasledovne:
fun main() { val casVykonavani = measureTimeMillis { val median = zjistiMedian() val soucetPrvocisel = sectiPrvocisla() println("Výsledek: ${median + soucetPrvocisel}") } println(casVykonavani) }
Potom, čo tento program spustíme, dostaneme tento výstup:
Výsledek: 30 2000
Prvý riadok je súčet návratovej hodnoty metód. Na ďalšom riadku máme čas vykonania programu. Rovnako ako minule bude vykonávanie operácií trvať dve sekundy. Bohužiaľ, tento čas je zbytočne dlhý. Aby sme zlepšili výkon aplikácie, použijeme coroutines.
Výkon coroutines tvorených
pomocou async
Vieme už, že na tvorbu coroutines, ktoré nesú určitú hodnotu, musíme
použiť blok async
. Hodnotu danej async
coroutine
potom získame volaním metódy await()
. Poďme si teda všetko
ukázať na poslednom príklade. Aby sme mohli demonštrovať čas získavania
hodnôt z externého zdroja, použijeme v ňom teraz opäť funkciu
delay()
.
Začneme tiež pridaním dvoch metód s modifikátorom suspend
a
vytvoríme metódu zjistiMedianDelay()
a
sectiPrvocislaDelay()
. Ich implementácia bude podobná ako v
skoršom príklade s couroutines, navyše iba doplníme návratovú hodnotu:
suspend fun zjistiMedianDelay(): Int { delay(1000) return 10 } suspend fun sectiPrvocislaDelay(): Int { delay(1000) return 20 }
Teraz môžeme upraviť kód v metóde main()
. V ňom obe
metódy zavoláme a potom vypíšeme získaný výsledok a celkový čas oboch
operácií:
fun main() { val casVykonavani = measureTimeMillis { runBlocking { val median = async { zjistiMedianDelay() } val soucetPrvocisel = async { sectiPrvocislaDelay() } println("Výsledek: ${median.await() + soucetPrvocisel.await()}") } } println(casVykonavani) }
Vytvorili sme dve coroutines pomocou bloku async
, ktoré
vypočítajú našu hodnotu. V každej z nich sme zavolali naše metódy uspané
na sekundu. Následne sme vypísali súčet a dĺžku behu aplikácie do
konzoly. Kód spustíme a dostaneme tento výstup:
Výsledek: 30 1030
Znížili sme výkon aplikácie opäť skoro na polovičnú dobu, pretože sa kód vykonával asynchrónne.
V ďalších dieloch sa pozrieme, ako môžeme upravovať vlastnosti coroutines.
V budúcej lekcii, Dispatchers a CoroutineContext v Kotline , si predstavíme dispatchers a vysvetlíme si, ako fungujú a prečo je dobré ich v našich aplikáciách využiť.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 1x (237.64 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Kotlin