Pratique d'optimisation de la taille du paquet iOS Baidu APP 50M (6) Méthode de nettoyage inutile

Introduction

La taille du package Baidu APP a été considérablement réduite après une phase d'optimisation, telle que le nettoyage des ressources inutiles, la mise hors ligne des classes inutiles et les optimisations liées à la compilation Xcode. Cependant, après optimisation, la taille du package APP occupe toujours 350 Mo d'espace sur iPhone11. Dans le même temps, Baidu APP, en tant qu'application phare de Baidu, comporte de nombreuses et rapides itérations commerciales. L'optimisation des volumes et la prévention de la détérioration restent une tâche essentielle au stade actuel. Par conséquent, Baidu APP a commencé les travaux liés au nettoyage des méthodes inutiles avec une granularité plus petite et un risque de réparation plus élevé. On s'attend à ce que grâce au nettoyage des méthodes inutiles, la taille du paquet de Baidu APP soit effectivement réduite, tandis que les méthodes inutiles et le code redondant du projet seront supprimés et que la propreté du code sera améliorée.

Examen de la série d'articles sur les pratiques d'optimisation du volume des packages iOS de l'application Baidu :

2. Planifier la recherche

Pour nettoyer les méthodes inutiles, nous avons étudié les solutions annoncées actuellement par différents fabricants. La solution dominante est basée sur l'analyse des fichiers Mach-O + LinkMap, mais elle présente principalement les problèmes suivants :

1. Faible précision

2. Un filtrage manuel est requis pour les méthodes système

3. Appels non reconnus liés au chargement, à l'initialisation et à l'attribut

4. L'appel de réflexion de chaîne ne peut pas être reconnu, l'enregistrement de l'action cible, la méthode d'enregistrement de l'observateur, etc. ne peuvent pas être reconnus.

5. Incapable de reconnaître des scénarios de syntaxe complexes, tels que des appels de méthodes dans des chaînes d'héritage, des sous-classes implémentant des méthodes de classe parent, etc.

6. Notifications système et autres scénarios

Étant donné que les plans actuellement annoncés présentent les inconvénients ci-dessus et que la sensibilité du code hors ligne est très élevée, les entreprises concernées sont très prudentes. Par conséquent, il est très important de promouvoir le nettoyage des méthodes inutiles pertinentes et la précision de l'identification est directement liée à l'enthousiasme des entreprises concernées pour le code inutile hors ligne, de sorte que la solution ci-dessus est abandonnée.

3. Sélection des forfaits

En analysant les lacunes de la deuxième partie du plan, nous pouvons constater que le problème central de sa faible précision est que lors de l'analyse du produit, nous ne pouvons pas obtenir toutes les informations requises, ou nous n'avons pas trouvé de moyens efficaces pour obtenir ce que nous attendons. information. La meilleure façon de résoudre les problèmes mentionnés ci-dessus est d’obtenir autant d’informations de code que possible. Puisque nous ne pouvons pas retracer ce dont nous avons besoin à partir du produit, nous pouvons envisager de trouver les informations détaillées dont nous avons besoin à partir de la source, c'est-à-dire au niveau du code source.

Le code source contient certes toutes les informations, mais comment analyser le code source ? Il en existe trois types principaux :

  • Analysez le code source directement via des scripts

Toutes les règles grammaticales du code source doivent être respectées pour pouvoir analyser efficacement le code source, ce qui équivaut à écrire un analyseur de code source, ce plan a donc été abandonné.

  • Analysez l'AST (Abstract Syntax Tree) directement via des scripts

L'arbre de syntaxe abstraite (AST) généré lors du processus de compilation contient toutes les informations requises, et clang fournit également une ligne de commande, qui peut être utilisée pour obtenir directement les données AST. Cependant, la commande clang pour obtenir les données AST prend une seule classe comme dimension et la relation entre les classes est difficile à obtenir. Par exemple, la relation d'héritage, la relation entre la classification et la classe principale ne peut pas être obtenue, donc cette solution a également été abandonnée.

  • Analysez AST via libtooling et le kit de compilation auto-construit de Swift Compiler (les éléments liés à Swift seront présentés dans le prochain article)

L'analyse du produit AST générée par la commande clang ne pouvant toujours pas répondre aux besoins, nous intervenons directement dans le processus de compilation et obtenons les informations requises à partir du processus de génération AST interne de la compilation. Finalement, cette solution a été adoptée. Utilisez libtooling et la suite de compilation auto-construite de Swift Compiler pour analyser l'AST et obtenir toutes les informations requises.

