Gotta Go Fast: Building for Speed ​​en iOS. Parte 2



A veces puede encontrarse en una situación en la que su aplicación no puede funcionar bien. Aquí hay algunos instrumentos que puede usar y las mejores prácticas que puede implementar para mejorar las cosas.

Esta es la segunda parte del artículo basado en la charla magistral impartida por Luke Parham, un ingeniero de iOS en Fyusion y autor de tutoriales para el desarrollo de iOS en RayWenderlich.com, en la Conferencia Internacional de Desarrolladores Móviles MBLT DEV en 2017.

Instrumento de animación central


Si ha realizado muchos perfiles y ha encontrado todos sus cuellos de botella, a veces todavía hay problemas de rendimiento. Esto se debe a la forma en que las cosas de la interfaz de usuario funcionan en iOS. Cada vez que configura marcos o crea UIViews, lo que realmente sucede debajo del capó es hacer una CATransaction o el sistema lo hace por usted. Y estos se envían a una cosa llamada "el servidor de render". El servidor de renderizado se encarga de hacer animaciones. Si haces un UIView animateWith: lo que sea, todo eso sucederá en el servidor de representación, que es otro hilo y maneja todas las animaciones de la aplicación.



Aquí hay un analizador de tiempo que tiene un indicador de velocidad de fotogramas en la parte superior. Y en la parte inferior está la parte más importante de las opciones de depuración. Vamos a contar sobre las dos cosas más importantes y más fáciles de solucionar.



El primero es capas de colores mezclados. Este es realmente fácil de arreglar. Y esto nos lleva a la primera sección de actuación policial. Básicamente, muchas aplicaciones tienen problemas: incluso iMessage, querida aplicación de Apple, está haciendo muchas cosas no realmente geniales. Aquí vemos que hay mucho rojo:



Rojo significa que tiene etiquetas que tienen un fondo blanco. Y luego están encima de otro fondo blanco y, por alguna razón, no están configurados para ser opacos. Entonces la licuadora está combinando estos colores, blanco y blanco y, como resultado, está obteniendo un color blanco. Por cada píxel que tiene rojo, está haciendo cálculos adicionales sin ningún beneficio, aún obtienes blanco en el fondo.

Para evitar esto, puede hacer que las capas sean opacas siempre que sea posible si son del mismo color en el mismo color. Si la subvista tiene el mismo color de fondo, la combinación no es necesaria. Todo lo que tiene que hacer es establecer la opacidad de sus capas en 1 y luego asegurarse de que esté configurado el color de fondo. Si el color de fondo es claro, no siempre será opaco.



Representación fuera de pantalla


Los elementos renderizados fuera de pantalla se mostrarán en amarillo si activa esta opción. Lo bueno del instrumento Core Animation es que puedes ver otras aplicaciones. Puede activar estas opciones y luego ir a cualquier aplicación en su sistema y puede ver qué están haciendo mal. En este caso, Instagram tiene estas pequeñas burbujas en la parte superior que te muestran las historias de las personas.



Como puede ver, todos son amarillos. En iPhone 5 son agresivamente lentos. Y esto se debe a que el renderizado fuera de pantalla es mucho peor que la mezcla alfa. Se detiene la GPU. Termina teniendo que hacer cálculos adicionales de ida y vuelta entre la GPU y la CPU, por lo que obtienes paradas adicionales que son innecesarias la mayor parte del tiempo.

Bezier camino en lugar de ver las curvas


La siguiente regla: no use la propiedad de radio de esquina. Si tiene una vista y configura view.layer.cornerRadius, esto siempre presenta el renderizado fuera de pantalla. En su lugar, puede usar una ruta bezier y el mismo tipo de material CGBitmap de antes. En este caso, un contexto UIGraphics. Esta función funciona con UIImage que toma en un tamaño, hace esquinas redondeadas en función de ese tamaño y utiliza una ruta bezier para recortar. Luego recortamos la imagen y la devolvemos del contexto UIImage. Por lo tanto, esto devolverá una imagen redondeada previamente en lugar de redondear la vista dentro de la cual se encuentra la imagen.



El ultimo ejemplo. Aquí está Twitter y esta es una vista en tiempo real de esta animación en ejecución. Se supone que se abre y le muestra la información, pero todo este texto y otras cosas se han visualizado fuera de la pantalla, por lo que se ralentizó la animación. Esta es la cosa de menor rendimiento que he encontrado en una aplicación que se encuentra en la App Store.



