O uso de arquivos de ordenação é uma técnica recente para otimização de vinculadores. Esses são arquivos de texto contendo símbolos que representam funções. Vinculadores, como o lld, usam arquivos de ordenação para definir o layout das funções em uma ordem específica. Esses binários ou bibliotecas com símbolos ordenados reduzem as falhas da página e melhoram o tempo de inicialização de um programa devido ao carregamento eficiente de símbolos durante a inicialização a frio.
Os recursos de um arquivo de ordenação podem ser adicionados ao aplicativo seguindo três etapas:
- Gerar perfis e um arquivo de mapeamento
- Criar um arquivo de ordenação usando os perfis e o arquivo de mapeamento
- Usar o arquivo de ordenação durante o build de lançamento para definir o layout dos símbolos
Gerar um arquivo de ordenação
A geração de um arquivo de ordenação é feita em três etapas:
- Criar um build instrumentado do app que grave o arquivo de ordenação
- Executar o app para gerar os perfis
- Fazer o pós-processamento dos perfis e do arquivo de mapeamento
Criar um build instrumentado
Os perfis são gerados executando um build instrumentado do aplicativo.
Um build instrumentado requer a adição de -forder-file-instrumentation
às flags
do compilador e do vinculador, com o
-mllvm -orderfile-write-mapping=<filename>-mapping.txt
sendo adicionado apenas às flags do compilador.
A flag de instrumentação permite instrumentar o arquivo de ordenação para a criação de perfil e
carrega a biblioteca específica necessária para essa criação.
Por outro lado, a flag de mapeamento gera apenas o arquivo de mapeamento que mostra o
hash MD5 de cada função no binário ou na biblioteca.
Além disso, você precisa transmitir todas as flags de otimização, exceto -O0
, porque
esse elemento é necessário para as flags de instrumentação e de mapeamento.
Se nenhuma flag de otimização for transmitida, o arquivo de mapeamento não será gerado, e o
build instrumentado poderá gerar hashes incorretos para o arquivo de perfil.
ndk-build
Crie com APP_OPTIM=release
para que ndk-build use um modo de otimização
diferente de -O0
. Ao criar com o AGP, esse procedimento é automático para builds
de lançamento.
LOCAL_CFLAGS += \
-forder-file-instrumentation \
-mllvm -orderfile-write-mapping=mapping.txt \
LOCAL_LDFLAGS += -forder-file-instrumentation
CMake
Use um CMAKE_BUILD_TYPE
diferente de Debug
para que o CMake utilize um
modo de otimização diferente de -O0
. Ao criar com o AGP, esse procedimento é automático para builds
de lançamento.
target_compile_options(orderfiledemo PRIVATE
-forder-file-instrumentation
-mllvm -orderfile-write-mapping=mapping.txt
)
target_link_options(orderfiledemo PRIVATE -forder-file-instrumentation)
Outros sistemas de build
Compile seu código usando -forder-file-instrumentation -O1 -mllvm
-orderfile-write-mapping=mapping.txt
.
O uso de -O1
não é obrigatório, mas não utilize -O0
.
Omita -mllvm -orderfile-write-mapping=mapping.txt
durante a vinculação.
Todas essas flags não são necessárias para um build de lançamento, então ele precisa ser controlado por uma variante de build. Para simplificar, você pode configurar tudo isso no CMakeLists.txt, como mostrado neste exemplo (link em inglês).
Criar uma biblioteca de arquivos de ordenação
Além das flags, o arquivo de perfil precisa ser configurado, e o binário instrumentado precisa acionar explicitamente uma gravação de perfil durante a execução.
- Chame
__llvm_profile_set_filename(PROFILE_DIR "/<filename>-%m.profraw")
para configurar o caminho do perfil. Embora o argumento transmitido seja<filename>-%m.profraw
, o arquivo de perfil é salvo como<filename>-%m.profraw.order
. O app precisa conseguir gravar noPROFILE_DIR
, e você deve ter acesso ao diretório.- Como muitos perfis de bibliotecas compartilhadas são criados,
%m
é útil porque se expande para uma assinatura de módulo exclusiva para a biblioteca, resultando em um perfil separado por biblioteca. Para consultar mais especificadores de padrão, acesse este link (em inglês).
- Como muitos perfis de bibliotecas compartilhadas são criados,
- Chame
__llvm_profile_initialize_file()
para configurar o arquivo de perfil. - Chame
__llvm_orderfile_dump()
para gravar explicitamente no arquivo de perfil.
Os perfis são coletados na memória, e a função de despejo os grava no arquivo. Você precisa garantir que a função de despejo seja chamada no final da inicialização para que o arquivo de perfil tenha todos os símbolos até o final desse processo.
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;
}
Executar o build para perfis
Execute o app instrumentado em um dispositivo físico ou virtual para gerar os
perfis.
Você pode extrair os arquivos de perfil usando 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 .
Como mencionado, confira se você tem acesso à pasta que contém o arquivo de perfil gravado. Se você estiver usando um dispositivo virtual, recomendamos que evite os emuladores com a Play Store, porque eles não têm acesso a muitas pastas.
Pós-processar o arquivo de perfil e de mapeamento
Ao receber os perfis, você precisa encontrar o arquivo de mapeamento e converter cada perfil em um formato hexadecimal. Normalmente, o arquivo de mapeamento fica na pasta de build do app. Quando você tiver os dois, poderá usar nosso script para pegar um arquivo de perfil e o de mapeamento correto e gerar um arquivo de ordenação.
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
Para saber mais sobre o script, consulte este arquivo README.
Usar o arquivo de ordenação para criar um aplicativo
Depois de gerar um arquivo de ordenação, remova as flags anteriores e as
funções dele, já que elas são destinadas apenas a etapas de geração.
Você só precisa transmitir -Wl,--symbol-ordering-file=<filename>.orderfile
às
flags de compilação e do vinculador.
Às vezes, os símbolos não podem ser encontrados ou não podem ser movidos e emitem avisos para que você
possa transmitir -Wl,--no-warn-symbol-ordering
para suprimir esses avisos.
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
)
Outros sistemas de build
Compile seu código usando -Wl,--symbol-ordering-file=<filename>.orderfile
-Wl,--no-warn-symbol-ordering
.
Para mais informações, confira o exemplo de arquivo de ordenação (link em inglês).
Detalhes de implementação de arquivos de ordenação
Há muitas maneiras de gerar e usar arquivos de ordenação. O NDK usa o método do LLVM para que ele seja o mais útil para suas bibliotecas compartilhadas C ou C++ com um app Java ou Kotlin real. O Clang usa cada nome de função (símbolo) e cria um hash MD5 correspondente, gerando essa relação em um arquivo de mapeamento. O hash MD5 de uma função é gravado no arquivo de perfil (formato .profraw) quando a função é executada pela primeira vez. As execuções subsequentes não gravam o hash MD5 no arquivo de perfil para evitar cópias. Como resultado, apenas a primeira execução da função é registrada na ordem. Ao analisar o arquivo de perfil e o de mapeamento, você pode trocar cada hash MD5 pela função correspondente e gerar um arquivo de ordenação.
Os exemplos de um arquivo de perfil em formato hexadecimal e um de mapeamento podem ser encontrados como example.prof e example-mapping.txt, respectivamente.