Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Benchmarking Leiningen on Apple Silicon

Benchmarking Leiningen on Apple Silicon

Gene was tweeting about Clojure's startup performance on Apple Silicon, so I decided to benchmark my new MacBook Air with the M1 chip against my Intel based MacBook Pro.

TL;DR

img

The M1 chip in the MacBook Air - which lacks a fan - was able to quite tidily beat an almost top-of-the-line Intel chip. The standard deviation of results was also much tighter for the M1.

The average for the M1 was 1.846s vs. 2.798s for the Intel, a speedup of 1.5x.

Benchmarking

The approach of testing leiningen startup time comes from this excellent blog post by Alexander Yakushev. The idea is really simple - just start lein in a loop and record how long it takes each time. Perform enough iterations to attempt to smooth out confounding system effects like caching and power management.

for i in {1..32}; do TIMEFORMAT=%E; time ( echo "(quit)" | lein repl >/dev/null 2>&1 ) ; done

Apple Silicon config

Hardware:

    Hardware Overview:

      Model Name: MacBook Air
      Model Identifier: MacBookAir10,1
      Chip: Apple M1
      Total Number of Cores: 8 (4 performance and 4 efficiency)
      Memory: 16 GB
      System Firmware Version: 6723.80.17

Intel config

Hardware:

    Hardware Overview:

      Model Name: MacBook Pro
      Model Identifier: MacBookPro16,1
      Processor Name: 8-Core Intel Core i9
      Processor Speed: 2.3 GHz
      Number of Processors: 1
      Total Number of Cores: 8
      L2 Cache (per Core): 256 KB
      L3 Cache: 16 MB
      Hyper-Threading Technology: Enabled
      Memory: 16 GB
      System Firmware Version: 1554.60.15.0.0 (iBridge: 18.16.14338.0.0,0)

Setup on Apple Silicon

Ensuring you have a full ARM toolchain to see the true performance of the M1 is still a little tricky, but it's improving all the time. I've tried to note down the full set of steps I used.

The steps to get the Zulu OpenJDK installed on Intel should be trivial and I have not bothered to document them here :)

Install homebrew in the right location

On Apple Silicon machines, the new conventional path for homebrew is /opt/homebrew.

git clone https://github.com/Homebrew/brew.git /opt/homebrew

Install Zulu OpenJDK

As of this writing, the only OpenJDK version with an Apple Silicon port is from Azul. They even have multiple versions to choose from.

hdiutil mount -mountpoint /Volumes/zulu https://cdn.azul.com/zulu/bin/zulu13.35.1025-ca-jdk13.0.5.1-macosx_aarch64.dmg
sudo installer -pkg "/Volumes/zulu/*.pkg" -target /
hdiutil unmount /Volumes/zulu

Install Leiningen and dependencies

Note that in order to guarantee that x8664 binaries or bottles aren't sneaking in, I prefix all brew commands with arch -arm64

# install jenv for managing JDK paths
arch -arm64 brew install jenv
# make jenv aware of the Zulu JDK
jenv add /Library/Java/JavaVirtualMachines/zulu-13.jdk/Contents/Home/
# use the Zulu JDK in the current shell (you can also set this globally)
jenv shell zulu64-13.0.5.1
# link the Zulu JDK to stop some (but not all) brew recipes complaining
ln -s /Library/Java/JavaVirtualMachines/zulu-13.jdk/Contents/Home/ /opt/homebrew/opt/openjdk
# install rlwrap, required by clojure
arch -arm64 brew install --build-from-source rlwrap
# install maven, required by clojure, ignoring the openjdk dep
arch -arm64 brew install maven --ignore-dependencies
# install clojure from the clojure tap, which does not have a hard-coded path
arch -arm64 brew install clojure/tools/clojure
# install leniningen, ignoring the openjdk dep
arch -arm64 brew install --build-from-source leiningen --ignore-dependencies

At the end of this you should be able to test the installation with:

clj -e '(System/getProperty "java.home")'
"/Library/Java/JavaVirtualMachines/zulu-13.jdk/Contents/Home"

Leiningen should return the same result:

echo '(System/getProperty "java.home")' | lein repl
nREPL server started on port 50059 on host 127.0.0.1 - nrepl://127.0.0.1:50059
REPL-y 0.4.4, nREPL 0.8.3
Clojure 1.10.1
OpenJDK 64-Bit Server VM 13.0.5.1+1-MTS
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=> "/Library/Java/JavaVirtualMachines/zulu-13.jdk/Contents/Home"
user=> Bye for now!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment