Android 8.0 включает тесты производительности binder и hwbinder для пропускной способности и задержки. Хотя существует множество сценариев для обнаружения заметных проблем с производительностью, их выполнение может занять много времени, а результаты часто недоступны до тех пор, пока система не будет интегрирована. Используя предоставленные тесты производительности, вы сможете тестировать приложение в процессе разработки, выявлять серьезные проблемы на ранних этапах и улучшать удобство использования.
Проверки производительности делятся на четыре категории:
- Пропускная способность Binder (доступно в
system/libhwbinder/vts/performance/Benchmark_binder.cpp) - Задержка Binder (доступно в
frameworks/native/libs/binder/tests/schd-dbg.cpp) - Пропускная способность hwbinder (доступно в
system/libhwbinder/vts/performance/Benchmark.cpp) - Задержка hwbinder (доступно в
system/libhwbinder/vts/performance/Latency.cpp).
О binder и hwbinder
Binder и hwbinder – это инфраструктуры межпроцессного взаимодействия (IPC) Android, которые используют один и тот же драйвер Linux, но имеют следующие качественные различия:
| Формат | Binder | hwbinder |
|---|---|---|
| Назначение | Предоставить схему межпроцессного взаимодействия общего назначения для фреймворка | Обмен данными с оборудованием |
| Свойство | Оптимизировано для использования фреймворка Android. | Минимальная задержка |
| Как изменить правила планирования для переднего плана и фона | Да | Нет |
| Передача аргументов | Использует сериализацию, поддерживаемую объектом Parcel. | Использует буферы разброса и позволяет избежать избыточности при копировании данных, необходимых для сериализации Parcel. |
| Наследование приоритета | Нет | Да |
Процессы Binder и hwbinder
Визуализатор systrace показывает транзакции следующим образом:
В примере выше:
- Четыре процесса schd-dbg являются клиентскими.
- Четыре процесса связывания – это процессы сервера (их названия начинаются с Binder и заканчиваются порядковым номером).
- Клиентский процесс всегда связан с серверным процессом, который предназначен для этого клиента.
- Все пары процессов клиент-сервер планируются ядром независимо и параллельно.
В ЦП 1 ядро ОС выполняет клиентский код, чтобы отправить запрос. Затем он по возможности использует тот же ЦП, чтобы активировать серверный процесс, обработать запрос и переключиться обратно после его завершения.
Пропускная способность и задержка
При идеальной транзакции, когда клиент и сервер переключаются без проблем, тесты пропускной способности и задержки не дают существенно разных сообщений. Однако когда ядро ОС обрабатывает запрос на прерывание (IRQ) от аппаратного обеспечения, ожидает блокировки или просто не обрабатывает сообщение немедленно, может возникнуть задержка.
Тест пропускной способности генерирует большое количество транзакций с разными размерами полезной нагрузки, что позволяет получить хорошую оценку времени обычной транзакции (в лучшем случае) и максимальной пропускной способности, которую может достичь связыватель.
В отличие от него, тест задержки не выполняет никаких действий с полезной нагрузкой, чтобы минимизировать время обычной транзакции. Время транзакции можно использовать для оценки накладных расходов связывателя, составления статистики для худшего случая и расчета соотношения транзакций, задержка которых соответствует указанному сроку.
Как обрабатывать инверсии приоритетов
Инверсия приоритетов происходит, когда поток с более высоким приоритетом логически ожидает поток с более низким приоритетом. В приложениях, работающих в реальном времени, возникает проблема инверсии приоритетов:
При использовании планировщика Linux Completely Fair Scheduler (CFS) у потока всегда есть шанс запуститься, даже если у других потоков более высокий приоритет. В результате приложения с планированием CFS обрабатывают инверсию приоритетов как ожидаемое поведение, а не как проблему. Однако в случаях, когда фреймворку Android требуется планирование в реальном времени, чтобы гарантировать привилегии высокоприоритетных потоков, инверсию приоритетов необходимо устранить.
Пример инверсии приоритетов во время транзакции Binder (поток RT логически блокируется другими потоками CFS при ожидании обслуживания потоком Binder):
Чтобы избежать блокировок, можно использовать наследование приоритета, чтобы временно повысить приоритет потока Binder до потока реального времени, когда он обрабатывает запрос от клиента реального времени. Помните, что планирование в реальном времени имеет ограниченные ресурсы и должно использоваться с осторожностью. В системе с n процессорами максимальное количество текущих потоков RT также равно n. Дополнительным потокам RT может потребоваться подождать (и, следовательно, пропустить сроки), если все процессоры заняты другими потоками RT.
Чтобы устранить все возможные инверсии приоритетов, можно использовать наследование приоритетов как для binder, так и для hwbinder. Однако, поскольку binder широко используется в системе, включение наследования приоритета для транзакций binder может привести к тому, что система будет перегружена потоками реального времени.
Как запустить проверку пропускной способности
Тест пропускной способности выполняется для пропускной способности транзакций binder/hwbinder. В системе, которая не перегружена, пузырьки задержки встречаются редко, и их влияние можно устранить, если количество итераций достаточно велико.
- Тест пропускной способности binder находится в
system/libhwbinder/vts/performance/Benchmark_binder.cpp. - Тест пропускной способности hwbinder находится в состоянии
system/libhwbinder/vts/performance/Benchmark.cpp.
Результаты тестирования
Пример результатов тестирования пропускной способности для транзакций с полезной нагрузкой разного размера:
Benchmark Time CPU Iterations --------------------------------------------------------------------- BM_sendVec_binderize/4 70302 ns 32820 ns 21054 BM_sendVec_binderize/8 69974 ns 32700 ns 21296 BM_sendVec_binderize/16 70079 ns 32750 ns 21365 BM_sendVec_binderize/32 69907 ns 32686 ns 21310 BM_sendVec_binderize/64 70338 ns 32810 ns 21398 BM_sendVec_binderize/128 70012 ns 32768 ns 21377 BM_sendVec_binderize/256 69836 ns 32740 ns 21329 BM_sendVec_binderize/512 69986 ns 32830 ns 21296 BM_sendVec_binderize/1024 69714 ns 32757 ns 21319 BM_sendVec_binderize/2k 75002 ns 34520 ns 20305 BM_sendVec_binderize/4k 81955 ns 39116 ns 17895 BM_sendVec_binderize/8k 95316 ns 45710 ns 15350 BM_sendVec_binderize/16k 112751 ns 54417 ns 12679 BM_sendVec_binderize/32k 146642 ns 71339 ns 9901 BM_sendVec_binderize/64k 214796 ns 104665 ns 6495
- Время – задержка при передаче данных туда и обратно, измеряемая в реальном времени.
- ЦП – общее время, в течение которого ЦП были запланированы для теста.
- Итерации – количество раз, когда выполнялась тестовая функция.
Например, для полезной нагрузки размером 8 байт:
BM_sendVec_binderize/8 69974 ns 32700 ns 21296
… максимальная пропускная способность связующего ПО рассчитывается следующим образом:
Максимальная пропускная способность с полезной нагрузкой 8 байт = (8 * 21296)/69974 ~= 2,423 б/нс ~= 2,268 Гб/с
Варианты тестирования
Чтобы получить результаты в формате JSON, запустите тест с аргументом --benchmark_format=json:
libhwbinder_benchmark --benchmark_format=json
{
"context": {
"date": "2017-05-17 08:32:47",
"num_cpus": 4,
"mhz_per_cpu": 19,
"cpu_scaling_enabled": true,
"library_build_type": "release"
},
"benchmarks": [
{
"name": "BM_sendVec_binderize/4",
"iterations": 32342,
"real_time": 47809,
"cpu_time": 21906,
"time_unit": "ns"
},
….
}Как запустить тесты задержки
Тест на задержку измеряет время, которое требуется клиенту, чтобы начать инициализацию транзакции, перейти к серверному процессу для обработки и получить результат. Кроме того, тест проверяет, нет ли у планировщика известных проблем, которые могут негативно повлиять на задержку транзакций, например не поддерживает ли он наследование приоритетов или флаг синхронизации.
- Тест задержки связывания находится в папке
frameworks/native/libs/binder/tests/schd-dbg.cpp. - Тест задержки hwbinder находится в
system/libhwbinder/vts/performance/Latency.cpp.
Результаты тестирования
Результаты (в формате JSON) содержат статистику по средней, лучшей и худшей задержке, а также по количеству пропущенных сроков.
Варианты тестирования
Для тестирования задержки можно использовать следующие параметры:
| Команда | Описание |
|---|---|
-i value |
Укажите количество итераций. |
-pair value |
Укажите количество пар процессов. |
-deadline_us 2500 |
Укажите срок в микросекундах. |
-v |
Получить подробные выходные данные (отладка). |
-trace |
Остановить трассировку по истечении срока. |
В следующих разделах подробно описаны все варианты, их использование и примеры результатов.
Как указать количество итераций
Пример с большим количеством итераций и отключенным подробным выводом:
libhwbinder_latency -i 5000 -pair 3
{
"cfg":{"pair":3,"iterations":5000,"deadline_us":2500},
"P0":{"SYNC":"GOOD","S":9352,"I":10000,"R":0.9352,
"other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996},
"fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}
},
"P1":{"SYNC":"GOOD","S":9334,"I":10000,"R":0.9334,
"other_ms":{ "avg":0.19, "wst":2.9 , "bst":0.055, "miss":2, "meetR":0.9996},
"fifo_ms": { "avg":0.16, "wst":3.1 , "bst":0.066, "miss":1, "meetR":0.9998}
},
"P2":{"SYNC":"GOOD","S":9369,"I":10000,"R":0.9369,
"other_ms":{ "avg":0.19, "wst":4.8 , "bst":0.055, "miss":6, "meetR":0.9988},
"fifo_ms": { "avg":0.15, "wst":1.8 , "bst":0.067, "miss":0, "meetR":1}
},
"inheritance": "PASS"
}Результаты тестирования показывают следующее:
"pair":3- Создает пару клиент-сервер.
"iterations": 5000- Включает 5000 итераций.
"deadline_us":2500- Срок – 2500 мкс (2,5 мс). Большинство транзакций должны соответствовать этому значению.
"I": 10000- Одна итерация тестирования включает две транзакции:
- Одна транзакция с обычным приоритетом (
CFS other) - Одна транзакция с приоритетом в реальном времени (
RT-fifo)
- Одна транзакция с обычным приоритетом (
"S": 9352- 9352 транзакции синхронизируются на одном ЦП.
"R": 0.9352- Указывает соотношение, в котором клиент и сервер синхронизируются на одном ЦП.
"other_ms":{ "avg":0.2 , "wst":2.8 , "bst":0.053, "miss":2, "meetR":0.9996}- Среднее (
avg), худшее (wst) и лучшее (bst) время для всех транзакций, выполненных вызывающим абонентом с обычным приоритетом. Две транзакцииmissсрок, поэтому коэффициент выполнения (meetR) равен 0,9996. "fifo_ms": { "avg":0.16, "wst":1.5 , "bst":0.067, "miss":0, "meetR":1}- Аналогично
other_ms, но для транзакций, инициированных клиентом с приоритетомrt_fifo. Скорее всего (но не обязательно),fifo_msпокажет лучшие результаты, чемother_ms, с более низкими значениямиavgиwstи более высоким значениемmeetR(разница может быть ещё более значительной при загрузке в фоновом режиме).
Примечание. Фоновая нагрузка может повлиять на результат тестирования пропускной способности и кортеж other_ms в тесте задержки. Только fifo_ms может показывать похожие результаты, если фоновая загрузка имеет более низкий приоритет, чем RT-fifo.
Как указать значения пар
Каждый клиентский процесс связан с серверным процессом, выделенным для клиента, и каждая пара может быть запланирована независимо для любого ЦП. Однако перенос ЦП не должен происходить во время транзакции, если флаг SYNC имеет значение honor.
Убедитесь, что система не перегружена. Высокая задержка в перегруженной системе ожидаема, но результаты тестирования в таких условиях не дают полезной информации. Чтобы проверить систему при более высоком давлении, используйте -pair
#cpu-1 (или -pair #cpu с осторожностью). Тестирование с использованием -pair n с n > #cpu перегружает систему и создает бесполезную информацию.
Как указать значения сроков
После обширного тестирования пользовательских сценариев (запуска теста задержки на подходящем устройстве) мы определили, что задержка должна составлять не более 2,5 мс. Для новых приложений с более высокими требованиями (например, 1000 фотографий в секунду) этот срок будет изменен.
Укажите подробный вывод
Параметр -v позволяет получить подробные выходные данные. Пример:
libhwbinder_latency -i 1 -v-------------------------------------------------- service pid: 8674 tid: 8674 cpu: 1 SCHED_OTHER 0-------------------------------------------------- main pid: 8673 tid: 8673 cpu: 1 -------------------------------------------------- client pid: 8677 tid: 8677 cpu: 0 SCHED_OTHER 0-------------------------------------------------- fifo-caller pid: 8677 tid: 8678 cpu: 0 SCHED_FIFO 99 -------------------------------------------------- hwbinder pid: 8674 tid: 8676 cpu: 0 ??? 99-------------------------------------------------- other-caller pid: 8677 tid: 8677 cpu: 0 SCHED_OTHER 0 -------------------------------------------------- hwbinder pid: 8674 tid: 8676 cpu: 0 SCHED_OTHER 0
- Поток сервиса создается с приоритетом
SCHED_OTHERи выполняется вCPU:1сpid 8674. - Первая транзакция запускается
fifo-caller. Чтобы обслужить эту транзакцию, hwbinder повышает приоритет сервера (pid: 8674 tid: 8676) до 99, а также помечает его временным классом планирования (обозначается как???). Затем планировщик переводит процесс сервера в состояниеCPU:0и синхронизирует его с тем же ЦП, что и клиент. - У вызывающего абонента второй транзакции приоритет
SCHED_OTHER. Сервер понижает свой приоритет и обслуживает вызывающую сторону с приоритетомSCHED_OTHER.
Как использовать трассировку для отладки
Чтобы устранить проблемы с задержкой, можно указать параметр -trace. При использовании теста задержки запись трассировки останавливается в момент обнаружения недопустимой задержки. Пример:
atrace --async_start -b 8000 -c sched idle workq binder_driver sync freqlibhwbinder_latency -deadline_us 50000 -trace -i 50000 -pair 3deadline triggered: halt ∓ stop trace log:/sys/kernel/debug/tracing/trace
На задержку могут влиять следующие компоненты:
- Режим сборки Android. Режим Eng обычно работает медленнее, чем режим userdebug.
- Framework. Как сервис фреймворка использует
ioctlдля настройки связывания? - Драйвер Binder. Поддерживает ли драйвер точную блокировку? Содержит ли он все исправления, связанные с производительностью?
- Версия ядра. Чем лучше ядро поддерживает режим реального времени, тем лучше результаты.
- Конфигурация ядра. Содержит ли конфигурация ядра параметры
DEBUG, напримерDEBUG_PREEMPTиDEBUG_SPIN_LOCK? - Планировщик ядра. Есть ли в ядре планировщик Energy-Aware (EAS) или Heterogeneous Multi-Processing (HMP)? Влияют ли на планировщик какие-либо драйверы ядра (драйвер
cpu-freq, драйверcpu-idle,cpu-hotplugи т. д.)?