En un
art铆culo anterior, aprendimos sobre las razones de la inestabilidad de las pruebas unitarias y c贸mo lidiar con ellas. Ahora queremos considerar una de las nuevas herramientas de Apple para depurar y perfilar c贸digo. Estamos hablando del marco de registro os_log presentado en WWDC 2018, que fue ampliado por la herramienta de an谩lisis de rendimiento, os_signpost.

En uno de los sprints, tuvimos la tarea de implementar la generaci贸n de un documento pdf en el lado del cliente. Completamos la tarea y demostramos con 茅xito los resultados al equipo. Pero quer铆amos asegurarnos de la efectividad de los matices t茅cnicos de la decisi贸n. Signpost nos ayud贸 con esto. Con 茅l, pudimos acelerar la visualizaci贸n del documento varias veces.
Para obtener m谩s informaci贸n sobre la tecnolog铆a de aplicaci贸n os_signpost, vea d贸nde puede ayudarlo y c贸mo ya nos ha ayudado, vaya debajo del gato.
Profundizando en el tema
Hay muchas aplicaciones en el tel茅fono del usuario, y todas ellas usan recursos comunes del sistema: CPU, RAM, red, bater铆a, etc. Si su aplicaci贸n realiza sus tareas y no se bloquea, esto no significa que funcione de manera eficiente y correcta. A continuaci贸n, describimos los casos que puede encontrar.
Un algoritmo sub贸ptimo puede conducir a una larga carga de CPU.- Al comienzo de la aplicaci贸n, despu茅s de 20 segundos de espera, el sistema cerrar谩 la aplicaci贸n y el usuario ni siquiera ver谩 la primera pantalla. En este caso, el sistema establecer谩 un informe de bloqueo, cuya caracter铆stica distintiva ser谩 el tipo de excepci贸n: EXC_CRASH (SIGKILL) con el tipo 0x8badf00d .
- Los procesos intensivos en recursos en el subproceso en segundo plano pueden afectar la capacidad de respuesta de la interfaz de usuario, aumentar el consumo de bater铆a y obligar a la aplicaci贸n a finalizar el sistema (si la CPU se sobrecalienta durante mucho tiempo).
Cajas de RAM:Las especificaciones para los tel茅fonos en el sitio web de Apple no proporcionan informaci贸n sobre la RAM, pero otras fuentes proporcionan la siguiente asignaci贸n de memoria para los modelos de tel茅fonos:
Tipo
| 4S
| 5 5
| 5C
| 5s
| 6 6
| 6P
| 6S
| 6SP
|
RAM, GB
| 0.5 0.5
| 1
| 1
| 1
| 1
| 1
| 2
| 2
|
Tipo
| SE
| X
| 7 7
| 7P
| 8
| 8P
| XS
| XSM
| Xr
|
RAM, GB
| 2
| 3
| 2
| 3
| 2
| 3
| 4 4
| 4 4
| 3
|
Cuando hay muy poca RAM libre, iOS comienza a buscar memoria para liberar, enviando simult谩neamente una advertencia de memoria a todas las aplicaciones en ejecuci贸n. Este proceso afecta impl铆citamente la CPU y la bater铆a del dispositivo. Si se ignora la advertencia de memoria y la asignaci贸n de memoria contin煤a, el sistema finaliza por la fuerza el proceso de solicitud. Para el usuario, esto parece un bloqueo, sin retrocesos en el informe del bloqueo.
Uso excesivo de solicitudes de red . Esto tambi茅n conduce a una disminuci贸n en la duraci贸n de la bater铆a. La duplicaci贸n de solicitudes y / o la falta de cancelaci贸n de solicitudes innecesarias tambi茅n conduce a un uso ineficiente de la CPU.
No te olvides de CoreLocation . Cuanto m谩s a menudo y con mayor precisi贸n solicitemos la ubicaci贸n del usuario, m谩s se gasta la bater铆a del dispositivo. Para verificar la exactitud del procesamiento de los casos descritos, sugerimos usar os_signpost para perfilar los procesos de la aplicaci贸n y luego analizar los datos obtenidos.
Integraci贸n de herramientas en el proyecto.
En el nivel superior, el proceso de creaci贸n de un PDF consta de tres pasos:
- recibir datos a trav茅s de la red;
- formaci贸n de documentos;
- mostrar en la pantalla: decidimos dividir y registrar las etapas de generaci贸n de documentos, comenzando por el usuario haciendo clic en el bot贸n "Generar" y terminando con la visualizaci贸n del documento en la pantalla.
Supongamos que nos enfrentamos a la tarea de analizar una solicitud de red asincr贸nica. El marcado en el c贸digo se ver谩 as铆:
import os.signpost let pointsOfInterestLog = OSLog(subsystem: "com.example.your-app", category: . pointsOfInterest) let networkLog = OSLog(subsystem: "com.example.your-app", category: "NetworkOperations") os_signpost(.event, log: pointsOfInterestLog, name: "Start work") os_signpost(.begin, log: networkLog, name: "Overall work") for element in elements { os_signpost(.begin, log: networkLog, name: "Element work") makeWork(for: element) os_signpost(.end, log: networkLog, name: "Element work") } os_signpost(.end, log: networkLog, name: "Overall work")
Los pasos para usar el poste indicador son los siguientes:
- Importe el marco os.signpost.
- Cree una instancia de OSLog. Vale la pena considerar que hay varios tipos de eventos: para eventos de intervalo (por ejemplo, una solicitud de red), puede usar una categor铆a arbitraria, y para eventos simult谩neos (por ejemplo, hacer clic en un bot贸n), la categor铆a predefinida pointsOfInterest / OS_LOG_CATEGORY_POINTS_OF_INTEREST.
- Para eventos de intervalo, llame a la funci贸n os_signpost con el tipo .begin y .end al principio y al final de la etapa bajo investigaci贸n. Para eventos simult谩neos, use el tipo .event.
- Si el c贸digo bajo investigaci贸n se puede ejecutar de forma as铆ncrona, agregue una ID de se帽alizaci贸n, que le permitir谩 separar los intervalos del mismo tipo de operaciones con diferentes objetos.
- Opcionalmente, puede agregar datos adicionales (metadatos) a los eventos enviados. Por ejemplo, el tama帽o de las im谩genes descargadas a trav茅s de la red o el n煤mero de la p谩gina PDF generada. Dicha informaci贸n ayudar谩 a comprender qu茅 sucede exactamente en la etapa investigada de la ejecuci贸n del c贸digo.
Del mismo modo en obj-c:
@import os.signpost; os_log_t pointsOfInterestLog = os_log_create("com.example.your-app", OS_LOG_CATEGORY_POINTS_OF_INTEREST); os_log_t networkLog = os_log_create("com.example.your-app", "NetworkOperations"); os_signpost_id_t operationIdentifier = os_signpost_id_generate(networkLog); os_signpost_event_emit(pointsOfInterestLog, operationIdentifier, "Start work"); os_signpost_interval_begin(networkLog, operationIdentifier, "Overall work"); for element in elements { os_signpost_id_t elementIdentifier = os_signpost_id_make_with_pointer(networkLog, element); os_signpost_interval_begin(networkLog, elementIdentifier, "Element work"); [element makeWork]; os_signpost_interval_end(networkLog, elementIdentifier, "Element work"); } os_signpost_interval_end(networkLog, operationIdentifier, "Overall work");
A una nota. Si el proyecto debe ejecutarse en iOS antes de la versi贸n 12.0, Xcode ofrecer谩 ajustar las llamadas os_signpost en la construcci贸n if #available. Para no saturar el c贸digo, puede colocar esta l贸gica en una clase separada.
Vale la pena considerar que os_signpost requiere un literal de cadena est谩tica como par谩metro del nombre del evento. Para agregar una escritura m谩s estricta, puede crear una enumeraci贸n con tipos de eventos y, en la implementaci贸n de la clase, asignarlos a literales de cadena. Poner OSLog en una clase separada agregar谩 la l贸gica para deshabilitarlo para el esquema de lanzamiento (hay un comando OSLog separado para esto).
import os.signpost let networkLog: OSLog if ProcessInfo.processInfo.environment.keys.contains("SIGNPOSTS_FOR_NETWORK") { networkLog = OSLog(subsystem: "com.example.your-app", category: "NetworkOperations" } else { networkLog = .disabled }
Puede agregar valores de cualquier propiedad a la marca de evento con los siguientes decodificadores de tipo para un formato conveniente:
Tipo de valor
| Especificador personalizado
| Salida de ejemplo
|
tiempo_t
| % {time_t} d
| 2016-01-12 19:41:37
|
timeval
| % {timeval}. * P
| 2016-01-12 19: 41: 37.774236
|
especificaci贸n de tiempo
| % {timespec}. * P
| 2016-01-12 19: 41: 37.2382382823
|
errno
| % {errno} d
| Tubo roto
|
iec-bytes
| % {iec-bytes} d
| 2.64 MiB
|
bitrate
| % {bitrate} d
| 123 kbps
|
velocidad de bits iec
| % {iec-bitrate} d
| 118 kibps
|
uuid_t
| % {uuid_t}. * 16P % {uuid_t}. * P
| 10742E39-0657-41F8-AB99-878C5EC2DCAA
|
Ahora, al perfilar la aplicaci贸n, los eventos de os_signpost se enviar谩n a los instrumentos en forma de datos tabulares. Para cambiar a herramientas, use el m茅todo abreviado de teclado Cmd + I, luego seleccione la herramienta necesaria para la creaci贸n de perfiles. Para ver los datos marcados, solo active las herramientas os_signpost y Punto de inter茅s en el lado derecho de la interfaz de la herramienta.

Por defecto, los eventos se agrupan en categor铆as y se muestran en una tabla, donde se calculan su n煤mero y estad铆sticas sobre el tiempo de ejecuci贸n. Adem谩s, hay una pantalla gr谩fica en la l铆nea de tiempo, que facilita la comparaci贸n de los eventos recibidos con los resultados en otras herramientas. Tambi茅n existe la posibilidad de personalizar la visualizaci贸n de estad铆sticas y escribir sistemas expertos, pero este tema merece un art铆culo aparte.
Ejemplos de uso
Caso No. 1. PDFKit vs WKWebView
Mediante el uso de os_signpost, vimos que para documentos peque帽os (un par de p谩ginas) el paso m谩s largo fue el 煤ltimo paso, mostrar el documento, en lugar de trabajar con una red o gr谩ficos. Esto nos llev贸 a la decisi贸n de reemplazar
WKWebView con
PDFView , lo que aceler贸 la visualizaci贸n del documento de 1,5 segundos a 30 milisegundos. En los gr谩ficos, se ve as铆:
Mostrar un documento PDF (WKWebView) en Time Profiler
Mostrar un documento PDF (PDFView) en Time ProfilerLos datos resultantes se pueden implementar en otras herramientas que proporciona Xcode. Como mostr贸 la herramienta de asignaciones, la ganancia en velocidad de descarga se logr贸 al aumentar el uso de RAM.
Caso No. 2. Advertencia de baja memoria
Un documento PDF se genera de forma as铆ncrona, y su formaci贸n requiere la asignaci贸n de una cantidad significativa de memoria. En caso de memoria insuficiente, decidimos agregar la capacidad de detener la operaci贸n asincr贸nica de crear un documento.
Como sabe, cuando usa NSOperationQueue, el m茅todo cancelAllOperation libera una cola existente, pero no detiene las operaciones que ya se est谩n ejecutando. De esto concluimos que en la implementaci贸n de la operaci贸n es necesario determinar peri贸dicamente su condici贸n y dejar de funcionar. Por lo tanto, libera recursos si est谩 configurado en estado Cancelado.
El siguiente paso es una operaci贸n asincr贸nica que debemos verificar para cancelar. Pero al mismo tiempo, no est谩 claro con qu茅 frecuencia hacer esta verificaci贸n. Ten铆amos dos opciones: verificaci贸n l铆nea por l铆nea y verificaci贸n p谩gina por p谩gina. os_signpost tambi茅n ayud贸 aqu铆. Al final result贸 que, agregando un cheque por cancelaci贸n en el ciclo l铆nea por l铆nea de renderizar la tabla en el documento, aumentamos el tiempo que tard贸 en generar el documento (en 150 p谩ginas) en 2 veces. La segunda opci贸n era m谩s 贸ptima en t茅rminos de rendimiento y en realidad no aument贸 el tiempo que llev贸 crear el documento. Como resultado, cuando recibimos el evento de advertencia de memoria, cancelamos la operaci贸n mediante programaci贸n y mostramos la pantalla de error para el usuario.
Para asegurarnos de que la memoria se libere, tambi茅n podemos usar os_signpost. Esta vez agregando un marcador sobre el inicio del evento en el m茅todo didRecieveMemoryWarning y un marcador sobre el final en viewDidLoad de la pantalla de error. Por cierto, puede emular un evento de memoria insuficiente en el simulador (shift + command + m).
Caso No. 3. Actualizar restricciones
Poste indicador puede ser 煤til en el proceso de dise帽o. Para crear restricciones, utilizamos el marco de
alba帽iler铆a . La documentaci贸n para el marco dice que se recomienda usar el m茅todo updateConstraints () para crear restricciones. Pero Apple desaconseja hacerlo, y puede verificar esto con el marcado de se帽alizaci贸n.

De acuerdo con la documentaci贸n de Apple, updateConstraints solo debe usarse para cambiar restricciones si no podemos hacerlo en el lugar donde ocurri贸 el cambio.

Despu茅s de analizar los resultados, llegamos a la conclusi贸n de que la llamada updateConstraints no es tan frecuente en nuestra aplicaci贸n, aproximadamente cada vez que aparece la vista en la pantalla.
A pesar de esto, para evitar posibles defectos de rendimiento, recomendamos seguir los consejos de Apple al respecto.
Conclusiones
En 2018, Apple brind贸 a los desarrolladores la oportunidad de expandir de manera independiente las herramientas de creaci贸n de perfiles. Por supuesto, puede usar otras herramientas de depuraci贸n: puntos de interrupci贸n, salida a la consola, temporizadores, perfiladores personalizados. Pero tardan m谩s tiempo en implementarse o no siempre dan una imagen completa de lo que est谩 sucediendo.
En el pr贸ximo art铆culo, consideraremos c贸mo usar la informaci贸n recibida del panel de manera m谩s eficiente escribiendo nuestro propio sistema experto (Custom Instruments).
Enlaces utilesEl art铆culo fue escrito con @victoriaqb - Victoria Kashlina, desarrolladora de iOS.