Разработка
Профилирование вашего I/O
В этой заметке я рассказываю, как я наткнулся на ненужные операции ввода-вывода на этапе конфигурации Gradle в AndroidX и исправил их.
В статьях «Профилирование — это хорошо» и «Конфигурация Gradle — смерть от тысяч порезов бумагой» я говорил о важности каждой миллисекунды на этапе конфигурации Gradle. Сокращение ввода-вывода на этом этапе — еще один важный аспект. В этой заметке я рассказываю, как я наткнулся на ненужные операции ввода-вывода на этапе конфигурации Gradle в AndroidX и исправил их.
Изучая трассировку ./gradlew build —dry-run в случае чистой сборки на холодную, я обнаружил, что в первые 20 секунд на PGPPublicKeyRing тратится удивительно много времени — 2.6 секунды.
AndroidX работает с включенной проверкой подписи Gradle, поэтому не удивительно, что он читает файл, но удивительно, что это занимает так много времени для файла размером 760 Кб с SSD. Увидев, что это событие чтения файла, я перешел на вкладку событий, чтобы посмотреть, что происходит.
Здесь я обнаружил десятки тысяч чтений файла verification-keyrings.keys, каждое из которых состояло из 1 байта. Затем я щелкнул на одной из записей, чтобы посмотреть трассировку стека.
Если посмотреть на это, то это либо ошибка в библиотеке BouncyCastle PGP, либо в Gradle. В SecuritySupport.java:111 мы видим, что createInputStreamFor(keyringFile) используется для создания этого входного потока, отправляемого в библиотеку BouncyCastle.
private static InputStream createInputStreamFor( File keyringFile) throws IOException { InputStream stream = new FileInputStream(keyringFile); if (keyringFile.getName().endsWith(KEYS_FILE_EXT)) { return new ArmoredInputStream(stream); } return stream; }
Использование FileInputStream без обертывания его BufferedInputStream приводит к вызову операции ввода-вывода для каждого вызова InputStream#read(). И это была именно ошибка в Gradle! У нас будет исправление для этого в Gradle 8.1.
Используя тот же процесс, я также смог обнаружить, что код AndroidX buildSrc выполнял ввод/вывод на этапе конфигурации, чтобы проверить, есть ли в данном проекте тестовый код, и смог удалить его, сэкономив сотни I/O-вызовов на этом этапе.