Как использовать оптимизацию на основе профиля

Система сборки Android для Android 13 и более ранних версий поддерживает использование оптимизации на основе профиля (PGO) Clang для собственных модулей Android, в которых есть правила сборки blueprint. На этой странице описано, как работает Clang PGO, как создавать и обновлять профили, используемые для PGO, и как интегрировать PGO с системой сборки (с примером использования).

Примечание. В этом документе описывается использование PGO на платформе Android. Чтобы узнать, как использовать PGO в приложении Android, посетите эту страницу.

О Clang PGO

Clang может выполнять оптимизацию на основе профиля, используя два типа профилей:

  • Профили на основе инструментов создаются на основе целевой программы с инструментами. Эти профили подробные и создают большую нагрузку на время выполнения.
  • Профили на основе выборки обычно создаются с помощью счетчиков аппаратного обеспечения. Они не создают большой нагрузки на систему и могут собираться без изменения двоичного кода. Они менее подробны, чем профили, созданные на основе данных об инструментах.

Все профили должны быть созданы на основе типичной рабочей нагрузки, которая отражает обычное поведение приложения. Clang поддерживает профили на основе AST (-fprofile-instr-generate) и LLVM IR (-fprofile-generate)). В Android для PGO на основе инструментов поддерживаются только профили на основе LLVM IR.

Для сборки с целью сбора профилей необходимы следующие флаги:

  • -fprofile-generate для инструментов на основе ИК-излучения. При этом серверная часть использует подход взвешенного минимального остовного дерева, чтобы уменьшить количество точек инструментирования и оптимизировать их размещение на ребрах с малым весом (используйте этот параметр также для шага связывания). Драйвер Clang автоматически передает компоновщику среду выполнения профилирования (libclang_rt.profile-arch-android.a). Эта библиотека содержит процедуры для записи профилей на диск при выходе из программы.
  • -gline-tables-only для сбора профиля на основе выборки, чтобы создать минимальные сведения для отладки.

Профиль можно использовать для PGO с помощью -fprofile-use=pathname или -fprofile-sample-use=pathname для профилей на основе инструментов и выборки соответственно.

Примечание. Если в код вносятся изменения и Clang больше не может использовать данные профиля, он генерирует предупреждение -Wprofile-instr-out-of-date.

Использовать PGO

Использование PGO включает следующие этапы:

  1. Создайте библиотеку или исполняемый файл с инструментарием, передав -fprofile-generate компилятору и компоновщику.
  2. Собирайте профили, выполняя типичную рабочую нагрузку на инструментированном двоичном файле.
  3. Выполните постобработку профилей с помощью утилиты llvm-profdata (подробнее об обработке файлов профилей LLVM…).
  4. Используйте профили, чтобы применить PGO, передав -fprofile-use=<>.profdata компилятору и компоновщику.

Для PGO в Android профили должны собираться в автономном режиме и проверяться вместе с кодом, чтобы обеспечить воспроизводимость сборок. Профили можно использовать по мере развития кода, но их необходимо периодически обновлять (или когда Clang предупреждает, что профили устарели).

Сбор профилей

Clang может использовать профили, собранные при запуске тестов производительности с помощью инструментированной сборки библиотеки или путем выборки аппаратных счетчиков при запуске теста производительности. В настоящее время Android не поддерживает сбор профилей на основе выборки, поэтому вам необходимо собирать профили с помощью инструментированной сборки:

  1. Определите тест производительности и набор библиотек, которые он использует.
  2. Добавьте свойства pgo в контрольную точку и библиотеки (подробности ниже).
  3. Создайте сборку Android с инструментированной копией этих библиотек, используя:
    make ANDROID_PGO_INSTRUMENT=benchmark

benchmark – это плейсхолдер, который идентифицирует набор библиотек, используемых во время сборки. Фактические входные данные представителя (и, возможно, другой исполняемый файл, который ссылается на библиотеку, подвергаемую тестированию) не относятся к PGO и выходят за рамки этого документа.

  1. Установите или синхронизируйте сборку с инструментарием на устройстве.
  2. Запустите тест, чтобы собрать профили.
  3. Используйте инструмент llvm-profdata (описан ниже) для постобработки профилей и подготовки их к проверке в дереве исходного кода.

Использовать профили при сборке

Проверьте профили в toolchain/pgo-profiles в дереве Android. Название должно совпадать с указанным в дочернем свойстве profile_file свойства pgo для библиотеки. Система сборки автоматически передает файл профиля в Clang при сборке библиотеки. Переменную среды ANDROID_PGO_DISABLE_PROFILE_USE можно задать как true, чтобы временно отключить PGO и оценить ее эффективность.

