53 votes

Performances de UICollectionView - _updateVisibleCellsNow

Je travaille sur un projet personnalisé UICollectionViewLayout qui affiche les cellules organisées par jour/semaine/mois.

Le défilement n'est pas fluide, et il semble que le décalage soit causé par [UICollectionView _updateVisibleCellsNow] qui est appelé sur chaque boucle de rendu.

Les performances sont correctes pour < 30 éléments, mais à partir de 100 éléments, elles sont terriblement lentes. S'agit-il d'une limitation de UICollectionView et des mises en page personnalisées, ou est-ce que je ne donne pas assez d'informations à la vue pour qu'elle fonctionne correctement ?

Source ici : https://github.com/oskarhagberg/calendarcollection

Mise en page : https://github.com/oskarhagberg/calendarcollection/blob/master/CalendarHeatMap/OHCalendarWeekLayout.m

Source de données et délégué : https://github.com/oskarhagberg/calendarcollection/blob/master/CalendarHeatMap/OHCalendarView.m

Profil de temps : Time profile - Custom layout

Mise à jour

Peut-être est-ce futile ? Quelques tests avec un simple UICollectionViewController avec un UICollectionViewFlowLayout qui reçoit approximativement la même quantité de cellules/écran donne un profil temporel similaire.

Time profile - Standard flow layout

Je pense qu'il debe être capable de gérer ~100 cellules opaques simples à la fois sans la gigue. Est-ce que je me trompe ?

0 votes

Non, vous n'avez pas tort. Mes tests avec un objet vanilla UICollectionView utilisant Flowlayout et un simple tableau de cellules allouées statiquement contenant des objets de visualisation d'images, montrent que vous pouvez facilement atteindre 100 cellules sans problème de performance. Avec une configuration classique sur un iPad 3, il faut s'attendre à des problèmes de performances à partir de 700 cellules.

1 votes

Dans mon cas, les fonctions liées à NSDate prennent trop de temps...

0 votes

Pouvez-vous expliquer comment vous avez créé votre vue de collection de cellules vanille ? Sur l'iPad 3, les cellules ne contiennent qu'une seule image (la même pour toutes les cellules), même avec 50 cellules ce n'est pas complètement lisse, et complètement inutilisable au-delà de 150 cellules. De plus, quand je dis 150 cellules, je veux dire visibles à la fois sur l'écran, pas dans l'ensemble des données. Avec 30 cellules à l'écran, je peux facilement maintenir la fluidité même avec des ensembles de données énormes (100K+).

93voto

Altimac Points 174

N'oubliez pas non plus d'essayer de rastériser la couche de la cellule :

cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;

J'avais 10 fps sans ça, et boom 55fps avec ! Je ne suis pas vraiment familier avec le GPU et le modèle de composition, donc ce qu'il fait exactement n'est pas tout à fait clair pour moi, mais en gros il aplatit le rendu de toutes les sous-vues en un seul bitmap (au lieu d'un bitmap par sous-vue ?). Quoi qu'il en soit, je ne sais pas s'il y a des inconvénients, mais il est nettement plus rapide !

0 votes

J'ai eu besoin d'ajouter ceci en utilisant iOS7, ce n'était pas nécessaire pour ios6, mais mon FPS était haché (50), maintenant 59 et lisse comme de la soie !

0 votes

De plus, je dois appeler [cell layoutSubviews] dans cellForItemAtIndexPath après le code ci-dessus.

0 votes

L'inconvénient, entre autres, est que tout réside en mémoire à la place. Cela doit être évalué et peut différer selon les cas d'utilisation. Comme mentionné ici : stackoverflow.com/questions/11521959/

18voto

TheBasicMind Points 801

J'ai mené une enquête approfondie sur les performances de UICollectionView. La conclusion est simple. Les performances sont médiocres pour un grand nombre de cellules.



EDIT : Mes excuses, je viens de relire votre message, le nombre de cellules que vous avez devrait être correct (voir le reste de mon commentaire), donc la complexité des cellules peut aussi être un problème.

