Are your Android Gradle builds ever slower than you expect? Sometimes even painfully slow?
I think most Android devs have felt this at one point or another, and many engineers spend countless hours optimizing Gradle builds to improve the productivity of mobile dev teams.
Unfortunately, sometimes, even a well-optimized Gradle build can be slow. Even really slow at times.
This was the case for our team recently.
We’ve invested a lot of time and effort into getting our Gradle builds under control; making them faster and more efficient across the team. And yet, over the past couple of weeks, we had noticed a frustrating trend of extremely slow builds for one of our devs.
According to our Gradle Enterprise data, some of their local builds were taking 5-15x longer than expected. This was far beyond our typical range of expected build times.
Obviously, something abnormal was happening here, but what?
Thankfully, between Gradle Enterprise build scan insights, and good old-fashioned pair-debugging, we were able to get a good sense of what was causing these extremely slow builds and take steps to rectify the situation.
I’m going to walk through some of our thinking and process in this post, but the moral of the story is this:
If your system is taxed BEFORE your Gradle build even starts, your build is likely going to be slow
Understanding What’s Working And What Isn’t?
How did we start investigating the cause of these slow downs?
As with most build speed issues, we started by investigating some of our build optimizations
- Was local caching turned off for any of these builds?
- Was remote caching turned off for any of these builds?
- Were we suffering from negative cache savings when accessing the remote build cache?
- Are we experiencing a high number of cache misses?
- Were there extremely large change sets being built?
How we answered these questions is a post for another time. The short story is that we used Gradle Enterprise to explore the buildscan details for specific builds.
What matters here is that our build speed optimizations were generally working how we expected. And when comparing the execution of the same Gradle task across different developers’ machines, we continued to see a very large discrepancy in the execution time.
This hinted that there was something going on specifically with this machine, rather that with the project as a whole.
To help further rule out anything specific to the code or the build configuration, we tried one final test.
./gradlew help on two machines
- On my dev machine which was building as expected
- On the machine seeing the slowest of local build times
What we found was surprising, but helped highlight the possible issue.
Running the `help` task on one machine took ~1-3 seconds while on the affected machine it took over 2 minutes.
Clearly something was wrong here.
The `help` task helps us highlight only the configuration time of a Gradle project. If configuration alone is so bogged down that it’s taking minutes to complete, then maybe something is off in the environment itself, rather than with our build at large.
Examining System Configuration
If Gradle build optimizations were behaving as expected, only slower, then perhaps it was differences in the machines themselves.
To start digging into the development machine itself, we started by taking a look at the system’s CPU and available memory.
- i5 processor
- 16GB memory
These specs immediately helped us understand that we couldn’t directly compare the build times from the affected machine and some of the other laptops on our team. We have a mix of devices. Some very similar to this i5/16GB configuration, while others are more similar to an i7/32GB configuration.
A lower spec’d machine would account for some increase in build times, but not to the extent we were seeing with as much as a 15x increase, and certainly wouldn’t explain why the `help` task took over 2min to finish.
So, we then went deeper; to look more closely at how the available resources were actually being used.
Examining System Resource Pressure
Running an Android Gradle build can be a taxing process; asking a lot from both system memory and from available CPUs.
If there are a lack of available system resources, perhaps the builds are being negatively impacted.
To check the status of the system, we opened up the MacOS Activity Monitor. We wanted to check two things:
- Idle CPU %
- Memory Used
The results of this stuck out like a sore thumb.
Without Android Studio or a Gradle build running we saw the following:
- 15% idle CPU (Meaning 85% usage)
- 25% available memory (12GB used out of 16GB total)
As soon as we opened Android Studio on the affected machine, we saw further large spikes in CPU and memory to the point where the machine started to lock up across the board; bringing everything to a halt.
We had found our culprit.
The combination of Android Studio, Android Virtual Devices, video calls, and endless browser tabs was severely taxing this laptop’s system resources. Add in an Android Gradle build and suddenly CPU and Memory were maxed out, causing things to come to a screeching halt.
Freeing Up System Resources
Thankfully, this hypothesis was pretty easy test and remedy. We used Activity Monitor to examine what was taking up CPU/Memory and tried to close the most taxing processes.
On the CPU front, a few things stuck out to us
- pretty consistently high usage from the browser; likely coming from the video call we were on
- several unknown processes that looked to be taking up a lot of CPU. At least 2 of these were known issues on MacOS
- an Android emulator running in the background that wasn’t taking up a lot but still requiring some consistent CPU usage
It was a bit easier to understand where the system memory was being allocated.
- the web browser was taking up a large chunk of memory; likely due to a high number of tabs open and an active video call
- an Android emulator running in the background taking up ~3GB of memory
- Slack, and additional apps running in the background
- when opened, Android Studio consumed ~4GB
After closing out of numerous tabs and programs, changing web browsers, closing the Android emulator, and closing Android Studio, we re-ran the `help` task from the command line.
This time, the task completed in a few seconds as we would expect.
For a more comprehensive test, we ran our default build task again, and were again happy to see that build times were much closer to our expected baseline performance.
Recommendations For Keeping Your Build Fast
The moral of this story is that our Android builds are going to be painfully slow if our systems do not have enough cpu and memory resources available.
So, if you’re experiencing unusually slow builds, you might try some of the following before cursing Gradle and the Android build system.
- change to a less resource web browser
- avoid building your app while on a video call
- use a physical device for testing to avoid the use of an emulator
- build your app without Android Studio being open
- build your app without any other taxing programs being open
- adjust Gradle’s JVM memory settings to something that isn’t likely to consume all your available memory
- keep your machine well cooled to avoid thermal throttling of your CPU
And last, but certainly not least, try to get a more powerful development machine if you can.
If you’re in a position of leadership/influence in your team, and you see a coworker dealing with slow builds simply because they have an underpowered machine, I encourage you to put together a developer efficiency business argument for getting them a faster machine.
A couple of thousands of dollars for a new laptop is likely going to be cheaper and faster than trying to shave another couple of minutes off your already optimized Gradle build.
Hopefully this will help those out there that are maybe scratching their heads as to why their well-optimized builds are still building so slowly; or intermittently slowly.
With so many more video calls, more documents being passed around, and a lack of access to physical testing devices, it’s maybe not surprising if we are over-taxing our laptops and slowing down our builds.