Чтобы указать дополнительные каталоги профилей для определенного продукта, добавьте их в переменную PGO_ADDITIONAL_PROFILE_DIRECTORIES в файле BoardConfig.mk. Если указаны дополнительные пути, профили в этих путях переопределяют профили в toolchain/pgo-profiles.

При создании образа выпуска с использованием цели dist для make система сборки записывает имена отсутствующих файлов профиля в $DIST_DIR/pgo_profile_file_missing.txt. Вы можете проверить этот файл, чтобы узнать, какие файлы профиля были случайно удалены (что незаметно отключает PGO).

Включите PGO в файлах Android.bp

Чтобы включить PGO в файлах Android.bp для нативных модулей, просто укажите свойство pgo. У этого свойства есть следующие вложенные свойства:

Ресурс Описание
instrumentation Установите значение true для PGO с использованием инструментов. Значение по умолчанию – false.
sampling Для оптимизации на основе профиля с использованием выборки задайте значение true. Значение по умолчанию – false.
benchmarks Список строк. Этот модуль предназначен для профилирования, если в параметре сборки ANDROID_PGO_INSTRUMENT указан какой-либо тест из списка.
profile_file Файл профиля (относительно toolchain/pgo-profile), который будет использоваться с PGO. Если файл не существует, сборка предупреждает об этом, добавляя его в $DIST_DIR/pgo_profile_file_missing.txt, если только свойство enable_profile_use не имеет значение false ИЛИ переменная сборки ANDROID_PGO_NO_PROFILE_USE не имеет значение true.
enable_profile_use Установите значение false, если профили не должны использоваться во время сборки. Можно использовать во время начальной загрузки, чтобы включить сбор профилей или временно отключить PGO. Значение по умолчанию – true.
cflags Список дополнительных флагов, которые будут использоваться при сборке с инструментами.

Пример модуля с PGO:

cc_library {
    name: "libexample",
    srcs: [
        "src1.cpp",
        "src2.cpp",
    ],
    static: [
        "libstatic1",
        "libstatic2",
    ],
    shared: [
        "libshared1",
    ]
    pgo: {
        instrumentation: true,
        benchmarks: [
            "benchmark1",
            "benchmark2",
        ],
        profile_file: "example.profdata",
    }
}

Если контрольные показатели benchmark1 и benchmark2 отражают типичное поведение для библиотек libstatic1, libstatic2 или libshared1, то свойство pgo этих библиотек также может включать контрольные показатели. Модуль defaults в Android.bp может включать общую спецификацию pgo для набора библиотек, чтобы не повторять одни и те же правила сборки для нескольких модулей.

Чтобы выбрать другие файлы профиля или выборочно отключить PGO для архитектуры, укажите свойства profile_file, enable_profile_use и cflags для каждой архитектуры. Пример (целевая архитектура выделена жирным шрифтом):

cc_library {
    name: "libexample",
    srcs: [
          "src1.cpp",
          "src2.cpp",
    ],
    static: [
          "libstatic1",
          "libstatic2",
    ],
    shared: [
          "libshared1",
    ],
    pgo: {
         instrumentation: true,
         benchmarks: [
              "benchmark1",
              "benchmark2",
         ],
    }

    target: {
         android_arm: {
              pgo: {
                   profile_file: "example_arm.profdata",
              }
         },
         android_arm64: {
              pgo: {
                   profile_file: "example_arm64.profdata",
              }
         }
    }
}

Чтобы устранить ссылки на библиотеку времени выполнения профилирования во время профилирования на основе инструментов, передайте флаг сборки -fprofile-generate компоновщику. Статические библиотеки, инструментированные с помощью PGO, все общие библиотеки и любые двоичные файлы, которые напрямую зависят от статической библиотеки, также должны быть инструментированы для PGO. Однако для таких общих библиотек или исполняемых файлов не обязательно использовать профили PGO, и их свойство enable_profile_use можно установить в значение false. В остальных случаях PGO можно применять к любой статической библиотеке, общей библиотеке или исполняемому файлу.

Как работать с файлами профиля LLVM