4. Conception du schéma

Comme mentionné ci-dessus, Baidu APP a finalement adopté les solutions d'analyse statique libtooling et Swift Compiler, donc ce qui suit les développera à partir des niveaux de principe et de mise en œuvre.

4.1 Introduction au processus de compilation

4.1.1 Structure globale de la compilation Xcode

Dans cette section, nous parlons d'abord brièvement de la structure du compilateur, du processus de compilation et de ce qu'est l'analyse statique ?

image

△Figure 4-1

Comme le montre la figure 4-1, LLVM adopte la structure en trois phases ci-dessus (Three Phase Design), qui sont le frontal de compilation (Frontend), le module d'optimisation de la compilation et le back-end du compilateur (Backend). Alors, comment ces trois structures correspondent-elles à Xcode, comme le montre la figure 4-2 :

image

△Figure 4-2

Lors de la compilation quotidienne avec Xcode, Xcode appelle deux frontaux de compilateur, à savoir Clang et Swift, pour créer un produit de compilation commun via les deux frontaux de compilateur, puis générer uniformément des fichiers cibles via le compilateur back-end LLVM.

Grâce au journal de compilation Xcode, vous pouvez voir que clang est utilisé pour la compilation pour Objective-C, C et C++, et que différents paramètres de compilation sont utilisés pour contrôler les trois langages différents ci-dessus :

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang

Pour les fichiers Swift, le compilateur Swift est utilisé pour la compilation :

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-frontend

Pour ces deux fichiers exécutables, vous pouvez décompresser Xcode vous-même et effectuer des appels en ligne de commande, ou vous pouvez utiliser la commande --help pour vérifier quels paramètres ou fonctions de compilation ils prennent en charge. Le compilateur interne de Xcode est en fait la version personnalisée par Apple des versions open source de LLVM et Swift, qui présente certaines différences par rapport aux versions open source.

4.1.2 Processus de compilation Clang et Swift

Comme le montre la figure ci-dessous, le processus de compilation frontale Clang et Swift, vous pouvez voir que le processus de compilation Swift comporte une partie SIL supplémentaire. En fait, il existe également une transformation garantie SIL. Bien sûr, la partie SIL n'est pas la mise au point. À partir de la figure 4-3, vous pouvez voir que les compilateurs Clang et Swift généreront AST et constateront que l'AST contient la plupart des informations dont nous avons besoin, et que Clang et Swift Compiler exposent également les interfaces pertinentes pour obtenir des informations AST, le travail restant est donc Seulement quatre points :

1. Construisez le projet de package de compilation et assurez-vous qu'il s'exécute normalement

2. Obtenez l'AST et obtenez les données requises en fonction des fonctionnalités syntaxiques d'Objective-C ou C ou C++

3. Effectuer une analyse commerciale et un traitement des données acquises

4. Il existe certaines différences entre la version open source LLVM et la version réelle utilisée par Xcode, donc certains contenus liés à la compilation doivent être adaptés.

image

△Figure 4-3

4.2 Conception globale du système

Pour l'utilisation d'un langage de programmation, comme le montre la figure 4-4, il comprend deux niveaux, un niveau est la déclaration et l'autre niveau est l'appel. Déclarez les classes, protocoles, propriétés, méthodes, fonctions, etc. Le contenu déclaré en même temps est à usage, donc le même contenu déclaré peut être appelé. C'est juste une question d'appel interne ou d'appel public. D'un point de vue technique, tout le contenu déclaré moins le contenu déclaré appelé est ce qui reste, c'est le contenu non appelé, ce qui est la méthode inutile dont nous avons besoin. Bien entendu, le jugement technique nécessite en fin de compte un jugement commercial, car certaines sont des capacités de base fournies au monde extérieur. Quant à savoir s'il convient de les supprimer, une discussion plus approfondie est nécessaire. Cet article traite principalement de problèmes techniques.

image

△Figure 4-4

À partir du code source de Clang, nous pouvons savoir que la déclaration et l'appel correspondent respectivement aux classes de base Decl et Expr dans le code source de LLVM. La solution technique globale est présentée dans la figure 4-5 ci-dessous. Les méthodes inutiles sont divisées en quatre couches :

