PC & Mobile

Utilisation de transducteurs pour accélérer les tableaux JavaScript – ITNEXT

Utilisation de transducteurs pour accélérer les tableaux JavaScript - ITNEXT


Le traitement de grands tableaux JavaScript est très lent

Dans cet article, j'examinerai les moyens d'améliorer la vitesse de traitement des baies de JavaScript de grande taille utilisant des transducteurs. Cet article s'appuie sur les conclusions de mon précédent article: Accélération du traitement des tableaux JavaScript.

tl; dr Le traitement des tableaux JavaScript est lent, mais avec les transducteurs, vous pouvez résoudre beaucoup de ces problèmes. Néanmoins, vous voudrez d’abord examiner vos options natives.

Je n’avais jamais pensé en avoir besoin, mais j’avais besoin d’utiliser ma solution à partir de Accélération du traitement des tableaux JavaScript pour accélérer le chargement d’un plugin Chrome que j’écrivais récemment.

Ce plugin a chargé plus de 26 800 éléments dans une liste et les a traités. Étonnamment, même avec quelques filtre et carte fonctions, c'était assez rapide. Jusqu'à ce que je commence à effectuer des traitements avancés et que je devais dupliquer la taille du tableau pour en faire plus de 53 700 éléments. C’est là que j’ai remarqué un ralentissement de plusieurs ordres de grandeur.

Avertissement: Tous les exemples de code sont composés, mais je les ai rendus similaires à l'implémentation réelle. Le temps de traitement de ces exemples est beaucoup plus rapide que mon implémentation réelle, mais sur des machines plus lentes, les ralentissements sont encore pires.

Tout fonctionnait bien

Avant d’apporter des modifications, examinons le code original:

Cela pourrait éventuellement être amélioré en utilisant des transducteurs, mais je ne pensais pas que c'était nécessaire car le temps de traitement était en moyenne de 8,5 ms.

Même quand même, regardons la version du transducteur:

D'après mon article précédent, vous penseriez que ce serait plus rapide, mais ce n'est pas le cas. Avec 22,1 ms, il n’ya pas assez d’éléments dans la liste pour que les transducteurs soient une alternative plus rapide que la solution native. Tableau méthodes et il n'y a pas assez de transformations sur les données. C’est assez simple. Par conséquent, à moins d’ajouter plus d’articles ou d’opérateurs, cela n’améliorera pas la vitesse.

Le ralentissement

Ce plugin a été presque instantané à charger, mais tout à coup, ce minuscule changement l'a fait passer 35 secondes à ne rien faire d'autre que le traitement d'un tableau JavaScript. Comment? Qu'est-ce que j'ai fait?

En raison d'un bogue dans ma gestion des exigences et de la façon dont Chrome gérait ce tableau de chaînes, je devais ajouter deux fois plus d'éléments à la liste.

Voici à quoi ressemblait ce changement:

Avertissement: Bien que cela n’ait pas de sens hors contexte dans ce faux exemple de code, les exigences du projet nécessitaient la deuxième . version aussi bien.

Je lance chaque repère 6 fois. Avec une moyenne de 12300ms, c'était de loin le repère le plus ennuyeux. Parlez lent!

Mais regardons quelques façons de résoudre ce problème. Je connais le concat function crée un nouveau tableau à chaque exécution. Cela va certainement faire partie du problème, alors j’ai utilisé le fait que concat prend 2 arguments:

Ce changement subtil de réduction du nombre de concat les déclarations nous ont pris de moitié: 6600ms. Cependant, vous auriez probablement deviné ce nombre, car réduire de moitié le nombre de tableaux créés réduit de moitié le temps de traitement total.

Et nous pouvons réellement réduire cela davantage! Au lieu d'avoir concat vérifiez chaque argument que nous avons passé, nous pouvons simplement lui donner un tableau à la place. Si vous ne saviez pas, concat aplatit réellement les tableaux qu’il a donnés:

Et avec ce simple changement, nous n’avons plus que 2600 ms. Pourquoi? Je ne peux que deviner que c’est parce que nous créons un tableau à l’avance et concat ne doit pas traiter arguments lui-même. C’est une méthode plus ancienne d’utilisation de l’argument spread qui ne fonctionne que dans les versions plus anciennes. une fonction définitions et non dans les fonctions de flèche de graisse.

Si vous êtes curieux, vous pouvez en savoir plus sur cet article de MDN:

L'accélération

Comme vous pouvez le constater, ce code, bien qu’il ait l’air moderne et fonctionnel, souffre encore beaucoup de la façon dont le réducteur est utilisé. Les transducteurs RxJS à la rescousse!

Pourquoi utiliser RxJS?

Comme dans le précédent article, nous utiliserons à nouveau RxJS. Comme vous le savez probablement, je connais RxJS et j’ai déjà écrit des méthodes autour de souscrire je peux donc l’utiliser de manière synchrone, tout comme les scripts JavaScripts. Tableau méthodes.

Bien que j'aurais pu utiliser une bibliothèque de transducteurs sur étagère pour ce projet, il s'agit d'un plug-in Chrome qui utilise beaucoup RxJS. Faire venir une autre bibliothèque ajoute plus de complexité au projet et depuis que je suis sous contrat pour le faire, je veux réduire la quantité d’outils qu’une autre personne devra apprendre pour maintenir le projet. L’introduction de RxJS était déjà incertaine, mais en raison de la nature asynchrone de ce plugin, il a déjà réduit beaucoup de complexité.

Nous sommes partis au plaid!

Revenons à notre version du transducteur. Réessayons:

2300ms? Vraiment? Ce n’est pas le gain de vitesse auquel je m'attendais compte tenu de nos chiffres de l’article précédent.

C’est là que ce test échoue. Les transducteurs sont une manière complètement différente de penser au traitement par matrice. Sous le capot, les transducteurs sont un tas de fonctions d’emballage dans un réduire boucle. Cela signifie avoir 2 réduire les fonctions ralentissent probablement un peu les choses; en fait, grand O (n²).

Utilisant fusionner la carte, nous avons finalement atteint 70.6ms plus raisonnables. Nous nous sommes complètement débarrassés du tableau réduire et passé au plus rapide fusionner la carte transducteur. Maintenant, nous traitons toutes nos valeurs et ne les plaçons que dans un tableau à la toute fin. C’est la clé de cette accélération. Nous avons tous deux réduit le nombre de fois que nous avons créé un tableau (à un) et l'avons retardé le plus longtemps possible (jusqu'à la fin).

C'est ce à quoi vous vous attendiez, n'est-ce pas? Eh bien j'espère que non! Je pense que nous pouvons toujours le réduire davantage. Nous devons pouvoir le rapprocher beaucoup plus de notre 8,5 ms initial; sinon, je considère cela comme un échec.

Maintenant, comme le test initial du transducteur était de 22,1 ms, nous ne pouvons raisonnablement supposer que c’est le mieux que nous puissions faire. Comme nous dupliquons la taille du tableau, notre objectif est 40ms. Nous avons encore du chemin à faire.

Dupliquer notre logique de matrice avec un couple fusionner la carte opérateurs, c’est ce que je propose:

Je pensais que, puisque nous ne créions qu’un seul tableau, ce serait assez rapide, mais ce n’est pas le cas. Avec une moyenne de 51,5 ms, cela ne correspond clairement pas à nos chiffres.

En outre, remarquez comment j'ai dupliqué le filtre logique? C'est temporaire. Dans une situation réelle, ces opérateurs pourraient être combinés pour créer un nouvel opérateur utilisable dans les deux cas. Ainsi, vous ne rencontrerez plus de problèmes de maintenance à l’avenir. Juste pour que vous le sachiez, j'ai essayé de traiter la logique de filtrage avant de traiter les deux listes, mais cela a été un peu plus lent, car je devais créer un tableau intermédiaire.

