Optimización del tiempo de construcción - Parte 1

Casi todos los desarrolladores al menos una vez encontraron un tiempo de construcción bastante largo para su proyecto. Esto conduce a una reducción de la productividad y ralentiza el proceso de desarrollo de todo el equipo. Como puede ver, aumentar el tiempo de construcción de un proyecto es crucial porque tiene un impacto directo en el momento en que la aplicación se publica en la AppStore y para el lanzamiento más rápido de nuevas características en su aplicación.

En este artículo, aprenderemos cómo perfilar un ensamblaje en Xcode y obtener métricas. En el próximo artículo hablaré sobre los métodos para eliminar los cuellos de botella y acelerar el montaje del proyecto. También debe mencionarse que utilizaremos el proyecto Kickstarter para iOS, que se puede encontrar en Github . ¡Entonces empecemos!

¿Qué estamos midiendo?


Lo primero que debemos hacer es determinar qué estamos tratando de medir y optimizar. Se pueden considerar dos opciones:

  • Construcción limpia: limpia y construye el proyecto. A menudo, las compilaciones limpias se realizan en CI para verificar una nueva solicitud de extracción y ejecutar pruebas unitarias.
  • Construcción incremental : construya el proyecto después de cambios significativos en el código fuente. El desarrollador crea este ensamblaje mientras trabaja en una nueva funcionalidad.

En la mayoría de los casos, reducir el tiempo durante la construcción limpia también debería acelerar la construcción incremental . La mejor opción sería crear métricas para ambos tipos de ensamblajes y rastrearlos. Mediremos el tiempo de compilación utilizando la configuración de depuración solo porque se usa la mayor parte del tiempo y tiene un mayor impacto en el desarrollo.

Calculamos el tiempo de montaje


La forma más efectiva de mejorar el tiempo de compilación debería ser un enfoque basado en la implementación y verificación de cambios basados ​​en métricas específicas. Considérelos, así como las herramientas que podemos usar para obtener información sobre el tiempo de construcción del proyecto.

Informe de tiempo de compilación de Xcode


Podemos obtener fácilmente datos de tiempo de construcción usando Xcode. Realiza un seguimiento de todas las compilaciones de forma predeterminada y le permite ver el tiempo de compilación del proyecto en el navegador de informes.

imagen

También tiene la opción de mostrar información similar en Xcode en el panel del visor de actividades . Se puede habilitar para la visualización utilizando la línea de comando ejecutando el comando: el tiempo de compilación del proyecto se muestra después del ensamblaje junto con el mensaje "Se realizó correctamente". Estas son solo dos opciones básicas que deberían darle una idea aproximada del tiempo de construcción limpio e incremental .

defaults write com.apple.dt.Xcode ShowBuildOperationDuration YES



imagen



Resumen del tiempo de construcción del proyecto en Xcode


Resumen de tiempo de compilación de Xcode: su primer amigo en obtener estadísticas de tiempo de compilación y cuellos de botella. Puede ejecutarlo a través de Producto-> Realizar acción-> Generar con resumen de temporización . Ahora verá un gran desglose del tiempo dedicado a diversas tareas:

imagen

este método debería ser un buen punto de partida para encontrar las tareas que requieren más tiempo en el proceso de ensamblaje. La captura de pantalla anterior muestra que CompileStoryboard , CompileXIB , CompileSwiftSources y las fases PhaseScriptExecution ocuparon la mayor parte del tiempo de compilación. Xcode pudo completar parte de las tareas en paralelo, por lo que el ensamblaje se completa mucho más rápido que el tiempo necesario para completar la tarea de cada uno de los comandos.

Podemos obtener un resumen del tiempo de compilación para la compilación limpia usando xcodebuild con la opción: buildWithTimingSummary : ahora obtengamos las mismas métricas para la compilación incremental. Cabe señalar que el tiempo de construcción incremental depende completamente de los archivos que se modifican en el proyecto. Para obtener resultados consistentes, puede modificar un archivo y compilar el proyecto. A diferencia de los sistemas Buck o Bazel, Xcode utiliza marcas de tiempo para determinar qué ha cambiado y qué debe reconstruirse. Podemos actualizar la marca de tiempo usando el tacto:

xcodebuild -project 'Kickstarter.xcodeproj' \
-scheme 'Kickstarter-iOS' \
-configuration 'Debug' \
-sdk 'iphonesimulator' \
-showBuildTimingSummary \
clean build | sed -n -e '/Build Timing Summary/,$p'

