The newest and latest (late-2020) Apple Macbook Air and Macbook Pro 13” with Apple Silicon has been out on the market for a while now. Recently, I had the priviledge of getting a Macbook Air with M1 chip as as a dev machine to test various things (c++, python, jupyter notebook) that I use.

From the Youtube reviews (MKBHD) and various benchmarking websites (Toms’ Hardware, Daring Fireball), I already know that the performance of the M1 chips is really impressive at the given power consumption; and that people are getting ridiculous battery lifes on their M1 Macs. However, as someone that intends to use the Mac as a development daily driver, my main concern is how my workflow and toolchain will work with the new Apple Silicon and ARM64 instruction set. After a few weeks of tinkering and exploration, I think it is safe to say that Apple has done an incredible job to ensure smooth transition to the new hardware, and that AMD/Intel/Microsoft or any other hardware manufacturer should be shitting their pants right now for the on-slaught that’s about to come.

How Rosetta2 Works

To quote Apple’s developer documentation on what is Rosetta:

Rosetta is a translation process that allows users to run apps that contain x86_64 instructions on Apple silicon.

First you need to install Rosetta 2 via the Terminal

softwareupdate --install-rosetta

If you use the file commandline utility to inspect the type of file of various executables, you should see something like this

(base) ➜  ~ file /Users/bolu/miniconda3/bin/python
/Users/bolu/miniconda3/bin/python: Mach-O 64-bit executable x86_64

Rosetta2 allows you to run the executable directly as if the Python is running on a x86_64 machine, example below:

(base) ➜  python
Python 3.8.5 (default, Sep  4 2020, 02:22:02)
[Clang 10.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import platform
>>> platform.architecture()
('64bit', '')
>>> platform.machine()
'x86_64'
>>>

So, from the perspective of the user, when running compiled programs, the Rosetta translation process is transparent - one just need to ensure the compiler and linker are all linking consistently to x86_64 or arm64 binarires.

Crosscompiling a C++ Project

To illustrate cross-compilation, we have a toy hello world program here:

#include <iostream>

int main(){
        std::cout << "hello world!" << std::endl;
        return 0;
}

By default, clang++ will use the host machine architecture as the target, as a result, if we compile the following program, we’ll get a ARM64 executable:

(base) ➜  Tmp clang++ hello.cpp
(base) ➜  Tmp ./a.out
hello world!
(base) ➜  Tmp file a.out
a.out: Mach-O 64-bit executable arm64

You can specify that you wanted to compile this program targetting x86_64 architecture via the -arch flag

(base) ➜  Tmp clang++ -arch x86_64 hello.cpp
(base) ➜  Tmp file a.out
a.out: Mach-O 64-bit executable x86_64

Here’s the interesting bit, on M1 Macs, you can execute these two binraries as if they are native by just invoking them. The Rosetta2 is smart enough to figure out whether to apply Rosetta2 to translate the binaries.

What about Universal Binary apps?

Universal binary apps really contain two executables for both architecture. Apple provided an example of using lipo tool to wrap both the arm64 and x86 binaries together. For a larger app, I imagine the overall steps would be something like:

  • Build static libs / shared libs for both architectures
  • Link libraries with executables for both arm64 / x86 as two separate packages
  • Use lipo tool to create a universal binary, then package shared lib together

What about interpreted languages like Python/Ruby/Bash?

For bash scripts, or interpreted languages like Python / Ruby, you can specify the target architecture by using the arch command like so: arch -x86_64 <COMMAND> or arch -arm64 <COMMAND>

(base) ➜  Tmp arch -x86_64 bash test.sh
Current architecture is i386

(base) ➜  Tmp bash test.sh
Current architecture is arm64

What if you use arch with the wrong architecture type? You will get a Bad CPU type error:

(base) ➜  Tmp arch -x86_64 ./hello_arm
arch: posix_spawnp: ./hello_arm: Bad CPU type in executable

Anaconda on M1

Getting Anaconda python distribution working on M1 is suprisingly easy (the x86_64 version), just launch the installer shell scirpt via the arch -x86_64 prefix, then everything works as if it’s native.

conda and conda-forge will also work out of the box assuming the host machine is x86_64 architecute, for example, I installed the open source mixed integer linear program solver cbc through conda-forge via one line as follows:

conda install -c conda-forge coincbc

# after completion
(base) ➜  Tmp which cbc
/Users/bolu/miniconda3/bin/cbc
(base) ➜  Tmp file /Users/bolu/miniconda3/bin/cbc
/Users/bolu/miniconda3/bin/cbc: Mach-O 64-bit executable x86_64

Of course, this comes with performance penalties of running x86_64 on ARM architecture, but I am not really running anything heavy on the Mac during development anyway, so I’m very happy to eat the performance cost for broader compatibility.

Brew on M1

Similarly, we can install the x86 version of Brew via the arch flag, here’s a stack overlfow post on the topic.

It is also my understanding that the brew team has been working on a native-arm64 port, but because it will take time for all the package maintainers and developers to support compiling to arm natively, this process will take a while, but for now, with Rosetta, the transition has been surprisingly painless. For example, the openconnect VPN tool works out of the box even when it’s compiled with architecture x86_64

(base) ➜  Tmp file /usr/local/bin/openconnect
/usr/local/bin/openconnect: Mach-O 64-bit executable x86_64

IDEs support

  • XCode 12 has full M1 support, and obviously has been optimized for Apple Silicon. It is simply a pleasure to use. However, the projects that I work with are usually not using xcodeproj build system. My current workaround is to use CMake to generate a xcode project, and then open the project in XCode to develop.
  • Jetbrains 2020.3 release of various IDEs now support Apple Silicon - I have not used them yet, since CLion has been known to be very power hungry. I tried the Rosetta2 CLion on the Developer Transition Kit Mac Mini, and it was really painful and unuseble for a medium sized project. I am sure with a native ARM version, this will get better. But for now, I’ve opted for lighter weight editors such as vim and xcode for more complicated projects.
  • VSCode - I use vscode for everything non C++ and the support has been really good. The Insiders edition now has ARM support, and the essential remote development extensions that make VSCode so awesome is now available!

Docker support

In my typical workflow, I don’t really use Docker that much as an interactive development environment, we usually build our packages in Docker or deploy the final application through Docker. So I have not tested docker compatibiltiy myself. But it does appear that the Docker devs have made M1 support a priority, there’s even now a tech preview Docker ver. with M1 support out today - so I would probably have confidence that it should work out of the box pretty seamlessly soon in 2021.

Final thoughts

There is no question that Apple has done it again - the new Apple Silicon Macs are simply amazing consumer machines, so I would not waste any more words to hammer this point home.

For developers, the moat around x86 is disappearing faster than anyone expected due to the excellent design and execution of Rosetta2. I originally had thought that it would take at least a year of transition and integraiton. However, after testing it myself, the gap is really small. The new Macbooks are ready today for 90% of development tasks targetting x86 deployment.

Looking ahead into the near future - I expect most consumer facing library and app developers will depend on Rosetta2 for a couple of years, and then transition fully to the ARM architecture. The day where we develop and then deploy on a full ARM server such as the Ampere Altra is fast approaching. AMD, Intel should all be very, very scared right now.