Maintenant, je vous amène à la solution - la vraie solution - que j'ai utilisée dans ce plugin. C’est complexe et moche, mais dans le plugin, j’ai été en mesure de revenir à la vitesse de traitement initiale, qui est en réalité plus rapide que celle de carte.

C’est assez similaire à la version précédente sauf que nous utilisons forkJoin au lieu de fusionner la carte. La différence est qu'il renvoie un tableau des deux résultats. Nous créons en fait deux tableaux plus petits que nous combinons ensuite en un plus grand, mais dans l'exemple précédent, nous créions un grand tableau à partir d'un groupe d'éléments traités.

À ce stade, je ne peux pas vous dire pourquoi cette version est plus rapide car elle dépasse ma connaissance de RxJS. J'aurais supposé que la version précédente serait plus rapide, mais apparemment pas.

Nous avons quand même réussi à obtenir 41,2 ms, ce qui correspond à nos estimations. Mais cela ne me semble pas suffisant. Je pense que nous pourrions en arriver à au moins 17 ms.

Une réalisation soudaine

Cette méthode de double traitement m'a fait réfléchir, pourquoi ne pas faire la même chose avec carte? En fait, il pourrait être à la fois plus rapide et plus facile à lire!

Voici à quoi ça ressemble:

Et oui, il se situe dans la fourchette de nos estimations à 19,8 ms. C’est la solution que je cherchais depuis le début, mais j’ai décidé de changer de voie, car je ne savais pas à quel point cela pouvait être simple. Sérieusement, si vous voyiez quelque chose prendre plus de 12 secondes, quelle serait votre première réaction?

Conclusion

Comme vous l’avez vu, les transducteurs peuvent présenter d’importants avantages en termes de vitesse, même dans notre système optimisé. réduire exemple, mais jusqu’à ce que vous ayez un grand nombre d’articles ou un grand nombre d’opérateurs de pipelines, leur capacité à atteindre leurs objectifs sera limitée. Tableau méthodes en JavaScript.

Cela ne veut pas dire que toutes les bibliothèques de transducteurs sont comme ceci bien. J'utilise RxJS, conçu pour traiter les valeurs dans le temps. Ce n'est pas optimisé pour un traitement de tableau comme celui-ci et se rapporte simplement à mes articles précédents qui utilisent également RxJS de manière intéressante. Si j'avais utilisé une vraie bibliothèque de transducteurs, je me demandais combien ce code aurait été plus rapide.

Honnêtement, je suis surpris des résultats de cet article. Pour une raison quelconque, le plugin Chrome traitait beaucoup plus lentement et la version du transducteur avait donc un impact majeur sur la vitesse de traitement, mais pour une raison quelconque, Node.js v10 dispose de quelques optimisations qui accélèrent considérablement les choses pour le serveur natif. Tableau méthodes. Que ou, ils ont ralenti les méthodes de transducteur. Je ne le saurais jamais! Ce qui est bien, c'est que la version du transducteur n'a été ralentie que de 10 à 20 ms. Je parie qu’il ne faudrait pas autant d’éléments pour changer ces résultats.

Le point de rupture

Ok, j'étais curieux et ai couru plus de points de repère. Comparaison des méthodes de matrice et de transducteur les plus rapides avec une liste de 500 000 éléments. La méthode du transducteur était plus rapide de 100ms. Finalement! Quelque chose qui a répondu à mes attentes.

Avec cet ensemble de données particulier, j'ai pu trouver le point critique: environ 250 000 articles. Passé cela, l’écart se creuse considérablement. Maintenant, je suis curieux de savoir comment la vitesse du processeur et la mémoire disponible affectent ces tests. Est-ce que cela va être en faveur des transducteurs ou des méthodes de tableau natif?

Plus de lectures

Si vous êtes intéressé par plus de sujets liés à RxJS, vous devriez consulter mes autres articles:

Show More

SupportIvy

SupportIvy.com : Un lieu pour partager le savoir et mieux comprendre le monde. Meilleure plate-forme de support gratuit pour vous, Documentation &Tutoriels par les experts.

Related Articles

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Close
Close