При выполнении библиотеки с инструментарием или исполняемого файла создается файл профиля с именем default_unique_id_0.profraw в /data/local/tmp (где unique_id – это числовой хеш, уникальный для этой библиотеки). Если этот файл уже существует, во время записи профилей среда выполнения профилирования объединяет новый профиль со старым. Обратите внимание, что разработчики приложений не могут получить доступ к /data/local/tmp. Вместо этого они могут использовать, например, /storage/emulated/0/Android/data/packagename/files. Чтобы изменить расположение файла профиля, задайте переменную среды LLVM_PROFILE_FILE во время выполнения.

Затем утилита llvm-profdata используется для преобразования файла .profraw (и, возможно, объединения нескольких файлов .profraw) в файл .profdata:

  llvm-profdata merge -output=profile.profdata <.profraw and/or .profdata files>

profile.profdata, а затем добавить его в дерево исходного кода для использования при сборке.

Если во время тестирования загружается несколько инструментированных двоичных файлов или библиотек, каждая библиотека создает отдельный файл .profraw с уникальным идентификатором. Как правило, все эти файлы можно объединить в один файл .profdata и использовать для создания PGO. Если библиотека используется в другом тесте, ее необходимо оптимизировать с помощью профилей из обоих тестов. В этом случае полезен вариант show llvm-profdata:

  llvm-profdata merge -output=default_unique_id.profdata default_unique_id_0.profraw
llvm-profdata show -all-functions default_unique_id.profdata

Чтобы сопоставить unique_id с отдельными библиотеками, выполните поиск по каждому unique_id в выходных данных show, чтобы найти название функции, уникальное для библиотеки.

Пример использования: PGO для ART

В этом исследовании ART используется как понятный пример, но он не отражает реальный набор библиотек, профилированных для ART, и их взаимозависимости.

Компилятор dex2oat в ART зависит от libart-compiler.so, который, в свою очередь, зависит от libart.so. Среда выполнения ART реализована в основном в файле libart.so. Контрольные показатели для компилятора и среды выполнения будут разными:

Benchmark Профилированные библиотеки
dex2oat dex2oat (исполняемый файл), libart-compiler.so, libart.so
art_runtime libart.so
  1. Добавьте следующее свойство pgo в dex2oat, libart-compiler.so:
        pgo: {
            instrumentation: true,
            benchmarks: ["dex2oat",],
            profile_file: "dex2oat.profdata",
        }
  2. Добавьте в libart.so следующее свойство pgo:
        pgo: {
            instrumentation: true,
            benchmarks: ["art_runtime", "dex2oat",],
            profile_file: "libart.profdata",
        }
  3. Создайте сборки с инструментарием для контрольных показателей dex2oat и art_runtime, используя:
        make ANDROID_PGO_INSTRUMENT=dex2oat
        make ANDROID_PGO_INSTRUMENT=art_runtime
  4. Вы также можете создать одну сборку с инструментарием, в которой все библиотеки будут инструментированы с помощью следующей команды:

        make ANDROID_PGO_INSTRUMENT=dex2oat,art_runtime
        (or)
        make ANDROID_PGO_INSTRUMENT=ALL

    Вторая команда создает все модули с поддержкой PGO для профилирования.

  5. Запустите тесты, чтобы проверить dex2oat и art_runtime и получить:
    • Три файла .profraw из dex2oat (dex2oat_exe.profdata, dex2oat_libart-compiler.profdata и dexeoat_libart.profdata), идентифицированные с помощью метода, описанного в разделе Работа с файлами профилей LLVM.
    • Один art_runtime_libart.profdata.
  6. Создайте общий файл profdata для исполняемого файла dex2oat и libart-compiler.so, используя следующую команду:
    llvm-profdata merge -output=dex2oat.profdata \
        dex2oat_exe.profdata dex2oat_libart-compiler.profdata
  7. Получите профиль для libart.so, объединив профили из двух контрольных точек:
    llvm-profdata merge -output=libart.profdata \
        dex2oat_libart.profdata art_runtime_libart.profdata

    Необработанные значения libart.so из двух профилей могут различаться, поскольку тесты отличаются по количеству тестовых случаев и продолжительности выполнения. В этом случае можно использовать взвешенное объединение:

    llvm-profdata merge -output=libart.profdata \
        -weighted-input=2,dex2oat_libart.profdata \
        -weighted-input=1,art_runtime_libart.profdata

    Приведенная выше команда присваивает профилю из dex2oat в два раза больший вес. Фактический вес определяется на основе знаний о предметной области или экспериментальным путем.

  8. Загрузите файлы профиля dex2oat.profdata и libart.profdata в toolchain/pgo-profiles для использования во время сборки.