Si votre conception le permet, vérifiez :

  1. Chaque cellule est opaque.

  2. Le contenu des cellules s'accroche aux limites.

  3. Les coordonnées des cellules ne contiennent pas de valeurs fractionnaires (par exemple, elles sont toujours calculées comme des pixels entiers).

  4. Essayez d'éviter les chevauchements de cellules.

  5. Essayez d'éviter les ombres portées.



La raison en est en fait très simple. Beaucoup de gens ne la comprennent pas, mais elle vaut la peine d'être comprise : Les UIScrollViews n'utilisent pas de Core Animation pour défiler. Ma croyance naïve était qu'elles impliquaient une "sauce" secrète d'animation de défilement et qu'elles demandaient simplement des mises à jour occasionnelles aux délégués de temps en temps pour vérifier le statut. Mais en fait, les vues de défilement sont des objets qui n'appliquent directement aucun comportement de dessin. Tout ce qu'ils sont vraiment, c'est une classe qui applique une fonction mathématique abstraite du placement des coordonnées des UIViews qu'ils contiennent, de sorte que les coordonnées des vues sont traitées comme relatives à un plan abstrait de ContentView plutôt que relatives à l'origine de la vue qui les contient. Une vue défilante mettra à jour la position du plan de défilement abstrait en fonction de l'entrée de l'utilisateur (par exemple, le balayage) et, bien sûr, il y a aussi un algorithme physique qui donne un "momentum" aux positions des coordonnées traduites.

Maintenant, si vous deviez produire votre propre objet de mise en page de vue de collection, en théorie, vous pourriez en produire un qui inverse à 100% la translation mathématique appliquée par la vue de défilement sous-jacente. Ce serait intéressant mais inutile, car il semblerait alors que les cellules ne bougent pas du tout lorsque vous les faites glisser. Mais je soulève cette possibilité parce qu'elle illustre le fait que l'objet de mise en page de la vue de collection travaillant avec l'objet de vue de collection lui-même effectue une opération très similaire à celle du scrollview sous-jacent. Par exemple, il offre simplement la possibilité d'appliquer une traduction mathématique supplémentaire, image par image, des attributs des vues à afficher, et dans la plupart des cas, il s'agira d'une simple traduction des attributs de position.

Ce n'est que lorsque de nouvelles cellules sont insérées, supprimées, déplacées ou rechargées que CoreAnimation intervient, le plus souvent en appelant :

- (void)performBatchUpdates:(void (^)(void))updates
                 completion:(void (^)(BOOL finished))completion

UICollectionView demande les attributs de mise en page des cellules pour chaque image de défilement et chaque vue visible est mise en page pour chaque image. Les UIView sont des objets riches optimisés pour la flexibilité plus que pour les performances. Chaque fois qu'une vue est disposée, le système effectue un certain nombre de tests pour vérifier son alpha, son zIndex, ses sous-vues, ses attributs d'écrêtage, etc. La liste est longue. Ces vérifications et les modifications de la vue qui en résultent sont effectuées pour chaque élément de vue de la collection pour chaque image.