1.Couche de base : assembler les paramètres de compilation requis par l'outil de compilation + effectuer la correspondance des règles de syntaxe

2. Couche Transformer : convertir les règles de syntaxe de correspondance des données et convertir les formats de données généraux

3. Couche de données générales : Les données générées via la couche Transformer sont stockées dans des catégories. Les données stockées incluent toutes les données du code, telles que les attributs, les méthodes, les protocoles et autres données.

4. Couche d'application métier : L'analyse métier peut être effectuée sur les données stockées générées par la couche de données générale

image

△Figure 4-5

4.3 Mise en œuvre détaillée de la solution

4.3.1 Construction de l'outil de compilation Objective-C

L'outil de compilation se présente sous la forme d'un fichier exécutable similaire au clang fourni avec Xcode, comme le montre l'encadré rouge de la figure 4-6.

/Users/UserName/Documents/XcodeEdition/Xcode14.2/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang

image

△Figure 4-6

En termes simples, l'outil de compilation construit à partir du code source possède certaines fonctions de Xcode clang. Il utilise l'objet AST généré pendant le processus de compilation pour effectuer une analyse de l'arbre syntaxique abstrait et obtenir toutes les informations syntaxiques du langage de programmation requis.

4.3.1.1 Construction du code source LLVM

La construction des outils de compilation nécessite les bibliothèques statiques ou dynamiques fournies par LLVM. Ces bibliothèques sont obtenues en construisant vous-même le code source de LLVM. Vous pouvez obtenir le chemin du code source LLVM à partir de github. Après avoir entré l'interface LLVM github, vous pourriez ne pas savoir quelle branche ou quel code de balise doit être construit. Quelle version correspond au clang utilisé par Xcode ? La version actuelle de Xcode est 14.2 ou 14.3. Utilisez la commande clang --version pour voir que Xcode utilise clang 14, donc release/14.x est construit (aucune relation correspondante n'est trouvée, déduite) et la construction est exécutée après le la construction est réussie. clang --version constatera que le clang de la version open source et le numéro de version mineur de Xcode sont différents. En effet, le clang utilisé par Xcode est personnalisé par Apple en fonction du code open source, qui dépend du numéro de bibliothèques dépendantes ou de fichiers d'en-tête de clang dans Xcode. De plus, vous pouvez également voir dans le journal de compilation que certains paramètres pris en charge par le clang Xcode ne sont pas pris en charge par le clang open source. Bien qu'Apple propose certaines personnalisations, l'impact global est limité. Par conséquent, vous n’avez pas trop à vous soucier de la cohérence des numéros de versions mineures. (Une vérification préliminaire est qu'il est également possible de construire la dernière version/16.x clang16).

image

△Figure 4-7 _

Il existe deux principaux types de commandes de construction spécifiques, l'une est la méthode de construction Ninja et l'autre la méthode Xcode. Si vous avez besoin de Xcode pour déboguer le code source, vous pouvez choisir le mode Xcode. Cependant, la bibliothèque statique qui est finalement intégrés à l'outil de compilation doivent être construits en mode Release, afin que la taille de l'outil soit plus petite. Au minimum, certaines exceptions d'avertissement seront également bloquées. Vous pouvez vous référer au processus de construction du guide de démarrage dans la bibliothèque open source LLVM pour construire. Les commandes d'assemblage impliquées peuvent être épissées par vous-même ou vous pouvez utiliser les commandes suivantes :

构建过程
git clone https://github.com/llvm/llvm-project.git
cd llvm-project
mkdir build (这个build文件夹可以自行命名,不固定。针对不同目标可以创建不同文件夹进行不同构建,如 mkdir ninjaBuild 或 mkdir xcodeBuild)
cd build (or cd xcodeBuild)
cmake -G "Ninja" -DCMAKE_BUILD_TYPE=Release ../llvm
cmake --build .

Compilez la version Xcode et remplacez Ninja par Xcode.

4.3.1.2 Construction du projet