Build Timing Summary
CompileStoryboard (29 tasks) | 87.128 seconds
CompileSwiftSources (4 tasks) | 54.144 seconds
PhaseScriptExecution (14 tasks) | 18.167 seconds
CompileAssetCatalog (2 tasks) | 6.532 seconds
CompileXIB (21 tasks) | 6.293 seconds
CodeSign (7 tasks) | 3.069 seconds
Ld (4 tasks) | 2.342 seconds
LinkStoryboards (2 tasks) | 0.172 seconds
CompileC (3 tasks) | 0.122 seconds
Ditto (20 tasks) | 0.076 seconds
Touch (4 tasks) | 0.007 seconds
** BUILD SUCCEEDED ** [92.620 sec]



touch KsApi/mutations/CancelBackingMutation.swift && \
xcodebuild -project 'Kickstarter.xcodeproj' \
-scheme 'Kickstarter-iOS' \
-configuration 'Debug' \
-sdk 'iphonesimulator' \
-showBuildTimingSummary \
build | sed -n -e '/Build Timing Summary/,$p'

Build Timing Summary
PhaseScriptExecution (14 tasks) | 18.089 seconds
CodeSign (7 tasks) | 2.990 seconds
CompileSwiftSources (1 task) | 1.245 seconds
Ld (1 task) | 0.361 seconds
** BUILD SUCCEEDED ** [23.927 sec]

Advertencias de verificación de tipo


Cuando el tiempo de compilación es un cuello de botella, podemos obtener más información configurando Otros indicadores rápidos en las opciones de compilación del proyecto en Xcode. Cuando el indicador está habilitado, Xcode genera una advertencia para cualquier función o expresión que tome más de 100 ms para determinar el tipo de variables:

  • -Xfrontend -warn-long-function-bodies = 100
  • -Xfrontend -warn-long-expression-type-Check = 100

imagen

Ahora conoce el código con el que el compilador Swift tiene problemas y ahora puede intentar mejorarlo.

Opciones de diagnóstico del compilador


El compilador Swift tiene muchas opciones de diagnóstico integradas que puede usar para perfilar el ensamblaje.

  • -driver-time-compilation : un alto nivel de sincronización de las tareas realizadas por el controlador.
  • -Xfrontend -debug-time-compilation — .
  • -Xfrontend -debug-time-function-bodies – .
  • -Xfrontend -debug-time-expression-type-checking – .

Usemos el indicador -debug-time-compilation para obtener información sobre los archivos más lentos al compilar: Como puede ver, tomó 25 segundos compilar SettingsNewslettersCellViewModel.swift . Desde el registro de ensamblaje, puede obtener información adicional sobre el tiempo de compilación del archivo: ahora es obvio que la verificación de tipo y el análisis semántico son los trabajos que requieren más tiempo. Avancemos y enumeremos las funciones y expresiones más lentas en el paso Verificación de tipo :

xcodebuild -project 'Kickstarter.xcodeproj' \
-scheme 'Kickstarter-iOS' \
-configuration 'Debug' \
-sdk 'iphonesimulator' \
clean build \
OTHER_SWIFT_FLAGS="-Xfrontend -debug-time-compilation" |
awk '/CompileSwift normal/,/Swift compilation/{print; getline; print; getline; print}' |
grep -Eo "^CompileSwift.+\.swift|\d+\.\d+ seconds" |
sed -e 'N;s/\(.*\)\n\(.*\)/\2 \1/' |
sed -e "s|CompileSwift normal x86_64 $(pwd)/||" |
sort -rn |
head -3

25.6026 seconds Library/ViewModels/SettingsNewslettersCellViewModel.swift
24.4429 seconds Library/ViewModels/PledgeSummaryViewModel.swift
24.4312 seconds Library/ViewModels/PaymentMethodsViewModel.swift



===-------------------------------------------------------------------------===
Swift compilation
===-------------------------------------------------------------------------===
Total Execution Time: 25.6026 seconds (26.6593 wall clock)

