Kotlin vs

Kotlin vs

Як уже знають всі Android-розробники, Google недавно оголосила про офіційну підтримку Kotlin в Android. Багато ризики, пов'язані з використанням цього чудового мови в Android-проектах, зняті. Але актуальним, особливо для дуже великих проектів, яким є Badoo, залишається питання про швидкість збірки. Я був радий виявити, що в мережі вже є дослідження на цю тему, і перекладом одного з них хочу поділитися.

Отже, якщо ви переводите додаток з Java на Kotlin, чи буде воно компілюватиметься довше?

У більш ранній статті обговорювалося конвертування Android-додатки з Java цілком в Kotlin. Коду на Kotlin виходило менше, і він був зручніше в супроводі, ніж на Java, так що я прийшов до висновку, що воно того варте. Але деякі розробники не хочуть пробувати Kotlin, побоюючись, що він може компілюватися повільніше Java. І це занепокоєння - справедливо: ніхто не хоче витрачати час на конвертацію коду, якщо в результаті складання триватиме довше. Так що давайте вивчимо тривалість компіляції додатка App Lock до і після конвертації в Kotlin. Я не буду порівнювати швидкість Kotlin і Java через підрядник, а замість цього спробую відповісти на питання, чи вплине конвертування всієї кодової бази з однієї мови в інший на загальну тривалість збірки.

Як я тестував тривалість збірки

Я написав shell-скрипти для повторюваних запусків Gradle-збірок за різними сценаріями. Всі тести виконувалися послідовно по десять разів. Перед кожним новим сценарієм проект очищався. Для сценаріїв, які використовують демона Gradle. останній зупинявся перед запуском бенчмарка.

Все бенчмарки виконувалися на машині з Intel Core i7-6700, що працює з частотою 3,4 ГГц, оснащеної 32 Гб пам'яті DDR4, а також SSD-приводом Samsung 850 Pro. Вихідний код збирався за допомогою Gradle 2.14.1.

Я хотів прогнати бенчмарки для декількох поширених сценаріїв використання: чисті збірки с / без демона Gradle, інкрементальні збірки без зміни файлів, інкрементальні збірки зі зміненим файлом.

Кодова база App Lock на Java містила 5491 метод і 12 371 рядок коду. Після конвертації в Kotlin кількість методів зменшилася до 4987, а кількість рядків - до 8564. У процесі перетворення в архітектуру не вносилися ніякі серйозні зміни, так що вимірювання тривалості компіляції до і після конвертації має дати чітке уявлення про різницю в тривалості зборки між Java і Kotlin .

Чисті збірки без демона Gradle

Це найгірший сценарій з точки зору тривалості зборки для обох мов: запуск чистої збірки з холодним стартом. Для цього тесту я відключив демон Gradle.

Ось скільки часу зайняли всі десять збірок:

Kotlin vs

Десять послідовних чистих збірок без демона Gradle

Середня тривалість збірки Java становить 15,5 секунд, Kotlin - 18,5 секунд: збільшення на 17%. Чи не найкращий початок для Kotlin, але більшість людей компілюють свій код за іншими сценаріями.

Найчастіше ми кілька разів компілюємо одну і ту ж кодову базу в міру внесення в неї змін. Саме для цього сценарію був розроблений демон Gradle, так що давайте включимо його і подивимося, що вийде.

Чисті збірки з включеним демоном Gradle

Однією з проблем JIT-компіляторів на кшталт JVM є те, що вони витрачають час на компіляцію виконуваного в них коду, так що в міру його виконання продуктивність процесу збільшується. Але якщо зупинити JVM-процес, то приріст продуктивності втрачається. При кожній збірці Java-коду зазвичай доводиться запускати і зупиняти JVM. В результаті він кожен раз заново робить одну і ту ж роботу. Для вирішення цієї проблеми Gradle поставляється з демоном, який продовжує функціонувати між збірками і допомагає підтримувати приріст продуктивності, який забезпечувався б JIT-компіляції. Включити демон можна за допомогою Gradle-команди --daemon. введеної в командному рядку, або за допомогою додавання org.gradle.daemon = true в файл gradle.properties.

Ось результат прогону тієї ж серії збірок, але з включеним демоном Gradle:

Kotlin vs

Десять послідовних складань з включеним демоном Gradle

Як бачите, перший прогін займає приблизно стільки ж часу, скільки в сценарії без демона. У наступних збірках продуктивність зростає аж до четвертого прогону. При такому сценарії більш доцільно оцінювати середню тривалість збирання після третього прогону, коли демон вже прогрілося. В цьому випадку чиста збірка на Java займає в середньому 14,1 секунди, а на Kotlin - 16,5 секунд: збільшення на 13%.

Kotlin наздоганяє Java, але все ще відстає. Проте незалежно від мови демон Gradle зменшує тривалість збірок більш ніж на 40%. Якщо ви його ще не використовуєте, то саме час почати.

Отже, повні збірки на Kotlin виконуються трохи повільніше, ніж на Java. Але зазвичай ми компілюємо після внесення змін всього лише в кілька рядків коду, так що інкрементальні збірки повинні демонструвати іншу продуктивність. Давайте дізнаємося, чи зможе Kotlin наздогнати Java там, де це важливо.

інкрементальні збірки

Використання інкрементальною компіляції є одним з найважливіших властивостей компілятора щодо підвищення продуктивності. При звичайній збірці перекомпілюються всі вихідні файли проекту, а при інкрементальною - відстежується, які файли змінилися з моменту попередньої збірки, і в результаті перекомпілюються тільки ці файли і ті, що від них залежать. Це може робити дуже сильний вплив на тривалість компіляції, особливо в великих проектах.
Інкрементальні збірки з'явилися в Kotlin 1.0.2. їх можна включити, додавши kotlin.incremental = true в файл gradle.properties. або через командний рядок.

Отже, як зміниться тривалість компіляції Kotlin в порівнянні з Java при використанні інкрементальною компіляції?

Ось результати бенчмарка за умови відсутності змін в файлах:

Kotlin vs

Десять послідовних інкрементальних збірок без зміни файлів

Тепер протестуємо Інкрементальний компіляцію за умови зміни одного вихідного файлу. Для цього я перед кожною збіркою змінював Java-файл і його Kotlin-еквівалент. В даному бенчмарку це файл, що відноситься до призначеного для користувача інтерфейсу, від нього не залежать інші файли:

Kotlin vs

Десять послідовних інкрементальних збірок з одним окремим зміненим файлом

Нарешті, давайте подивимося на результати інкрементальною компіляції з одним зміненим вихідним файлом, який імпортується в багато інших файли проекту:

Kotlin vs

Десять послідовних інкрементальних збірок за умови зміни одного ключового файлу

Як бачите, демона Gradle все ще доводиться прогрівати протягом двох-трьох прогонів, але після цього обидві мови стають дуже близькі по продуктивності. При відсутності змін в файлах у Java йде 4,6 секунди на прогріту збірку, а у Kotlin - 4,5 секунди. Якщо ми змінюємо файл, але він не використовується іншими файлами, то Java потрібен 7 секунд на виконання прогрітій збірки, а Kotlin - 6,1 секунди. Нарешті, якщо змінений файл імпортується в багато інших файли проекту, то при прогрітому демона Gradle Інкрементальний збірка Java займає 7,1 секунди, а у Kotlin йде в середньому 6 секунд.

висновок

Ми виміряли продуктивність при декількох різних сценаріях, щоб дізнатися, чи зможе Kotlin конкурувати з Java по тривалості компіляції. При чистих збірках, які виконуються порівняно рідко, Java перевершує Kotlin на 10-15%. Але найчастіше розробники виконують часткові збірки, при яких великий виграш у часі досягається за рахунок інкрементального компілювання. Завдяки працює демона Gradle і включеної інкрементальною компіляції Kotlin не поступається, або навіть трохи перевершує Java. Вражаючий результат, якого я не очікував. Висловлюю команді розробників Kotlin свою повагу за створення мови, який не тільки володіє прекрасними можливостями, а й компілюється так швидко.

Якщо ви поки не спробували Kotlin з побоювань збільшення тривалості компіляції, то можете більше не турбуватися: він компілюється так само швидко, як Java.

Сирі дані, зібрані мною при прогоні бенчмарков, лежать тут.