M1 Mac mini Review

I’ve had an M1 Mac mini for about a month now, so I thought I’d write up my experiences with it. The configuration I got has 16GB of RAM and 256GB of storage (the base amount). The reasoning for the bare minimum storage is that Apple charges an arm and a leg for extra space, and I intend this to primarily be a stopgap machine until there are higher-power Apple Silicon equipped laptops. If I really desperately need extra space down the line, I can buy a cheap external SSD (that will continue to be useful after this computer is gone). The 16GB of RAM, however, is necessary to do any iOS development (Xcode and all the various associated services can by themselves easily consume almost 8 gigs). So far, I’ve moved just about all of my non-work desktop computing over to it, and it’s been absolutely fantastic.

The first thing I did after setting up my account was to install Firefox. When the M1 Macs first became available there weren’t yet native versions of big applications like Firefox and Chrome available, but fortunately by the time I got mine, a beta version of Firefox that was recompiled for ARM was available.

Just playing around, visiting a few websites, I was happy to see everything appeared to be working. You might think this would be a given, but when I tried Firefox on the macOS 11 at the beginning of the beta cycle (on my Intel machine, even), video playback was almost completely non-functional.

With Firefox reinstalled, I started signing back into a bunch of my accounts. With long, random passwords, this is rather tedious, so before proceeding I wanted to install my password manager. For reasons that don’t bear getting into, I use an old version of 1Password 6, not the most recent. Of course, this means that it hasn’t been recompiled to be a Universal app; it only runs on Intel. Fortunately, Rosetta comes to the rescue. After copying the .app from another computer, I double clicked it to launch it, just like any other app, after which the OS presented a dialog asking if I wanted to install Rosetta.

Presumably it’s shipped separately and not bundled with the base OS because of the bandwidth requirements of shipping an x86_64 version of every system library that not everyone may need. Additional copies of system libraries are needed because within a single process only one architecture can be used: either everything must be Intel or everything must be Apple Silicon.

Using Rosetta for 1Password worked perfectly. Aside from a slight delay when launching the app for the first time (due to Rosetta pre-translating the Intel binary into ARM). Once it launched, everything worked exactly as I expect. Even IPC between 1Password under Rosetta and the natively-running Firefox extension works flawlessly.

Other Intel-only apps I’ve tried have also worked similarly well. IINA, a video player built on top of mpv, works perfectly. Inkscape, in my brief testing, also works as well as it does on Intel, which is to say, it functions but is not particularly pleasant (fortunately Inkscape some time ago released a native macOS version that no longer depends on XQuartz, removing a potential barrier). I’m also currently running MacVim under Rosetta (in which I’m writing this post) and it’s working without issue. coc.nvim is a Vim plugin which hosts Node.js extensions that provide autocompletion and LSP support. The part of the plugin that runs inside Vim runs, of course, through Rosetta. Node, which I installed through Homebrew, supports ARM natively, and, as with 1Password, IPC accross the architecture boundary works perfectly. The only other significant app I tested under Rosetta is the game Sayonara Wild Hearts. The version in the App Store is Intel-only, and it’s built with Unity 2018.4.7f1, which was released in August of 2019. It works flawlessly running at 1440p @ 144Hz. I can’t say the exact framerate, because the game doesn’t have a counter built in and I’m too lazy to find a standalone macOS one, but it’s definitely well above 60FPS and runs very smoothly, without any noticable input latency or hitching.

The only performance issue I encountered with Rosetta was using an older version of Node.js that was not compiled to run natively. Performance when running under Rosetta was significantly worse than both running natively on the M1 and natively on my Intel laptop. I tried to run npm install but called it quits after almost 5 minutes and switched to a newer version of Node that was compiled for ARM. Presumably this significant performance difference is due to Node’s usage of the V8 VM, which does just-in-time compilation. This requires Rosetta to constantly re-translate x86_64 code into ARM, rather than being able to translate everything ahead-of-time and then execute natively, without interruption.

Of the Mac apps I’ve tried, the sole app that hasn’t worked is MonitorControl, an app that uses the Display Data Channel to control the backlight brightness on monitors that macOS itself does not support. Although the app is running under Rosetta, that isn’t the problem. It seems that the system APIs used to access the DDC either aren’t implemented on ARM or aren’t supported by the driver for Apple’s own GPU. Hopefully this is merely an oversight that can be fixed in a future macOS update.

One of the first things I did upon receiving the machine was following these popular instructions to install Homebrew. I installed two copies of it: one running natively on ARM, installed in /opt/homebrew/, and the other forced to run in x86_64 under Rosetta in the classic location. There’s also a shell alias to let me easily access the Rosetta version by running ibrew which wraps the Homebrew command in arch -x86_64.

I expected the native version of Homebrew to be fairly unstable, and that I would have to fallback on the Intel version frequently. In a pleasant surprise, that has not been the case. Almost everything I’ve tried to install with Homebrew has worked perfectly with the ARM version, even those packages which themselves are x86 only and run under Rosetta. In fact, the only thing that hasn’t worked with native Homebrew has been iPerf.

$ brew list --formula | wc -l
	87
$ ibrew list --formula | wc -l
	1

Up to this point, everything I’d done had been not so different from setting up any previous Mac. The first thing I did that really gave me a sense of how much faster the M1 is was installing Xcode. The non-App Store version of Xcode is distributed in an ~11GB .xip file, which is a compressed, cryptographically-signed format that is notoriously slow to extract. I didn’t time it, but when extracting Xcode on my Intel laptop, I’d start the extraction process and then go back to doing something else for 10 to 15 minutes. On the M1, it took between 5 and 10 minutes. Still not terribly quick, but a substantial improvement. With Xcode installed, I was eager to start playing around with my iOS projects.