Entonces, ¿cómo sucedió esto? Una cosa que hace que esto suceda es la propiedad shouldRasterize de un CALayer. Es una opción en una capa que le permite almacenar en caché las texturas que se han renderizado. Hay muchas reglas extrañas. Al igual que si no se ha utilizado en una cierta cantidad de milisegundos, abandonará el caché. Y luego, si sale de la memoria caché, se representará fuera de la pantalla en cada fotograma. Realmente no vale la pena los posibles beneficios que tiene. Y es difícil verificar si realmente lo está beneficiando.

Resumen


Evite el renderizado fuera de la pantalla y la mezcla alfa si puede. La mezcla alfa a veces es necesaria. Es mejor que el renderizado fuera de pantalla. El renderizado fuera de pantalla ocurre por un par de razones. Puede suceder desde las sombras; puede suceder desde el redondeo de la esquina; Puede suceder por el enmascaramiento.

Haga las vistas opacas cuando sea posible. No use la propiedad de radio de esquina, use las rutas de Bezier tanto como pueda. Además, no use las propiedades de sombra de capa si está haciendo sombras de texto. Puedes usar NSShadow en su lugar.

Rastreo de actividad


El rastreo de actividad es una especie de versión de nivel mucho más bajo de algo que haría el generador de perfiles de tiempo. Te da una vista de todos tus hilos y cómo están interactuando. Y es bastante complicado. Pero tiene características realmente agradables que puede configurar.

Sistema de seguimiento


Use System Trace para rastrear tiempos para eventos específicos. Puede configurar formas de rastrear eventos específicos y secciones de código y ver cuánto tardan en una aplicación del mundo real. Le permite obtener información detallada sobre lo que está sucediendo en su sistema.

  • Use "Publicaciones de señalización" para indicar cuándo sucede algo importante.
  • Los puntos son eventos únicos cuando / si quieres ver como ocurrió una animación o algo así.
  • Las regiones tienen un principio y un fin. Para la decodificación de imágenes, puede ver cuándo comienza y cuándo termina para poder estimar cuánto tiempo tomó en general.




Aquí es cómo configurar una plantilla de rastreo del sistema. Haces esta lista de eventos que pueden suceder. Entonces, el número uno es una descarga de imágenes. Dos es una decodificación de imágenes, y tres es esta animación de inclinación que agregué. Básicamente, configura algunas opciones adicionales para ver qué colores van a ser. Básicamente, le envía un número como 1 o 2, será rojo o verde según lo que envíe allí.



Si está en Objective-C, debe importar este encabezado kdebug_signpost. En Swift, solo está disponible para ti.



Y luego debe llamar a esta función, kdebug_signpost o kdebug_signpost_start y kdebug_ signpost_end. Y funcionan con el código que ingresaste. Así que configuramos esos tres eventos con esos números. Entonces pasas ese número aquí. Le pasa un objeto que es básicamente la clave para este evento. Y luego, el último número es el color. Entonces 2 es como si supieras rojo o algo así.

Tengo un proyecto de ejemplo Swift en GitHub . En cierto modo simplifiqué las cosas. Hay un comienzo y un final que son un poco más fáciles de manejar.

Así se verá una vez que haya ejecutado un seguimiento. No te mostrará nada al principio. Luego, cuando elimines la aplicación, hará algunos cálculos y te mostrará cosas aquí.



Aquí podemos ver nuestras descargas de imágenes que tomaron alrededor de 200 milisegundos. Y luego, hay una decodificación de imágenes que tomó como 40 milisegundos. Esto es realmente genial si tienes un montón de cosas locas en tu aplicación. Puede configurar todos estos eventos y luego ver la lectura de cuánto tardan cada uno y cómo interactúan entre sí. Eso es todo para el seguimiento del sistema.

Bono


Eche un vistazo al ejemplo de una desaceleración de la cámara donde podemos ver qué sucede si hay elementos de AR en la aplicación:



Aplicamos un efecto y ocupaba el 26.4% de todos los cálculos en cada cuadro solo para calcular un efecto. Y estaba ralentizando la cámara a algo loco como 10 cuadros por segundo.

Cuando busqué aquí y miré este gran cuello de botella, vi que lo mejor que estaba haciendo la mayor parte del trabajo era el uso de NSDispatchData intenso.



Esta es una subclase de NSData. Y todo esto es obtener bytes con la función de rango. Y esa es una función simple. Todo lo que hace es tomar algunos bytes de un dato y colocarlo en otro lugar. No es una locura, pero, aparentemente, todas las cosas que estaba haciendo internamente ocupaban el 18% de este 26%.

