Gradle 7

Article JAVA Magazine 04 – 2021

Iedere developer herinnert zich misschien nog wel het eerste project dat gebouwd werd. Of dat nu met Gradle, Maven, Ant of een andere tool was. De compiler ging aan de slag en enkele seconden later kreeg je te horen dat het bouwen succesvol was afgerond.

 

Naarmate de projecten complexer werden, en daarmee de hoeveelheid code fors groter, werden de build tijden ook al snel groter. Tientallen seconden, minuten, of zelfs nog langer. Om die reden is incrementeel builden al langere tijd mogelijk. Dat houdt echter nog steeds in dat alle bestanden nagelopen moeten worden om te zien of er een wijziging heeft plaatsgevonden. Zo ja, dan moet alles wat daarna gebouwd wordt opnieuw worden gecompileerd.

 

{ Virtual filesystem }

In recente versies van Gradle [1] is een feature toegevoegd die daar wat aan wil doen: watch-fs. Deze feature maakt gebruik van filesystem events en abonneert zich op wijzigingen op het filesysteem binnen het project. Dit wordt op de meest gangbare filesystemen op zowel Linux, Mac als Windows ondersteund; eigenlijk alleen netwerkshares en FAT-gebaseerde volumes vallen buiten de boot.

 

In de praktijk komt het er op neer dat de eerste keer dat je het project bouwt en deze feature staat aan (default vanaf versie 7, optioneel vanaf 6.7), er een boom wordt opgebouwd van alle files die bij het project horen. Tegelijkertijd abonneert de Gradle daemon zich op events voor al deze files en bijbehorende directories. Op de commandline met logging ziet dat er zo uit:

 

./gradlew clean build


Virtual file system retains information about 163 files, 35 directories and 0 missing files until next build

 

Bij een volgende ronde is dat anders:

 

Received 14 file system events since last build while watching 1 hierarchies

Virtual file system retained information about 163 files, 35 directories and 0 missing files since last build

 

Bovenstaand voorbeeld is van een klein project (<1kloc). Je kunt je voorstellen dat dit voor grotere projecten meer invloed heeft.

 

{ Benchmarking }

Om te meten wat voor effect dit heeft, kijk ik naar het effect op twee projecten waar ik recent een upgrade naar Gradle 7 heb doorgevoerd. Het ene project is klein (±750 loc, ±150 files in 35 directories) en het andere middel groot (±70 kloc, ±35k files in ±9700 directories). Om het verschil duidelijk te maken is er ook met versie 6.5.1 en 6.7 getest.

 

Hoewel de Gradle Profiler [2] door het Gradle-project wordt aanbevolen om te meten wat het effect van een aanpassing aan de build configuratie of Gradle-versie oplevert, laat ik deze in dit geval toch links liggen. Enerzijds omdat de cijfers van de build volgens de profiler en de daadwerkelijke duur op de commandline nogal ver uit elkaar liggen (voor het middelgrote project gaf de profiler 20-30s aan terwijl een reguliere build eerder rond de 5-6m duurt). Anderzijds is dat de manier waarop de benchmark werkt. Daarbij wordt tussen twee runs de daemon gestopt en zal daar elke VFS-cache opgebouwd door de build gelijk weer mee verloren gaan. Verder wordt er niks aan de code veranderd tussen twee runs en dat is juist waar we in geïnteresseerd zijn; het effect van de watch-fs feature op incrementele builds.

 

Om die redenen is er teruggegrepen op ouderwets handwerk. Daarbij beginnen we met het runnen van ./gradlew build om de boel te primen. Daarna maken we een kleine aanpassing en testen we de incremental build door weer een ./gradlew build uit te voeren. Dit voeren we dan een aantal keer uit om te kijken wat de gemiddelden en uitschieters zijn. In de tabel vind je de resultaten van deze laatste runs:

 

Daan: ik weet niet of je dit soort tabellen makkelijk kan overnemen, anders moet je me even laten weten hoe ik dit het meest ‘werkbaar’ voor jou kan maken!

 

  Project XS Project M
Gradle 6.5.1 1 – 3s 3m 37s – 5m 27s
Gradle 6.7 1 – 2s 3m 15s – 3m 58s
Gradle 7.2 1 s 24s – 4m 40s

 

Voor het kleine project moge duidelijk zijn dat de cijfers zo dicht bij elkaar liggen en er weinig winst behaald lijkt te zijn. Al was er ook niet veel ruimte meer om dat te doen. Het grotere project daarentegen haalde een deel van de tijd een zeer forse winst; meerdere runs lagen in de range van 24-28s. Ten opzichte van de andere versies is dat een heel groot verschil. Toch werd deze winst niet consistent behaald. Bij twee runs kwam de totale tijd toch weer in de buurt van wat de eerdere versies aan tijd nodig hadden. Mogelijk zijn deze twee runs te verklaren doordat de daemon niet hergebruikt kon worden, waardoor er een nieuwe instantie nodig was en dus ook een nieuwe VFS cache opgebouwd moest worden. Waarom de daemon niet hergebruikt kon worden, was niet duidelijk te achterhalen.

Onder de streep kunnen we wel de conclusie trekken dat deze nieuwe feature incrementele builds behoorlijk kan versnellen. Of dat ook voor jouw project geldt, lijkt echter af te hangen van de omvang van je project. Met name grotere projecten kunnen hier winnen.

 

Referenties:

  1. https://gradle.org
  2. https://blog.gradle.org/introducing-file-system-watching
  3. https://github.com/gradle/gradle-profiler

Bio:

Thomas Zeeman is software architect bij Trifork. In die hoedanigheid werkt hij mee aan diverse grote en kleinere projecten. Daarbij komt ook geregeld zijn interesse in nieuwe ontwikkelingen op het gebied van software ontwikkeling aan de orde.