LLVM fournit deux outils, libclang et libtooling. Baidu APP utilise libtooling. Les similitudes et les différences sont les suivantes :

  • libclang : (informations en ligne, non testées)

    1. Fournissez une interface C stable avec la possibilité de parcourir l'arborescence syntaxique, d'obtenir un jeton et de compléter le code.

    2. L'interface est stable et les mises à jour de la version Clang ont peu d'impact sur l'alignement.

    3.libclang ne peut pas obtenir toutes les informations d'AST

  • libtooling : (mesure réelle)

    1. Fournissez une interface C++ et les outils générés ne dépendent pas du compilateur et peuvent être utilisés comme commandes indépendantes.

    2. L'interface est instable et AST doit être mis à niveau si les bibliothèques dépendantes pertinentes sont mises à jour.

    3.libtooling peut obtenir toutes les informations d'AST

Enfin, j'ai choisi la forme libtooling.La raison principale est que libtooling peut obtenir toutes les informations de l'AST et peut s'exécuter indépendamment sans compter sur Xcode. La construction du projet en lui-même n'est pas compliquée, elle appartient toujours au niveau d'utilisation de l'API, vous pouvez directement vous référer à la documentation officielle de libtooling.

image

△Figure 4-8

Le flux global du code est illustré à la figure 4-8. Les principaux points principaux sont constitués de cinq parties :

  • Analyse des paramètres

  • Pour créer ClangTool, reportez-vous au code source LLVM ClangTooling -> Tooling.h Line309

  • Créer ASTFrontendAction, utilisé pour obtenir des données AST, créer ASTFonsumer et lier ASTMatcher

  • Faites correspondre chaque règle de syntaxe avec les éléments correspondants d'ASTMatcher

  • Effectuer le filtrage des données et le traitement commercial en fonction des données correspondantes

4.3.1.3 Conception de la structure de stockage des données

La structure de stockage des données adopte le format json. Voici un exemple de format de données de base, qui peut être étendu en fonction des besoins réels :

"objc(协议or类)@类名(类方法or实例方法)@方法名称":{
"identifier":"objc(协议or类)@类名(类方法or实例方法)@方法名称",
"isInstance":true,
"kind":16,
"location":{
"col":36,
"filename":"文件名称",
"line":147
    },
"name":"方法名称",
"paramters":"参数",
"returnType":"返回值类型",
"sourceCode":"源码"
}
{"declaration":{"identifier":"objc(协议or类)@类名(类方法or实例方法)@方法名称","isInstance":true,"kind":16,"location":{            "col":列数,"filename":"声明所在类名",            "line":行数        },"name":"方法名称","paramters":"参数名称","returnType":"返回值类型","sourceCode":"源代码"    },"kind":1,"location":{"col":5,"filename":"当前所在文件名","line":15    }}

5. Problèmes rencontrés et solutions

1. Problème d'identification des appels d'attributs

Pour les propriétés Objective-C, il existe deux méthodes get et set après compilation. L'une est ivar. L'appelant ne peut appeler que get ou set ou ivar, donc lorsqu'un seul appel se produit, même si cette propriété est appelée, la propriété actuelle n'est pas un méthode inutile. Les deux autres méthodes doivent être supprimées des résultats.

2. Lors de l'extraction du contenu de la méthode, vous devez également extraire le fichier d'en-tête.

L'implémentation des méthodes ne se fait pas nécessairement uniquement dans les fichiers .m. Par exemple, les fichiers d'en-tête C++ peuvent implémenter des méthodes, et les fichiers .h d'Objective-C peuvent implémenter certaines méthodes en ligne, ce qui est également syntaxiquement réalisable. Par conséquent, lors de l'extraction de méthodes, faites attention aux fichiers d'implémentation, ainsi qu'aux fichiers d'en-tête.

3. Résoudre les problèmes d'héritage

Dans des scénarios tels que des sous-classes implémentant des méthodes de classe parent, lors de l'identification des méthodes, toutes les méthodes remontent à leur classe parent et le nom de la classe parent est utilisé comme partie du nom de classe de l'identifiant dans la structure de données ci-dessus, afin que toutes les méthodes puissent correspondre à leur classe déclarée.

4. Filtrer les appels de méthode système

LLVM fournit une interface pour déterminer si la méthode actuelle appartient à une classe système.

5. Problèmes liés au filtrage des méthodes de mise en œuvre du système de classe affaires

Pour toutes les méthodes de la classe actuelle qui sont dans la classe actuelle et qui remontent à la classe parent dans sa chaîne d'héritage, déterminez si elles appartiennent à la méthode système. Si elles appartiennent à la méthode système, elles seront directement filtrées.