---User Time--- --System Time-- --User+System-- ---Wall Time--- --- Name ---
24.4632 ( 98.3%) 0.5406 ( 76.5%) 25.0037 ( 97.7%) 26.0001 ( 97.5%) Type checking and Semantic analysis
0.0981 ( 0.4%) 0.1383 ( 19.6%) 0.2364 ( 0.9%) 0.2872 ( 1.1%) Name binding
0.1788 ( 0.7%) 0.0043 ( 0.6%) 0.1831 ( 0.7%) 0.1839 ( 0.7%) IRGen
0.0508 ( 0.2%) 0.0049 ( 0.7%) 0.0557 ( 0.2%) 0.0641 ( 0.2%) Parsing
0.0599 ( 0.2%) 0.0020 ( 0.3%) 0.0619 ( 0.2%) 0.0620 ( 0.2%) SILGen
0.0285 ( 0.1%) 0.0148 ( 2.1%) 0.0433 ( 0.2%) 0.0435 ( 0.2%) SIL optimization
0.0146 ( 0.1%) 0.0015 ( 0.2%) 0.0161 ( 0.1%) 0.0162 ( 0.1%) Serialization, swiftmodule
0.0016 ( 0.0%) 0.0006 ( 0.1%) 0.0022 ( 0.0%) 0.0022 ( 0.0%) Serialization, swiftdoc
0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0001 ( 0.0%) SIL verification, pre-optimization
0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) AST verification
0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) SIL verification, post-optimization
24.8956 (100.0%) 0.7069 (100.0%) 25.6026 (100.0%) 26.6593 (100.0%) Total



xcodebuild -project 'Kickstarter.xcodeproj' \
-scheme 'Kickstarter-iOS' \
-configuration 'Debug' \
-sdk 'iphonesimulator' \
clean build \
OTHER_SWIFT_FLAGS="-Xfrontend -debug-time-expression-type-checking \
-Xfrontend -debug-time-function-bodies" |
grep -o "^\d*.\d*ms\t[^$]*$" |
awk '!visited[$0]++' |
sed -e "s|$(pwd)/||" |
sort -rn |
head -5

16226.04ms Library/Styles/UpdateDraftStyles.swift:31:3
10551.24ms Kickstarter-iOS/Views/RewardCardContainerView.swift:171:16 instance method configureBaseGradientView()
10547.41ms Kickstarter-iOS/Views/RewardCardContainerView.swift:172:7
8639.30ms Kickstarter-iOS/Views/Controllers/AddNewCardViewController.swift:396:67
8233.27ms KsApi/models/templates/ProjectTemplates.swift:94:5

De la misma manera, perfilamos nuestro ensamblaje y descubrimos que en la etapa de verificación de tipo hay más bien cuellos de botella. Como siguiente paso, puede echar un vistazo a las funciones y expresiones enumeradas anteriormente e intentar optimizar la inferencia de tipos .

Tiempo de construcción objetivo


Es aconsejable medir el tiempo de montaje del objetivo por separado y mostrarlos en el gráfico. Puede ayudar a comprender qué objetivos se recopilan o se pueden recopilar en paralelo. Para hacer esto, puede usar la herramienta xcode-build-time-rendering. Configurémoslo como RubyGem: una vez completada la instalación, ejecute el siguiente comando para ingresar el registro de marca de tiempo en la Fase de compilación de Run Script de sus objetivos: Luego compile el proyecto e informe usando: Como resultado, debería obtener un buen diagrama de Gantt por tiempo de compilación que muestre el tiempo asambleas de todos sus objetivos:

gem install xcode-build-times



xcode-build-times install



xcode-build-times generate



imagen

Métricas agregadas


Sería genial agregar los diversos indicadores mencionados anteriormente. XCLogParser es una gran herramienta que puede ayudarte con esto. Es un analizador de registros para xcactivitylog generado por Xcode, y brinda mucha información sobre el tiempo de compilación para cada módulo y archivo en el proyecto, advertencias, errores y resultados de pruebas unitarias. Puede instalarlo clonando el repositorio e iniciando a través de la línea de comando: Este es un informe creado para el proyecto Kickstarter iOS:

git clone https://github.com/spotify/XCLogParser
rake build
xclogparser parse --project Kickstarter --reporter html



imagen

Automatización


Cabe señalar que las métricas del tiempo de montaje dependen del hardware y su uso. Puedes usar tu equipo para experimentos. La mejor opción sería automatizar el proceso tanto como sea posible y usar equipos de CI dedicados para las métricas diarias. En última instancia, se pueden monitorear en el tablero y notificar sobre el empeoramiento de los tiempos de construcción con Slack.

Conclusión


Reducir el tiempo de construcción de un proyecto es crítico para mejorar la productividad del desarrollador. Hoy aprendimos cómo medir el tiempo de construcción de un proyecto y obtener algunas métricas para el análisis ...

En la próxima publicación, consideraremos los métodos que pueden usarse para reducir el tiempo de construcción.

¡Gracias por leer!

All Articles