Regla n. ° 1

Es un NSData y está obteniendo bytes. Eso es algo simple de Objective-C, pero si te encuentras con eso y eso es un cuello de botella, entonces es hora de cambiar a usar C en su lugar. Dado que el cuello de botella fue alrededor de una llamada para obtener valores flotantes, puede usar memcpy (). Con memcpy () puede mover una porción de datos a otro lugar. Corta muchos gastos generales.

Si miras como NSData, estas clases son como miles de líneas. Así que hay muchas cosas pasando allí. En este caso, tenemos el original en rojo.



Aquí obtienes un rango, toma algunos bytes y cópialos en el búfer. La versión memcpy () es casi exactamente la misma cosa. No parece más complicado y agresivamente hace menos cosas.



Cuando cambiamos eso y lo ejecutamos nuevamente, las cosas pasaron del 26% al 0.6% al cambiar esa línea a memcpy (). Y luego, la velocidad de fotogramas aumentó dramáticamente.

Regla # 2

Evite sobregirar si está haciendo algún tipo de aplicación de renderizado o incluso si está haciendo algo como una barra de carga. Muchas veces los eventos sucederán más de 60 cuadros por segundo. En ese caso, puede acelerar esta actualización de la interfaz de usuario utilizando un CADisplayLink. Tiene una propiedad llamada preferredFramesPerSecond. Eso es solo para iOS 10 o superior. Para los más antiguos, debe hacerlo manualmente, pero sigue siendo útil.



Puede establecer la velocidad de fotogramas deseada. Muchas veces, por ejemplo, para cargar barras, lo estableceré alrededor de 15 fotogramas por segundo porque realmente no importa. No tiene que actualizar 60 cuadros por segundo. Esto puede ahorrarle mucho trabajo que realmente hace si las cosas se ven igual en ambos sentidos.

Regla # 3

Utilice el almacenamiento en caché de IMP. Esto es útil solo para Objective-C. En Objective-C, cuando llama a un método oculto, en realidad está llamando a la función de envío de mensajes Objective-C (objc_msgSend ()). Si está viendo estas llamadas en trazas que ocupan una gran cantidad de tiempo, esto es algo de lo que puede deshacerse fácilmente. Básicamente es la tabla de caché donde busca punteros de función dándole un nombre de algún método. En lugar de hacer esa búsqueda cada vez, puede almacenar en caché el puntero de función y simplemente llamarlo directamente. Es al menos dos veces más rápido por lo general.



Si no tiene un puntero en caché, puede tomarlo llamando a methodForSelector:. Luego simplemente llamamos a este método como una llamada a función normal. Usted pasa el selector al objeto y luego cualquier argumento viene después de eso.

Regla n. ° 4

No use ARC. ARC es algo que agrega un montón de sobrecarga. En su código, tiene todas estas cosas sucediendo y salpica todo con retenciones y lanzamientos. Hace tanto como tiene que hacer, y hace un montón más. Entonces, si realmente desea optimizar, si ve que tiene un montón de llamadas retenidas y liberadas en su rastreo y que están tomando mucho tiempo, puede cambiar a no usar ARC, lo que es mucho más trabajo.

También es difícil hacer que tus compañeros de equipo acepten hacer esto y no enojarse por eso.

No use Swift si es especialmente sensible al rendimiento. Swift es un buen lenguaje. Tiene algunas características realmente buenas. Pero también usa más repeticiones en el interior para obtener un alto nivel de funcionalidad. Si desea ser rápido, debe acercarse lo más posible al ensamblaje lo más cerca posible de las cosas de bajo nivel. Y eso será más rápido porque es menos código automáticamente.

Si está investigando las cosas si pensó que era interesante, hay un libro realmente bueno llamado "iOS y MacOS: Ajuste de rendimiento" de Marcel Weiher. Se profundiza en muchas de estas cosas y mucho más allá de esto. También tengo una serie de videos. Hago videos para RayWenderlich. Hice una serie de instrumentos prácticos que profundiza y explica un poco más estas cosas y tiene algunos ejemplos. Entonces, si desea obtener más información sobre los instrumentos específicamente, puede ver esa serie de videos. Y luego, videos de WWDC: hay un montón de ellos que explican diferentes cosas de rendimiento como esta.

Video


Aquí puede encontrar la primera parte de un artículo basado en la charla de Luke. Mira la charla completa aquí:

Source: https://habr.com/ru/post/es436440/


All Articles