Pour garantir de bonnes performances, toutes les opérations image par image doivent être effectuées dans un délai de 17 ms. [J'ai mis cette clause entre crochets car j'ai relu votre message et je me suis rendu compte que je l'avais mal lu. Avec le nombre de cellules que vous avez, il ne devrait pas y avoir de problème de performance. J'ai personnellement constaté qu'avec un test simplifié avec des cellules vanille, contenant une seule image en cache, la limite testée sur un iPad 3 est d'environ 784 cellules à l'écran avant que les performances ne commencent à chuter en dessous de 50 images par seconde.

Dans la pratique, vous devrez rester en deçà de cette valeur.

Personnellement, j'utilise mon propre objet de mise en page personnalisé et j'ai besoin de performances plus élevées que celles fournies par UICollectionView. Malheureusement, je n'ai exécuté le test simple ci-dessus qu'à un certain stade du développement et j'ai réalisé qu'il y avait des problèmes de performances. Je vais donc retravailler le back-port open source de UICollectionView, PSTCollectionView. Je pense qu'il existe une solution de contournement qui peut être mise en œuvre de sorte que, juste pour le défilement général, la couche de chaque élément de cellule est écrite en utilisant une opération qui contourne la mise en page de chaque cellule UIView. Cela fonctionnera pour moi puisque j'ai mon propre objet de mise en page, et je sais quand la mise en page est nécessaire et j'ai une astuce qui permettra au PSTCollectionView de revenir à son mode de fonctionnement normal à ce moment-là. J'ai vérifié la séquence d'appels et elle ne semble pas être trop complexe, ni du tout irréalisable. Mais il est certain que ce n'est pas trivial et que d'autres tests doivent être effectués avant que je puisse confirmer que cela fonctionnera.

0 votes

Mes problèmes de performance sont sur un iPhone5. Peut-être que l'iPad 3 peut contenir beaucoup plus de cellules/écran. Vous mentionnez que le UICollectionView demande layoutAttributes pour chaque image, mais dans mon cas, je trouve que ce n'est pas le cas. Il demande un tableau d'attributs par lots avec un rectangle de la taille de l'écran seulement quelques fois lors du défilement. J'imagine donc qu'il devrait être capable de disposer les cellules sur la vue de contenu des vues défilantes, puis d'ajuster simplement le décalage (exactement comme vous le dites). Mais même si je ne fais que déplacer la vue de quelques pixels vers le haut et vers le bas (aucun attribut n'est demandé), elle est toujours aussi lente.

0 votes

Il est intéressant que vous mentionniez PSTCollectionView parce que c'est ce que j'ai peur de devoir faire aussi. Ça semble être une énorme entreprise. Soit ça, soit regrouper quelques UIViews en moins UICollectionViewCell s. Toujours la même quantité totale de UIView /écran, mais beaucoup moins de cellules. Mais avec cette solution, je perds la possibilité de passer d'une mise en page à l'autre et de faire des mises à jour par lots, et ce serait une déception. Ou simplement vivre avec le décalage, et espérer qu'ils l'améliorent dans iOS7.

0 votes

Re : PST Collection view, l'amélioration des performances de défilement est certainement non triviale et une entreprise beaucoup plus grande si vous devez construire votre propre objet de mise en page pour le faire. Cependant, je l'ai déjà fait (parce que j'avais besoin d'une feuille de calcul comme le défilement sur les axes x et y), donc avec un objet de mise en page en main, bien que potentiellement non trivial, c'est également une modification chirurgicale assez nucléée qui est requise. Nous verrons bien !

8voto

Andreas Karlsson Points 148

Quelques observations supplémentaires qui pourraient être utiles :

Je suis en mesure de reproduire le problème, en utilisant une mise en page de flux avec environ 175 éléments visibles à la fois : le défilement est fluide dans le simulateur, mais il est très lent sur l'iPhone 5. Je me suis assuré qu'ils sont opaques, etc.

enter image description here

Ce qui prend le plus de temps semble être le travail avec un NSDictionary mutable à l'intérieur. _updateVisibleCellsNow . Il s'agit à la fois de copier le dictionnaire et de rechercher des éléments par clé. Les clés semblent être UICollectionViewItemKey et la balise [UICollectionViewItemKey isEqual:] est la méthode la plus longue de toutes. UICollectionViewItemKey contient au moins type , identifier y indexPath et la comparaison de la propriété contenue indexPath [NSIndexPath isEqual:] prend le plus de temps.

D'après cela, je suppose que la fonction de hachage de UICollectionViewItemKey n'est pas suffisante car isEqual: est appelé si souvent pendant la recherche de dictionnaire. De nombreux éléments peuvent se retrouver avec le même hachage (ou dans le même seau de hachage, je ne sais pas comment fonctionne le NSDictionary).

Pour une raison quelconque, il est plus rapide avec tous les éléments dans une seule section, par rapport à plusieurs sections avec 7 éléments dans chacune. Probablement parce qu'il passe beaucoup de temps dans NSIndexPath isEqual et que c'est plus rapide si la ligne diffère en premier, ou peut-être parce que UICollectionViewItemKey obtient un meilleur hachage.

