2021 MacBook Pro (14-inch) Impressions

While working from home for most of 2020 and 2021 I’ve been using my 12-core (24-thread) Mac Pro for my usual C++ development. For the eventual return to the office (which is still a rather big question mark), I didn’t want returning there and working from a laptop to be such a huge step down, so of course I ordered one of the shiny new 2021 MacBook Pros. I went for the 14-inch for portability, as I’m mostly using it in clamshell-mode at my desk, and only occasionally when traveling (remember that?).

As I’m only really stressing the CPU, the 10-core “Pro” version with the small GPU is totally adequate, and the 32GB were enough memory to load all those cores when compiling. The extra memory bandwidth of the “Max” doesn’t help much when only loading the CPU.

The display is very nice (more dimming zones than the Pro Display XDR), but I hardly notice ProMotion (up to 120Hz refresh rate).

For some benchmarks, I compiled our largely C++ work project (using a different build system to Xcode — which seems to have trouble parallelizing large builds) on all cores (including hyper-threads on the Mac Pro):

  • 2019 Mac Pro (12-core, 24-thread; 96GB): 4m54secs
  • 2021 MacBook Pro (8+2-core, 32GB, 14-inch): 3m50secs
  • 2021 MacBook Pro (8+2-core, 32GB, 14-inch) low-power mode: 4m32secs

So that’s roughly 28% faster on the laptop. The fans become audible after ~2min on full load, but they aren’t as annoying as the ones on my previous 2018 i9 Intel MacBook Pro which spun up at the slightest provocation. The new MBP’s fans sound more like low-level white noise. With low-power mode enabled (which throttles the CPU), the fans do come on as well, but only spin at a barely audible level; resulting in a slow-down of 15% compared to normal operation (and still being faster than the Mac Pro).

Using clang on macOS to compile g++ / libstdc++ compatible binaries

clang++ by default compiles for system “native” C++ library. On macOS, that is LLVM’s libc++. But what happens if happen to have a lot of binaries (on in my case) libraries compiled with g++ using its own libdstdc++ library?

Why was I doing that in the first place? Work stuff is mostly Linux and therefore uses gcc, and the Apple-provided clang that ships with Xcode.app doesn’t support OpenMP, so I ended up building a lot of stuff with a homebrew-sourced gcc. But it turns out that gcc’s OpenMP support on macOS (in particular the implementation of the synchronization primitives in libgomp) is surprisingly slow (on Linux it’s much better). I ended up with a piece of OpenMP code whose performance was so bad, I need to cross check what the cause was, and so I installed a (non-Apple) clang with which I wanted to compile the code in question, but preferably without having to rebuild all my gcc-compiled (and therefore libstdc++-using) dependencies.

The following seems to work for a homebrew-installed gcc 9.3 and llvm (clang) 10:

CXXFLAGS="-stdlib=libstdc++ -stdlib++-isystem /usr/local/Cellar/gcc/9.3.0_1/include/c++/9.3.0/ -cxx-isystem /usr/local/Cellar/gcc/9.3.0_1/include/c++/9.3.0/x86_64-apple-darwin19"
LDFLAGS="-stdlib=libstdc++ -L /usr/local/Cellar/gcc/9.3.0_1/lib/gcc/9 -L /usr/local/opt/llvm/lib"

The last part of the link flags that add the library path back to the homebrew clang are only required so that that clang‘s OpenMP support can find its libomp support library.

This works unless you used something like libstdc++‘s implementation of std::call_once, because that needs internal symbols that are declared as __thread, which with how homebrew’s gcc is configurationed specifically or gcc on Darwin behaves generally used “emulated thread-local storage” (tls), which in turn requires mangling that __thread declared symbol differently 😔. clang even knows about that (see -femulated-tls), but when I tried to use that variant (which did indeed link without complaint) I got weird malloc errors in emutls functions, and it was easier for me to replace std::call_once with a hack than figuring out where those errors came from.

Mac Pro History

  • 2006-08
    • 2.66 GHz 2×2-Core Xeon
    • 4 GB 667 MHz DDR2 ECC
    • ATI Radeon X1900 XT 512 MB
    • 500 GB Serial ATA 3 Gb/s drive
    • 16x SuperDrive DL
    • Airport Extreme & BT 2.0+EDR
  • 2013-12
    • 3.5 GHz 6-Core Xeon
    • 32 GB 1866 MHz DDR3 ECC
    • 2x AMD FirePro D700 6 GB GDDR5
    • 512 GB PCIe-based SSD
  • 2019-12
    • 3.3 GHz 12-Core Xeon
    • 96 GB 2933 MHz DDR4 ECC
    • Radeon Pro 580X 8 GB GDDR5
    • 1 TB SSD

