Ordina file

Il file degli ordini è una tecnica di ottimizzazione recente del linker. Questi file di ordine sono file di testo contenenti simboli che rappresentano funzioni. I linker come lld utilizzano i file di ordine per strutturare le funzioni in un ordine specifico. Questi programmi binari o librerie con simboli ordinati riducono gli errori delle pagine e migliorano il tempo di avvio di un programma grazie al caricamento efficiente dei simboli durante l'avvio a freddo di un programma.

Le funzionalità dei file di ordine possono essere aggiunte alla tua applicazione seguendo tre passaggi:

  1. Genera profili e file di mappatura
  2. Crea un file di ordine dai profili e dal file di mappatura
  3. Utilizza il file dell'ordine durante la build della release per definire il layout dei simboli

Genera file di ordine

La generazione di un file dell'ordine richiede tre passaggi:

  1. Crea una versione con strumentazione dell'app che scrive il file dell'ordine
  2. Esegui l'app per generare i profili
  3. Post-elaborazione dei profili e del file di mappatura

Crea una build strumentale

I profili vengono generati eseguendo una build dell'applicazione strumentata. Una build con strumentazione richiede l'aggiunta di -forder-file-instrumentation ai flag del compilatore e del linker, mentre -mllvm -orderfile-write-mapping=<filename>-mapping.txt viene aggiunto rigorosamente ai flag del compilatore. Il flag di strumentazione consente la strumentazione dei file di ordine per la profilazione e carica la libreria specifica necessaria per la profilazione. D'altra parte, il flag di mappatura restituisce solo il file di mapping che mostra l'hash MD5 per ogni funzione all'interno del programma binario o della libreria.

Inoltre, assicurati di passare qualsiasi flag di ottimizzazione, ma -O0 perché sia il flag di strumentazione sia quello di mappatura ne richiedono uno. Se non viene passato alcun flag di ottimizzazione, il file di mapping non viene generato e la build instrumentata potrebbe generare hash errati nel file del profilo.

build-ndk

Assicurati di creare con APP_OPTIM=release, in modo che ndk-build utilizzi una modalità di ottimizzazione diversa da -O0. Quando crei con AGP, questa funzionalità è automatica per le build di release.

LOCAL_CFLAGS += \
    -forder-file-instrumentation \
    -mllvm -orderfile-write-mapping=mapping.txt \

LOCAL_LDFLAGS += -forder-file-instrumentation

Marca

Assicurati di utilizzare CMAKE_BUILD_TYPE diverso da Debug, quindi CMake utilizza una modalità di ottimizzazione diversa da -O0. Quando crei con AGP, questo è automatico 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 è obbligatorio, 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 dovrebbe essere controllata da una variabile di build. Per semplicità, puoi configurare tutto questo in CMakeLists.txt come nel nostro esempio.

Creare una libreria di file degli ordini

Oltre ai flag, è necessario configurare il file del profilo e il programma binario strumentato deve attivare esplicitamente la scrittura del profilo durante l'esecuzione.

  • Chiama __llvm_profile_set_filename(PROFILE_DIR "/<filename>-%m.profraw") per configurare il percorso del profilo. Anche se l'argomento passato è <filename>-%m.profraw, il file del profilo viene salvato come <filename>-%m.profraw.order. Assicurati che l'app possa scrivere PROFILE_DIR e di avere accesso alla directory.
    • A causa della profilazione di molte librerie condivise, %m è utile perché si espande in una firma univoca del modulo per la libreria, creando un profilo separato per ogni libreria. Per altri identificatori di pattern, visita questo link.
  • Chiama __llvm_profile_initialize_file() per configurare il file del profilo
  • Chiama __llvm_orderfile_dump() per scrivere esplicitamente nel file del profilo

I profili vengono raccolti in memoria e la funzione di dump li scrive nel file. Assicurati che la funzione dump venga richiamata alla fine dell'avvio, in modo che il file del profilo abbia 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 del 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 che tu possa accedere alla cartella contenente il file del profilo scritto. Se si tratta di un dispositivo virtuale, ti consigliamo di evitare gli emulatori con Play Store perché non hai accesso a molte cartelle.

Post-elaborare il profilo e il file di mappatura

Una volta ottenuti i profili, devi individuare il file di mappatura e convertire ogni profilo in formato esadecimale. In genere, puoi trovare il file di mapping nella cartella di build dell'app. Se hai entrambi, puoi utilizzare il nostro script per acquisire un file di profilo e il file di mapping corretto per generare un file di ordine.

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

Per ulteriori informazioni sullo script, consulta questo README.

Utilizza il file dell'ordine per creare l'applicazione

Dopo aver generato un file di ordine, devi rimuovere i flag precedenti e le funzioni del file di ordine, perché sono solo per i passaggi di generazione. Devi solo passare -Wl,--symbol-ordering-file=<filename>.orderfile ai flag di compilazione e linker. A volte, è impossibile trovare o spostare i simboli. È possibile che vengano visualizzati avvisi in modo da poter passare -Wl,--no-warn-symbol-ordering per eliminare questi avvisi.

build-ndk

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 \

Marca

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 del file di ordine.

Dettagli implementazione file ordine

Esistono molti modi per generare file di ordine e utilizzarli per la creazione. L'NDK utilizza il metodo di LLVM, rendendolo 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) e ne crea un hash MD5 e restituisce questa relazione a un file di mapping. L'hash MD5 di una funzione viene scritto nel file del profilo (formato profraw) quando la funzione viene eseguita per la prima volta. Eventuali esecuzioni successive della funzione non scrivono l'hash MD5 nel file del profilo perché intende evitare duplicati. Di conseguenza, nell'ordine viene registrata solo la prima esecuzione della funzione. Esaminando il file del profilo e di mapping, puoi prendere ogni hash MD5 e sostituirlo con la funzione corrispondente e ottenere un file di ordine.

Esempi di file di profilo in formato esadecimale e file di mapping sono disponibili rispettivamente come example.prof e example-mapping.txt.