Il file degli ordini è una tecnica di ottimizzazione del linker recente. Questi file degli ordini sono file di testo che contengono simboli che rappresentano le funzioni. I linker come lld utilizzano i file degli ordini per disporre le funzioni in un ordine specifico. Questi file binari o librerie con simboli ordinati riducono gli errori di pagina e migliorano il tempo di avvio di un programma grazie al caricamento efficiente dei simboli durante l'avvio a freddo di un programma.
Puoi aggiungere le funzionalità del file degli ordini alla tua applicazione seguendo questi tre passaggi:
- Genera profili e file di mappatura
- Crea un file degli ordini dai profili e dal file di mappatura
- Utilizza il file degli ordini durante la build di release per disporre i simboli
Genera file degli ordini
Per generare un file degli ordini sono necessari tre passaggi:
- Crea una versione instrumentata dell'app che scrive il file degli ordini
- Esegui l'app per generare i profili
- Post-elabora i profili e il file di mappatura
Crea una build instrumentata
I profili vengono generati eseguendo una build instrumentata dell'applicazione.
Una build instrumentata richiede l'aggiunta di -forder-file-instrumentation ai flag del
compilatore e del linker, con
-mllvm -orderfile-write-mapping=<filename>-mapping.txt
aggiunto rigorosamente ai flag del compilatore.
Il flag di instrumentazione abilita l'instrumentazione del file degli ordini per la profilazione e carica la libreria specifica necessaria per la profilazione.
D'altra parte, il flag di mappatura restituisce solo il file di mappatura che mostra l'hash MD5 per ogni funzione all'interno del file binario o della libreria.
Inoltre, assicurati di passare qualsiasi flag di ottimizzazione, ma non -O0, perché entrambi i flag di instrumentazione e di mappatura ne richiedono uno.
Se non viene passato alcun flag di ottimizzazione, il file di mappatura non viene generato e la build instrumentata potrebbe restituire hash errati nel file di profilo.
ndk-build
Assicurati di eseguire la build con APP_OPTIM=release in modo che ndk-build utilizzi una modalità di ottimizzazione diversa da -O0. Quando esegui la build con AGP, questa operazione è automatica per le build di release.
LOCAL_CFLAGS += \
-forder-file-instrumentation \
-mllvm -orderfile-write-mapping=mapping.txt \
LOCAL_LDFLAGS += -forder-file-instrumentation
CMake
Assicurati di utilizzare un CMAKE_BUILD_TYPE diverso da Debug in modo che CMake utilizzi una modalità di ottimizzazione diversa da -O0. Quando esegui la build con AGP, questa operazione è automatica per le build di release.
target_compile_options(orderfiledemo PRIVATE
-forder-file-instrumentation
-mllvm -orderfile-write-mapping=mapping.txt
)
target_link_options(orderfiledemo PRIVATE -forder-file-instrumentation)
Altri sistemi di build
Compila il codice utilizzando -forder-file-instrumentation -O1 -mllvm
-orderfile-write-mapping=mapping.txt.
-O1 non è specificamente richiesto, ma non utilizzare -O0.
Ometti -mllvm -orderfile-write-mapping=mapping.txt durante il collegamento.
Tutti questi flag non sono necessari per una build di release, quindi devono essere controllati da una variabile di build. Per semplificare, puoi configurare tutto in CMakeLists.txt come nel nostro esempio.
Crea una libreria di file degli ordini
Oltre ai flag, è necessario configurare il file di profilo e il file binario instrumentato deve attivare esplicitamente una scrittura del profilo durante l'esecuzione.
- Chiama
__llvm_profile_set_filename(PROFILE_DIR "/<filename>-%m.profraw")per configurare il percorso del profilo. Sebbene l'argomento passato sia<filename>-%m.profraw, il file di profilo viene salvato come<filename>-%m.profraw.order. Assicurati chePROFILE_DIRsia scrivibile dall'app e di avere accesso alla directory.- Poiché vengono profilate molte librerie condivise,
%mè utile perché si espande in una firma di modulo univoca per la libreria, generando un profilo separato per ogni libreria. Per altri identificatori di pattern, puoi consultare questo link.
- Poiché vengono profilate molte librerie condivise,
- Chiama
__llvm_profile_initialize_file()per configurare il file di profilo - Chiama
__llvm_orderfile_dump()per scrivere esplicitamente nel file di profilo
I profili vengono raccolti in memoria e la funzione di dump li scrive nel file. Devi assicurarti che la funzione di dump venga chiamata alla fine dell'avvio in modo che il file di profilo contenga tutti i simboli fino alla fine dell'avvio.
extern "C" {
extern int __llvm_profile_set_filename(const char*);
extern int __llvm_profile_initialize_file(void);
extern int __llvm_orderfile_dump(void);
}
#define PROFILE_DIR "<location-writable-from-app>"
void workload() {
// ...
// run workload
// ...
// set path and write profiles after workload execution
__llvm_profile_set_filename(PROFILE_DIR "/default-%m.profraw");
__llvm_profile_initialize_file();
__llvm_orderfile_dump();
return;
}
Esegui la build per i profili
Esegui l'app instrumentata su un dispositivo fisico o virtuale per generare i profili.
Puoi estrarre i file di profilo utilizzando adb pull.
adb shell "run-as <package-name> sh -c 'cat /data/user/0/<package-name>/cache/default-%m.profraw.order' | cat > /data/local/tmp/default-%m.profraw.order"
adb pull /data/local/tmp/default-%m.profraw.order .
Come accennato in precedenza, assicurati di poter accedere alla cartella contenente il file di profilo scritto. Se si tratta di un dispositivo virtuale, potresti voler evitare gli emulatori con Play Store perché non hai accesso a molte cartelle.
Post-elabora il file di profilo e di mappatura
Quando ricevi i profili, devi trovare il file di mappatura e convertire ogni profilo in formato esadecimale. In genere, puoi trovare il file di mappatura nella cartella di build dell'app. Quando hai entrambi, puoi utilizzare il nostro script per prendere un file di profilo e il file di mappatura corretto per generare un file degli ordini.
Linux/Mac/ChromeOS
hexdump -C default-%m.profraw.order > default-%m.prof
python3 create_orderfile.py --profile-file default-%m.prof --mapping-file <filename>-mapping.txt
Windows
certutil -f -encodeHex default-%m.profraw.order default-%m.prof
python3 create_orderfile.py --profile-file default-%m.prof --mapping-file <filename>-mapping.txt
Se vuoi saperne di più sullo script, puoi consultare questo README.
Utilizza il file degli ordini per creare l'applicazione
Dopo aver generato un file degli ordini, devi rimuovere i flag precedenti e le funzioni del file degli ordini perché sono destinati solo ai passaggi di generazione.
Devi solo passare -Wl,--symbol-ordering-file=<filename>.orderfile ai
flag del compilatore e del linker.
A volte, i simboli non possono essere trovati o spostati e generano avvisi, quindi puoi passare -Wl,--no-warn-symbol-ordering per sopprimere questi avvisi.
ndk-build
LOCAL_CFLAGS += \
-Wl,--symbol-ordering-file=<filename>.orderfile \
-Wl,--no-warn-symbol-ordering \
LOCAL_LDFLAGS += \
-Wl,--symbol-ordering-file=<filename>.orderfile \
-Wl,--no-warn-symbol-ordering \
CMake
target_compile_options(orderfiledemo PRIVATE
-Wl,--symbol-ordering-file=<filename>.orderfile
-Wl,--no-warn-symbol-ordering
)
target_link_options(orderfiledemo PRIVATE
-Wl,--symbol-ordering-file=<filename>.orderfile
-Wl,--no-warn-symbol-ordering
)
Altri sistemi di build
Compila il codice utilizzando -Wl,--symbol-ordering-file=<filename>.orderfile
-Wl,--no-warn-symbol-ordering.
Per ulteriori informazioni, consulta l'esempio di file degli ordini.
Dettagli sull'implementazione del file degli ordini
Esistono molti modi per generare file degli ordini e utilizzarli per la build. L'NDK utilizza il metodo di LLVM, quindi è il più utile per le librerie condivise C o C++ rispetto all'app Java o Kotlin effettiva. Clang prende ogni nome di funzione (simbolo), ne crea un hash MD5 e restituisce questa relazione a un file di mappatura. L'hash MD5 di una funzione viene scritto nel file di profilo (formato profraw) quando la funzione viene eseguita per la prima volta. Le esecuzioni successive della funzione non scrivono il relativo hash MD5 nel file di profilo perché vogliono evitare i duplicati. Di conseguenza, nell'ordine viene registrata solo la prima esecuzione della funzione. Esaminando il file di profilo e il file di mappatura, puoi prendere ogni hash MD5 e sostituirlo con la funzione corrispondente per ottenere un file degli ordini.
Esempi di file di profilo in formato esadecimale e di file di mappatura sono disponibili rispettivamente come example.prof e example-mapping.txt.