Honnêtement, cela semble vraiment bizarre que UICollectionView fasse ce lourd travail de dictionnaire à chaque frame de défilement, comme mentionné précédemment, chaque mise à jour de frame doit être <16ms pour éviter le lag. Je me demande si ce nombre de recherches dans le dictionnaire est suffisant :

  • Vraiment nécessaire pour le fonctionnement général de UICollectionView
  • Il s'agit de prendre en charge certains cas limites rarement utilisés et qui peuvent être désactivés pour la plupart des mises en page.
  • Du code interne non optimisé qui n'a pas encore été corrigé.
  • Une erreur de notre part

Espérons que nous verrons une amélioration cet été pendant la WWDC, ou si quelqu'un d'autre ici peut trouver un moyen de le corriger.

0 votes

Grandes observations. Peut-être que c'est quelque chose à classer dans un radar.

0 votes

À bien y réfléchir, la consultation d'un dictionnaire est nécessaire car, contrairement à une vue en tableau, l'ordre d'affichage des cellules n'est pas garanti. Vous pouvez définir les cellules pour qu'elles suivent n'importe quelle règle à tout moment et la vue de la collection doit savoir, pour n'importe quel cadre donné, quelles cellules sont à l'écran et où elles doivent être placées (même si les cellules sont visibles à partir des sections 1, 2 et 3, il n'y a toujours aucune garantie que toutes les cellules de la section 2 soient à l'écran). Cela devient très clair lorsque vous lancez votre propre mise en page. Un tableau ne fera pas l'affaire car les chemins d'index requis seront souvent discontinus.

0 votes

Andreas, est-ce que vous roulez votre propre objet Layout ? J'ai aussi constaté des problèmes de performance avec NSDictionary mais Je pense Ce problème a été quelque peu résolu lorsque j'ai commencé à mettre en cache tous les objets layoutAttributes générés. J'ai remarqué que de mauvaises choses se produisent si ces objets ne sont pas mis en cache, de sorte que le même objet d'attributs est renvoyé pour un IndexPath donné lors de futures demandes d'objets de mise en page. Je dis pensez à car malheureusement, je n'ai remarqué l'amélioration de cette mesure que quelque temps plus tard et je n'ai pas encore effectué de tests spécifiques pour le confirmer.

6voto

Ash Furrow Points 6157

Le problème n'est pas le nombre de cellules que vous affichez dans le total de la vue de la collection, mais le nombre de cellules qui sont à l'écran en même temps. Comme la taille des cellules est très petite (22x22), vous avez 154 cellules visibles à l'écran en même temps. Le rendu de chacune d'entre elles est ce qui ralentit votre interface. Vous pouvez le prouver en augmentant la taille des cellules dans votre Storyboard et en relançant l'application.

Malheureusement, il n'y a pas grand-chose que vous puissiez faire. Je vous recommanderais d'atténuer le problème en évitant de couper les limites et en essayant de ne pas mettre en œuvre l'option drawRect: puisque c'est lent.

1 votes

Je parlais des cellules/écrans. J'aurais dû clarifier :). Et j'ai également eu des cellules qui sont de simples cellules sans enfants (et sans drawRect), même problème. D'après le profil, il semble que la vue de la collection effectue une copie de tous les attributs (visibles) à chaque boucle de rendu. C'est là que le temps du CPU est dépensé, pas pour le dessin.

1 votes

L'astuce drawRect m'a aidé à résoudre mon problème.

6voto

NSSplendid Points 1372

Un grand coup de chapeau aux deux réponses ci-dessus ! Voici une autre chose que vous pouvez essayer : J'ai eu de grandes améliorations dans UICollectionView les performances en désactivant la mise en page automatique. Bien que vous deviez ajouter du code supplémentaire pour agencer l'intérieur des cellules, le code personnalisé semble être beaucoup plus rapide que la mise en page automatique.

0 votes

Malheureusement, la désactivation de la mise en page automatique n'est d'aucune utilité dans mon cas. Les performances sont les mêmes, qu'elle soit activée ou désactivée. Je l'ai désactivé au niveau du Storyboard. Savez-vous s'il est possible de la désactiver uniquement pour les cellules ? Elles sont simples et ne nécessitent pas de mise en page sophistiquée.

0 votes

Je n'ai jamais attaché ça, mais ça pourrait aider : stackoverflow.com/a/15242064/319019

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X