Tusker, my Mastodon app for iOS, is easily the biggest iOS project I have, at about 21 thousand lines of Swift code and a couple dozen .xib files (which surprisingly are the source of a significant fraction of the compile time). On my Intel laptop, which is an 8-core 16“ MacBook Pro (with 32GB of RAM, double that of the mini), a clean build in debug mode takes 47.9 seconds. On the Mac mini, the same takes 29.1 seconds, an incredible 39% improvement. When performing a full archive of the app, which builds it in release mode with all optimizations enabled, my Intel machine takes 83.9 seconds and the M1 takes 59.9 seconds, making for a 28% improvement.

It’s difficult to overstate how impressive this is. With Intel having long since fallen off the curve of Moore’s law, a 30 to 40 percent in a single (customer-facing) hardware generation is incredible.

While developing, I don’t actually spend that much time doing full rebuilds, though. So, how about a slightly more practical example of why this is such a big deal? One thing I’ve previously talked about (as have many others) as making perhaps the single biggest difference in the enjoyablity of programming is the speed of the feedback loop. That is to say, how fast is it from the time when I make a change to my code and hit the Build & Run button to the time at which I can actually see the effect of that change. The smaller the window of time in between those two, the less time there is for me to get distracted, the faster I can figure out whether the change has done what I desired, and the faster I can move on to making the next change. To test this, again using Tusker, I built and launched the app, made a 1-line trivial change to the project (adding view.backgroundColor = .red to a certain view controller), and then rebuilt and launched the app once more. This was more difficult to time accurately, but the M1 was about twice as fast as the Intel machine (~5 seconds down from ~10). Because of this, as I’ve continued to use this computer, iOS development is the task for which it’s made the largest difference in my day-to-day activities.

I ran a few more programming-related tests aside from iOS development, because that’s not all I do. When I first got the machine, I was still in the midst of Advent of Code. The puzzle I had most recently completed was day 11, which was implenting a variation of Conway’s Game of Life. I wrote my solution in Elixir on my laptop, and it worked perfeclty well but was bottlenecked by the fact that looking up the n-th element of a list in Elixir is an O(n) operation. I thought these would make for a decent improvised Elixir benchmark. On my Intel MBP, my solution for part 1 of the puzzle ran in 5.9 seconds and part 2 in 8.0 seconds. With BEAM (the VM used by Erlang/Elixir) running natively on the M1, those times came down to 2.6 seconds and 3.5 seconds respectively, or over twice as fast in both cases.

Out of curiosity (I didn’t expect to actually be able to use this machine for work, mainly because of the memory limitation), I also tried compiling the front-end code for the project I work on at my job. It’s composed of approximately 190 thousand lines in total of TypeScript, Vue, and Sass. Here the performance improvement was less impressive, though not insignificant. Compiling everything from scratch takes 85 seconds on my laptop and 69 seconds on the Mac mini. Additionally, when compiling from scratch, the fans in my laptop spin up and become quite loud (not to mention the chasis gets unpleasantly hot) about 60 seconds in, whereas the Mac mini remains silent for the entire duration. While running Webpack in watch mode and making a trivial change (inserting a console.log call in a Vue component) the time it takes for Webpack to finish recompiling is 19 seconds on my laptop and just 11 on the M1. I’m not certain, but I suspect at least part of the reason the performance improvement with Webpack is so much less than with other tasks, particularly during a clean build, is because Node.js is entirely single-threaded.

As for the hardware itself? It’s fantastic.

Like I went over, performance is great; it easily beats my 16“ Intel MacBook Pro in every task I used it for. The other remarkable thing is the noise. It has a single fan, but if you told me it was completely fanless, I would believe you. Not once have I heard the fan turn on. The environment I’m using it in isn’t incredibly quiet, but it’s really not that loud. The only way I can tell the fan in this computer is spinning is if I put my hand behind the rear exhaust vent or if I press my ear up against its surface.

Unlike on the laptop variants of the M1 machine, where the port selection is a measly two USB-C ports, the mini also has an Ethernet port, two type-A USB ports, a HDMI output, and a separate AC input. On a laptop, the I/O would be very limiting; trying to connect to an external desk setup would require some adapters or a big, fancy (not to mention expensive) Thunderbolt dock. But the mini has enough ports that I can directly connect everything from my setup that I’d normally connect to my laptop (granted, I do use my monitor’s built-in USB hub for both machines).

I’ve had only a few issues I’ve experienced that may be attributable to hardware. The first is that when I’m playing back audio to my Bluetooth headphones, they periodically cut out for a fraction of a second before resuming audio playback. I regularly use these same headphones with my Intel Mac on which I haven’t experienced this issue while running either Catalina or Big Sur. The other issue is that when the Mac mini is connected to my 1440p/144Hz display, it loses the 144Hz setting almost every time I wake it from sleep. Odly, just changing the refresh rate while the resolution is set to “Default for display” does nothing. I have to first change the resolution to be scaled down, and then back to the default before changing the refresh rate actually takes effect. The final issue, which has only happened once in the past month, is that when I woke the computer up from sleep, it had stopped outputting video over the HDMI port. It was still sending video over a USB-C to DisplayPort adapter, but not HDMI. Unplugging and reconnecting the HDMI cable didn’t fix the issue, nor did power cycling the monitor. I had to fully restart the Mac mini to resolve the issue.

But, all in all, for a product that’s the first generation of both a new (to macOS) CPU and GPU architecture, this has been an phenomenally good experience. I am incredibly eager to see both what a higher-performance variant of the M1 looks like and future generations of this architecture.

Comments

Comments powered by ActivityPub. To respond to this post, enter your username and instance below, or copy its URL into the search interface for client for Mastodon, Pleroma, or other compatible software. Learn more.

Reply from your instance: