Using gcc on Mac Silicon (M*)

Just a data point. Apple replaced GCC with Clang some years ago. GCC produces faster binaries for SWI-Prolog. GCC is rumoured to be better in optimizing large functions and the Prolog virtual machine is a giant function. The SWI-Prolog VM can also be compiled (due to the work by @dmchurch) implementing each VM instruction as a function. This introduces function call overhead. On Clang, both versions have roughly the same performance.

When the Apple M1 came out, Apple Clang was the only available compiler. At some point Macports came with GCC (I think 11). The gcc version suffered from stability issues and the performance difference was small. I just tested with the latest GCC (15). Now the default benchmark set runs at an average of 0.063 sec using Clang and 0.043 using GCC. Unlike on x86_64, using PGO (Profile Guided Optimization) or not makes little difference (0.043 (PGO) vs 0.045). Tests on Macbook Air M1.

Unfortunately GCC cannot produce “fat” binaries, which makes it impractical to use GCC for releases :frowning:

So, if you use MacOS, compile from source and care about performance, use GCC!

2 Likes

The bottom line seems to be that Clang is required to produce fat binaries but is less performant than GCC on both Intel and Apple silicon CPU’s.

Currently, for stable releases only, there is an Intel GCC build for MacOS. Is it worth considering a similar “slim binary” for stable versions running on Apple silicon? This would be a useful “stake in the ground” for those evaluating whether they need to build from scratch, and might benefit those who aren’t in a position to do their own MacOS builds (he says, raising his hand).

That is one line of thinking. I’m not sure. One of the problems is that the not-signed version of the bundle raises a lot of security alerts. That must be solved by splitting SWI-Prolog into a Framework (library) and application. I’m still hoping someone can help getting that done. I’m ok paying for an Apple developers status to do the actual signing. I don’t know how to do the split giving the CMake/CPack based build and bundle generation.

That is of course a bit of a side issue, but part of the whole picture getting a good version for MacOS. Other things on the table:

  • Get Homebrew to include xpce/swipl-win. I’m trying to get into contact. So far without luck.
  • Get Both Homebrew and Macports into using gcc, either as default or variant. The Macport maintainers are very cooperative, so that can probably be done.
  • Somehow neither Macports nor Homebrew provide a cross compiler version of gcc to generate x86_64 code on Apple Silicon. They do have several cross compilers, including x86_64, but building ELF executables. I do not know the purpose of that. I think I’m missing something. If we had that, we could build a performant fat binary by building both for arm64 and x86_64 and then combine the two using the Apple lipo tool. That requires a bit of scripting, but that should not be too hard. Currently I do the same for the dependencies as some of them cannot be cross-compiled. So, I build on an Intel and M1 mac and then combine them.

Help is much appreciated!

This has a solution:

  • Create a new instance of Macports using a different installation prefix
  • Set in <prefix>/etc/macports/macports.conf build_arch to x86_64
  • Run port selfupdate using this port instance
  • Run port install gcc15 using this port instance
  • Wait (about 15 hours!)
  • Now we have a GCC that is an x86_64 binary and that generates x86_64 code. The first is not ideal as it runs under Rosetta, slowing it down. It does the job though :slight_smile:

Now we

  • Build using this gcc in build.bundle-x86_64
  • Build using native arm64 Macports gcc-15 in build.bundle
  • Run a script that uses lipo to add each x86_64 binary to the corresponding arm64 binary
  • Run cpack

Et voilà … We have a universal binary installer that contains binaries that are about 40% faster than using Apple’s compilers :slight_smile:

I have uploaded this file to SWI-Prolog downloads. Please test and report problems!

4 Likes

Thank you!
The newest version runs everything without issue (so far) on my M3 MacBook.

Minimal testing but downloads and runs on M3 laptop and x86 (2013 Mac Pro).

Performance is about the same as as 10.0.0-Intel version on x86 (as expected) but considerably faster on M3 on a sample of 1 clpBNR global minimization test, but I’m pretty confident that will hold up.

One caveat: I’m only testing the command line version (swipl from the bundle), having abandoned the GUI bundled app a while back.

So this is great; simplifies my process (single release for all) and I can go back to using development stream on Intel for day-today work. Looks a bit complicated from your end but I assume that can all be automated.

Thanks.

I don’t expect subtle problems. Merely issues in (dynamic) linking and such, causing the system not to start or not to load specific extensions. GCC on arm64 is around much longer and while the first release of GCC for the M1 had some issues I assume that has all been resolved.

It is all in scripts/make-distribution. The only downside is that a release build now takes about 8 minutes on my M1. Before that was about 1 minute. That is mainly because running gcc under Rosetta makes it start a lot slower, which notably makes the single core configure step slow. The good part is that I think I can retire my Intel MacBook air :slight_smile: