Archivos de orden

El archivo de orden es una técnica reciente de optimización del vinculador. Es un archivo de texto que contiene símbolos que representan funciones. Los vinculadores, como lld, usan archivos de orden para diseñar funciones en un orden específico. Estos objetos binarios o bibliotecas con símbolos ordenados reducen las fallas de las páginas. Además, gracias a la carga eficiente de símbolos durante el inicio en frío, mejoran el tiempo de inicio de los programas.

Para agregar las funciones del archivo de orden a tu aplicación, sigue estos tres pasos:

  1. Genera perfiles y un archivo de asignación.
  2. Crea un archivo de orden a partir de los perfiles y el archivo de asignación.
  3. Usa el archivo de orden durante la compilación de lanzamiento para diseñar los símbolos.

Cómo generar un archivo de orden

Para generar un archivo de orden, debes seguir tres pasos:

  1. Compila una versión instrumentada de la app que escribe el archivo de orden.
  2. Ejecuta la app para generar los perfiles.
  3. Procesa posteriormente los perfiles y el archivo de asignación.

Crea una compilación instrumentada

Los perfiles se generan ejecutando una compilación instrumentada de la aplicación. Una compilación instrumentada requiere que se agregue -forder-file-instrumentation a las marcas del compilador y del vinculador, y que -mllvm -orderfile-write-mapping=<filename>-mapping.txt se agregue estrictamente a las marcas del compilador. La marca de instrumentación habilita la instrumentación del archivo de orden para la generación de perfiles y carga la biblioteca específica necesaria para la generación de perfiles. Por otro lado, la marca de asignación solo genera el archivo de asignación que muestra el hash MD5 para cada función dentro del objeto binario o la biblioteca.

Además, asegúrate de pasar todas las marcas de optimización excepto -O0, ya que la marca de instrumentación y la de asignación requieren una. Si no se pasa una marca de optimización, no se genera el archivo de asignación y la compilación instrumentada puede generar hashes incorrectos en el archivo de perfil.

ndk-build

Asegúrate de compilar con APP_OPTIM=release para que ndk-build use un modo de optimización distinto de -O0. Cuando compilas con AGP, esto se hace automáticamente para las compilaciones de lanzamiento.

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

LOCAL_LDFLAGS += -forder-file-instrumentation

CMake

Asegúrate de usar un CMAKE_BUILD_TYPE distinto de Debug para que CMake use un modo de optimización distinto de -O0. Cuando compilas con AGP, esto se hace automáticamente para las compilaciones de lanzamiento.

target_compile_options(orderfiledemo PRIVATE
    -forder-file-instrumentation
    -mllvm -orderfile-write-mapping=mapping.txt
)
target_link_options(orderfiledemo PRIVATE -forder-file-instrumentation)

Otros sistemas de compilación

Compila tu código con -forder-file-instrumentation -O1 -mllvm -orderfile-write-mapping=mapping.txt.

Específicamente, -O1 no es obligatorio, pero no uses -O0.

Omite -mllvm -orderfile-write-mapping=mapping.txt durante la vinculación.

Todas estas marcas no son necesarias para una compilación de lanzamiento, por lo que debe controlarse con una variable de compilación. Para simplificar, puedes configurar todo esto en el archivo CMakeLists.txt como en nuestro ejemplo.

Crea una biblioteca de archivos de orden

Además de las marcas, se debe configurar el archivo de perfil, y el objeto binario instrumentado debe activar de manera explícita una escritura de perfil durante su ejecución.

  • Llama a __llvm_profile_set_filename(PROFILE_DIR "/<filename>-%m.profraw") para configurar la ruta de acceso del perfil. Aunque el argumento que se pasa es <filename>-%m.profraw, el archivo de perfil se guarda como <filename>-%m.profraw.order. Asegúrate de que la app pueda escribir en PROFILE_DIR y de que tengas acceso al directorio.
    • Debido a que se generan perfiles de muchas bibliotecas compartidas, %m es útil porque se expande a una firma de módulo única para la biblioteca, lo que genera un perfil independiente por biblioteca. Para obtener más especificadores de patrones, consulta este vínculo.
  • Llama a __llvm_profile_initialize_file() para configurar el archivo de perfil.
  • Llama a __llvm_orderfile_dump() para escribir en el archivo de perfil.

Los perfiles se recopilan en la memoria y la función de volcado los escribe en el archivo. Debes asegurarte de que se llame a la función de volcado al final del inicio para que tu archivo de perfil tenga todos los símbolos hasta el final del inicio.

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;
}

Ejecuta la compilación para perfiles

Ejecuta la app instrumentada en un dispositivo físico o virtual para generar los perfiles. Puedes extraer los archivos 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 se mencionó antes, asegúrate de poder acceder a la carpeta que contiene el archivo de perfil escrito. Si se trata de un dispositivo virtual, te recomendamos evitar los emuladores con Play Store porque no tienes acceso a muchas carpetas.

Procesa posteriormente el perfil y el archivo de asignación

Cuando obtengas los perfiles, deberás buscar el archivo de asignación y convertir cada perfil en un formato hexadecimal. Por lo general, puedes encontrar el archivo de asignación en la carpeta de compilación de la app. Cuando tengas ambos, puedes usar nuestra secuencia de comandos para tomar un archivo de perfil y el archivo de asignación correcto para generar un archivo de orden.

Linux, Mac y 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

Si deseas obtener más información sobre la secuencia de comandos, puedes consultar este readme.

Cómo usar el archivo de orden para compilar la aplicación

Después de generar un archivo de orden, debes quitar las marcas anteriores y las funciones del archivo de orden, ya que solo están previstas para los pasos de generación. Solo debes pasar -Wl,--symbol-ordering-file=<filename>.orderfile a las marcas de compilación y del vinculador. A veces, los símbolos no se pueden encontrar o no se pueden mover y dar advertencias para que puedas pasar -Wl,--no-warn-symbol-ordering para suprimir estas advertencias.

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
)

Otros sistemas de compilación

Compila tu código con -Wl,--symbol-ordering-file=<filename>.orderfile -Wl,--no-warn-symbol-ordering.

Para obtener más información, consulta el ejemplo de archivo de orden.

Detalles de la implementación de archivos de orden

Existen muchas formas de generar archivos de orden y usarlos para la compilación. El NDK usa el método de LLVM, por lo que es más útil para las bibliotecas compartidas de C o C++ que para la app real de Java o Kotlin. Clang toma cada nombre de función (símbolo) y crea un hash MD5, y genera esta relación en un archivo de asignación. El hash MD5 de una función se escribe en el archivo de perfil (formato profraw) cuando la función se ejecuta por primera vez. Las ejecuciones posteriores de la función no escriben su hash MD5 en el archivo de perfil porque desea evitar duplicados. Como resultado, en el orden, solo se registra la primera ejecución de la función. Si recorres el archivo de perfil y el archivo de asignación, puedes tomar cada hash MD5, reemplazarlo por la función correspondiente y obtener un archivo de orden.

Puedes encontrar ejemplos de un archivo de perfil en formato hexadecimal y un archivo de asignación como example.prof y example-mapping.txt, respectivamente.