Cores went up, (base-)speed went down slightly (although the average turbo-boosted speed should be higher). Memory per thread went up from 1 GB to 4 GB (hello huge C++ templates). The graphics card will be replaced by the AMD Radeon Pro W5700X once available.

Some Game recommendations from 2018

  • Tacoma (“walking simulator” by Fullbright (who made “Gone Home”))
  • Zero Time Dilemma series (weird time travel visual novel + escape room puzzles)
  • Iconoclasts (metroidvania with strong puzzle focus
  • Life is Strange: Before the Storm (I thought I didn’t want to go back, but turns out, I did :))
  • Q.U.B.E and Q.U.B.E. 2 (first-person 3D puzzle games)
  • What Remains of Edith Finch (“walking simulator”)
  • Undertale (RPG by Toby Fox with an unusual battle system, good story and great music)
  • Yakuza 0 (prequel to the Yakuza series, good entry-point, and probably the best game in the series)
  • God of War (2018)
  • Monster Hunter World (still a bit obtuse, but the loop worked for me until the “now for the same monsters, but harder” part – which was 40-ish hours in)
  • Destiny 2: Forsaken (base D2 was a bit of a disappointment, but Forsaken managed another “Taken King” turn-around)
  • Celeste (excellent platformer, hard but fair (and teaches what you need to be able to do), great music, and interesting story)
  • ASTRO Bot Rescue Mission (charming VR platformer)
  • Marvel’s Spider Man (a bit repetitive, but ultimately OK)EXAPUNKS
  • 7 Billion Humans (sequel to Human Resource Machine, visual programming puzzle)

More (mechanical) keyboards and Mac key remapping

After browsing too much r/MechanicalKeyboards I bought some more keyboards… 😀

One is a white TKL CODE with 65g Zealios switches
White TKL CODE with 65g Zealios switches
(which is essentially the same as my current WASDv2 except for the case color, switches and lighting), which I bought because I was interested in the Zealios switches, and this is one of the few pre-made keyboards that offer them.

The other was a birthday present to myself and is a Leopold FC660C (Topre) in Grey & Blue:Leopold FC660C (Topre) in Grey & BlueThe Leopold does not have a Mac specific mode (although you can use its DIP switches to swap the Windows and Alt key, but that doesn’t get you ⌘ to the right of the space bar).

Therefore I fiddled a bit with remapping some keys for these keyboards for macOS use. One option for the Leopold FC660C is Hasu’s Alt Controller which replaces the controller board itself with a fully programmable one. Another option is to use Karabiner to remap the keys at the software level, but I found the CPU usage of Karabiner’s karabiner_grabber process a bit too high for what it should be doing. Since macOS 10.12 there’s another way to remap keys using hidutil.

My configuration (for both keyboards) looks as follows (stored inside ~/bin/remap.sh which is run on login, based on this script):

#!/usr/bin/env bash
# https://developer.apple.com/library/content/technotes/tn2450/_index.html
# https://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-1035.41.2/IOHIDFamily/IOHIDUsageTables.h.auto.html
# https://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-1035.41.2/IOHIDFamily/AppleHIDUsageTables.h.auto.html






# WASDv2 / CODE in Mac mode
hidutil property --matching '{"ProductID":0x269, "VendorID":0x4d9}' --set "{\"UserKeyMapping\":[
{$FROM: $F13,          $TO: $MEDIA_EJECT},
{$FROM: $F14,          $TO: $MEDIA_PREV},
{$FROM: $F15,          $TO: $MEDIA_NEXT},
# Leopold FC660C
hidutil property --matching '{"ProductID":0x134, "VendorID":0x853}' --set "{\"UserKeyMapping\":[
{$FROM: $PAUSE,        $TO: $MEDIA_NEXT},

Mainly this maps the modifier keys to be more Mac-like, caps lock to escape (which means I can run the Leopold’s escape key permanently in ` / ~ mode (using Fn + Q), as well as some media keys. The links in the header-comment of the script contain lists of supported pages and usages; the page is the upper 32-bit word and the usage the lower 32-bit word.

If you want to run this on login, you can use launchd by storing a plist in ~/Library/LaunchAgents/de.maven.remap_keys.plist for example containing

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">

Happy hacking!