Bonjour à tous! Dans mon travail, j'utilise souvent Kotlin pour l'automatisation. Mon activité n'est pas directement liée à la programmation, mais Kotlin simplifie grandement les tâches de travail.
Récemment, il était nécessaire de collecter des données de taille assez grande pour faire l'analyse, j'ai donc décidé d'écrire un petit script pour récupérer les données et les enregistrer dans Excel. Il n'y a eu aucun problème avec le dernier point - j'ai lu sur Apache POI, j'ai pris quelques exemples de la documentation officielle, je l'ai modifié moi-même. On ne peut pas en dire autant des requêtes Internet.
La source est retournée par lots de json et il était nécessaire de collecter ces "lots" rapidement, en les convertissant en texte et en écrivant un tableau dans un fichier.
Méthode asynchrone
J'ai décidé de commencer par une simple asynchronisation. Après avoir fouillé un peu HttpUrlConnection, je l'ai envoyé là où il appartient, en le remplaçant par HttpClient de Java.
Pour les tests, j'ai pris le service https://jsonplaceholder.typicode.com/ , qui m'a été suggéré par un développeur familier. J'ai enregistré un lien qui envoie à Json des commentaires sur une variable, afin de ne pas dupliquer et démarrer les tests.
const val URL = "https://jsonplaceholder.typicode.com/comments"
La fonction était prête et fonctionnait même. Les données sont arrivées.
fun getDataAsync(url: String): String? {
val httpClient = HttpClient.newBuilder()
.build()
val httpRequest = HttpRequest.newBuilder()
.uri(URI.create(link)).build()
return httpClient.sendAsync(httpRequest, BodyHandlers.ofString())
.join().body()
}
Il fallait maintenant vérifier la vitesse de travail. Armé de measureTimeMillis, j'ai exécuté le code.
val asyncTime = measureTimeMillis {
val res = (1..10)
.toList()
.map {getDataAsync("$URL/$it")}
res.forEach { println(it) }
}
println(" $asyncTime ")
Tout a fonctionné comme il se doit, mais je le voulais plus vite. Après un peu de fouille sur Internet, je suis tombé sur une solution dans laquelle les tâches sont effectuées en parallèle.
Carte parallèle
suspend fun <A, B> Iterable<A>.pmap(f: suspend (A) -> B): List<B> =
coroutineScope {
map { async { f(it) } }.awaitAll()
}
, ( Iterable) pmap, . A. async , .awaitAll() . suspend, .
, , - .
val parmapTime = measureTimeMillis {
runBlocking {
val res = (1..10)
.toList()
.pmap { getDataAsync("$URL/$it") }
println(mapResult)
}
}
println(" pmap $parmapTime ")
- 1523, . map async, .
Parallel Map v 2.0
, , .
suspend fun <T, V> Iterable<T>.parMap(func: suspend (T) -> V): Iterable<V> =
coroutineScope {
map { element ->
async(Dispatchers.IO) { func(element) }
}.awaitAll()
}
val parMapTime = measureTimeMillis {
runBlocking {
val res = (1..10)
.toList()
.parMap { getDataAsync("$URL/$it") }
}
println(res)
}
println(" map $parMapTime ")
Dispatchers.IO 2 ~ 610 . ! ( , excel ..) . , - .
Java ParallelStream
, stackowerflow parallelStream. , IDEA.
val javaParallelTime = measureTimeMillis {
val res = (1..10).toList()
.parallelStream()
.map { getDataAsync("$URL/$it") }
res.forEach { println(it) }
}
println("Java parallelSrtream $javaParallelTime ")
, . , . stream . , , , "" , Json.
, - , async . .
Les résultats peuvent être consultés dans le tableau ci-dessous. Pour moi, j'ai définitivement décidé de laisser asynchrone attendre . Principalement à cause de la gestion plus simple des erreurs, bien sûr. Et il n'est pas nécessaire d'aller au-delà des coroutines ici.
Méthode |
Temps (ms) |
|---|---|
Méthode asynchrone |
1487 |
Implémentation de Pmap depuis le Web |
1523 |
Mon option est parallelMap |
610 |
Java.parallelStream |
578 |
Dans le futur, il y a des pensées pour organiser cela dans une petite bibliothèque et l'utiliser à des fins personnelles, et bien sûr tout réécrire du «code hindou» au code humain, tant qu'il y aura suffisamment de possibilités. Et puis téléchargez tout cela sur vds.
J'espère que mon expérience est utile à quelqu'un. Je serais heureux de recevoir des critiques et des conseils constructifs! Merci à tous