6. Pour la mise en œuvre des méthodes protocolaires, il n'existe actuellement aucun moyen efficace de les identifier.La solution actuelle consiste à filtrer directement les méthodes protocolaires, et toutes les méthodes protocolaires sont considérées comme ayant été appelées.

Lors de l'extraction d'une méthode, déterminez les protocoles suivis par l'interface actuelle, parcourez les méthodes du protocole et déterminez s'il s'agit d'une méthode de protocole. Si tel est le cas, marquez-la comme appelée.

7. Problèmes avec les sous-classes implémentant les protocoles de classe parent

Remontez la chaîne d'héritage de la classe actuelle, déterminez les protocoles qu'elle suit dans la chaîne d'héritage et déterminez s'il s'agit d'une méthode de protocole.

8. Lorsqu'une entreprise normale implémente un protocole, il convient d'indiquer clairement que la classe actuelle suit le protocole, comme l'interface <conformprotocol>.Cependant, dans les scénarios réels, de nombreux codes ne sont pas marqués avec conformprotocol lors de la mise en œuvre du protocole. Cela aura un impact sur le jugement de la méthode protocolaire. Par exemple, les solutions 6.7 sont toutes échouées.

S'il y a une petite quantité de ce problème dans un composant, il doit y avoir un protocole clair à suivre pour pousser les parties concernées à le résoudre. Toutefois, s’il existe de nombreux scénarios de ce type pour certains composants et que tous ne seront pas réparés à court terme, une adaptation temporaire est nécessaire. Pour ce type de composant, collectez toutes les méthodes de protocole du composant déclaré par le composant actuel, et utilisez les méthodes de protocole collectées et toutes les déclarations extraites par le composant actuel pour faire la différence. Il existe une possibilité de dommages accidentels, mais le résultat est confiant (le composant n'a qu'une seule dimension et le traitement associé peut être effectué sur ses composants associés, car parfois les composants qu'il implémente ne sont pas nécessairement dans le composant actuel, ce qui nécessite les dépendances du composant actuel).

Il existe de nombreux cas de méthodes inutiles, et certaines d'entre elles sont répertoriées à titre de référence.

6. Résumé

Cette technologie est en fait utilisée dans Baidu APP depuis longtemps, car l'auteur était auparavant responsable de l'examen des modifications de l'interface, de la vérification de l'intégrité des composants, de l'analyse de la chaîne d'appels de conformité à la confidentialité, etc. de Baidu APP, tous s'appuyant sur cette technologie. des méthodes inutiles n'est que le travail de l'auteur. Une extension de sa fonction qui vient à l'esprit lors de l'optimisation du volume. Bien entendu, pour les problèmes techniques décrits ci-dessus, les méthodes inutiles pour traiter les détails sont évidemment plus délicates et comportent plus de cas. Les articles suivants présenteront l'analyse des méthodes inutiles Swift, l'examen des modifications d'interface, la vérification de l'intégrité des composants, l'analyse de la chaîne d'appels de conformité à la confidentialité, etc.

** --FIN--**

Les références:

[1]libclang:https://clang.llvm.org/doxygen/group__CINDEX.html

[2]documentation officielle de libtooling : https://clang.llvm.org/docs/LibTooling.html

[3]Code source de LLVM : https://github.com/llvm/llvm-project

Lecture recommandée:

Stratégie d'interception et de répartition des problèmes en temps réel basée sur des scénarios en ligne anormaux

Planification de lecture parallèle SSD extrêmement optimisée

La pratique de la création et de la publication de textes IA sur l'application Baidu

DeeTune : Conception et application du framework réseau Baidu basé sur eBPF

Moteur de recherche ANN hautes performances auto-développé par Baidu, open source

Amende de 200 yuans et plus d'un million de yuans confisqués You Yuxi : L'importance des documents chinois de haute qualité Le serveur de migration hard-core de Musk Solon pour JDK 21, les fils virtuels sont incroyables ! ! ! Le contrôle de la congestion TCP sauve Internet Flutter pour OpenHarmony est là La période LTS du noyau Linux sera restaurée de 6 ans à 2 ans Go 1.22 corrigera l'erreur de variable de boucle for Svelte a construit une "nouvelle roue" - les runes Google fête son 25e anniversaire
{{o.name}}
{{m.nom}}

Je suppose que tu aimes

Origine my.oschina.net/u/4939618/blog/10112383
conseillé
Classement