Mac OS X: beautiful but...

The Mac OS X (Server) operating system can't be described easily. Apple:

"Mac OS X Server starts with Darwin, the same open source foundation used in Mac OS X, Apple's operating system for desktop and mobile computers. Darwin is built around the Mach 3.0 microkernel, which provides features critical to server operations, such as fine-grained multi-threading, symmetric multiprocessing (SMP), protected memory, a unified buffer cache (UBC), 64-bit kernel services and system notifications. Darwin also includes the latest innovations from the open source BSD community, particularly the FreeBSD development community."

While there are many very good ideas in Mac OS X, it reminds me a lot of fusion cooking, where you make a hotch-potch of very different ingredients. Let me explain.

Hexley the platypus, the Darwin mascot

Darwin is indeed the open Source project around the Mach kernel 3.0. This operating system is based around the idea of a microkernel, a kernel that only contains the essence of the operating system, such as protected memory, fine-grained multithreading and symmetric multiprocessing support. This in contrast to "monolithic" operating systems, which have all of the code in a single large kernel.

Everything else is located in smaller programs, servers, which communicate with each other via ports and an IPC (Inter Process Communication) system. Explaining this in detail is beyond the scope of this article (read more here). But in a nutshell, a Mach microkernel should be more elegant, easier to debug and better at keeping different processes from writing in eachother's protected memory areas than our typical "monolithic" operating systems such as Linux and Windows NT/XP/2000. The Mach microkernel was believed to be the future of all operating systems.

However, you must know that applications (in the userspace) need, of course, access to the services of the kernel. In Unix, this is done with a Syscall, and it results in two context switches (the CPU has to swap out one process for another): from the application to the kernel and back.

The relatively complicated memory management (especially if the server process runs in user mode instead of kernel) and IPC messaging makes a call to the Mach kernel a lot slower, up to 6 times slower than the monolithic ones!

It also must be remarked that, for example, Linux is not completely a monolithic OS. You can choose whether you like to incorporate a driver in the kernel (faster, but more complex) or in userspace (slower, but the kernel remains slimmer).

Now, while Mac OS X is based on Mach 3, it is still a monolithic OS. The Mach microkernel is fused into a traditional FreeBSD "system call" interface. In fact, Darwin is a complete FreeBSD 4.4 alike UNIX and thus monolithic kernel, derived from the original 4.4BSD-Lite2 Open Source distribution.

The current Mac OS X has evolved a bit and consists of a FreeBSD 5.0 kernel (with a Mach 3 multithreaded microkernel inside) with a proprietary, but superb graphical user interface (GUI) called Aqua.

Performance problems

As the mach kernel is hidden away deep in the FreeBSD kernel, Mach (kernel) threads are only available for kernel level programs, not applications such as MySQL. Applications can make use of a POSIX thread (a " pthread"), a wrapper around a Mach thread.

Mac OS X thread layering hierarchy (Courtesy: Apple)

This means that applications use slower user-level threads like in FreeBSD and not fast kernel threads like in Linux. It seems that FreeBSD 5.x has somewhat solved the performance problems that were typical for user-level threads, but we are not sure if Mac OS X has been able to take advantage of this.

In order to maintain binary compatibility, Apple might not have been able to implement some of the performance improvements found in the newer BSD kernels.

Another problem is the way threads could/can get access to the kernel. In the early versions of Mac OS X, only one thread could lock onto the kernel at once. This doesn't mean only one thread can run, but that only one thread could access the kernel at a given time. So, a rendering calculation (no kernel interaction) together with a network access (kernel access) could run well. But many threads demanding access to the memory or network subsystem would result in one thread getting access, and all others waiting.

This "kernel locked bottleneck" situation has improved in Tiger, but kernel locking is still very coarse. So, while there is a very fine grained multi-threading system (The Mach kernel) inside that monolithic kernel, it is not available to the outside world.

So, is Mac OS X the real reason why MySQL and Apache run so slow on the Mac Platform? Let us find out... with benchmarks, of course!

The G5 as Server CPU Mac OS X versus Linux
Comments Locked


View All Comments

  • Joepublic2 - Monday, June 6, 2005 - link

    Wow, pixelglow, that's an awesome way to advertise your product. No marketing BS, just numbers!
  • pixelglow - Sunday, June 5, 2005 - link

    I've done a direct comparison of G5 vs. Pentium 4 here. The benchmark is cache-bound, minimal branching, maximal floating point and designed to minimize use of the underlying operating system. It is also single-threaded so there's no significant advantage to dual procs. More importantly it uses Altivec on G5 and SSE/SSE2 on the Pentium 4, and also compares against different compilers including the autovectorizing Intel ICC.

    Let the results speak for themselves.
  • webflits - Sunday, June 5, 2005 - link

    "From the numbers, it seems like gcc was only capable of using Altivec in one test,"

    The Altivec SIMD only supports single (32-bit) precision floating point and the benchmark uses double precision floating point.

  • webflits - Sunday, June 5, 2005 - link

    Here are the resuls on a dual 2.0Ghz G5 running 10.4.1 using the stock Apple gcc 4.0 compiler.

    [Session started at 2005-06-05 22:47:52 +0200.]

    FLOPS C Program (Double Precision), V2.0 18 Dec 1992

    Module Error RunTime MFLOPS
    1 4.0146e-13 0.0163 859.4752
    2 -1.4166e-13 0.0156 450.0935
    3 4.7184e-14 0.0075 2264.2656
    4 -1.2546e-13 0.0130 1152.8620
    5 -1.3800e-13 0.0276 1051.5730
    6 3.2374e-13 0.0180 1609.4871
    7 -8.4583e-11 0.0296 405.4409
    8 3.4855e-13 0.0200 1498.4641

    Iterations = 512000000
    NullTime (usec) = 0.0015
    MFLOPS(1) = 609.8307
    MFLOPS(2) = 756.9962
    MFLOPS(3) = 1105.8774
    MFLOPS(4) = 1554.0224
  • frfr - Sunday, June 5, 2005 - link

    If you test a database you have to disable the write cache on the disk on almost any OS unless you don't care about your data. I've read that OS X is an exception because it allows the database software control over it, and that mySql indeed does use this. This would invalidate al your mySql results except for OS X.

    Besides all serious database's run on controllers with write cache with batteries (and with the write cache on the disks disabled).

  • nicksay - Sunday, June 5, 2005 - link

    It is pretty clear that there are a lot of people who want Linux PPC benchmarks. I agree. I also think that if this is to be a "where should I position the G5/Mac OS X combination compared to x86/Linux/Windows" article, you should at least use the default OS X compiler. I got flops.c from to do my own test. I have a stock 10.4.1 install on a single 1.6 GHz G5.

    In the terminal, I ran:
    gcc -DUNIX -fast flops.c -o flops

    My results:

    FLOPS C Program (Double Precision), V2.0 18 Dec 1992

    Module Error RunTime MFLOPS
    1 4.0146e-13 0.0228 614.4905
    2 -1.4166e-13 0.0124 565.3013
    3 4.7184e-14 0.0087 1952.5703
    4 -1.2546e-13 0.0135 1109.5877
    5 -1.3800e-13 0.0383 757.4925
    6 3.2374e-13 0.0220 1320.3769
    7 -8.4583e-11 0.0393 305.1391
    8 3.4855e-13 0.0238 1258.5012

    Iterations = 512000000
    NullTime (usec) = 0.0002
    MFLOPS(1) = 736.3316
    MFLOPS(2) = 578.9129
    MFLOPS(3) = 866.8806
    MFLOPS(4) = 1337.7177

    A quick add-n-divide gives my system an average result of 985.43243.

    985. On a single 1.6 G5.

    So, the oldest, slowest PowerMac G5 ever made almost matches a top-of-the-line dual 2.7 G5 system?

    To quote, "Something is rotten in the state of Denmark." Or should I say the state of the benchmark?
  • Eug - Saturday, June 4, 2005 - link

    BTW, about the link I posted above:

    The guy who wrote that is the creator of the BeOS file system (and who now works for Apple).

    It will be interesting to see if this is truly part of the cause of the performance issues.

    Also, there is this related thread from a few weeks back on Slashdot:
  • profchaos - Saturday, June 4, 2005 - link

    The statement about Linux kernel modules is incorrect. It is a popular misconception that kernel modules make the Linux kernel something other than purely monolithic. The module loader links module code in kernelspace, not in userspace, the advantage being dynamic control of kernel memory footprint. Although some previously kernelspace subsystems, such as devfs, have been recently rewritten as userspace daemons, such as udev, the Linux kernel is for the most part a fully monolithic design. The theories that fueled the monolithic vs. microkernel flame wars of the mid-90s were nullified by the rapid ramping of single-thread performance relative to memory subsystems. From the perspective of the CPU, it take years for a context switch to occur since modifying kernel data structures in main memory is so slow relative to anything else. Userspace context switching is based on IPC in microkernel designs, and may require several context switches in practice. As you can see from the results, Linux 2.6 wipes the floor with Darwin just the same as it does with several of the BSDs (especially OpenBSD and FreeBSD4.x) and its older cousin Linux 2.4. It's also anyone's guess whether the Linux 2.6 systems were using pthreads (from NPTL) or linuxthreads in glibc. It takes a heavyweight UNIX server system, which today means IBM AIX on POWER, HP-UX on Itanium, or to a lesser degree Solaris on SPARC, to best Linux 2.6 under most server workloads.
  • Eug - Saturday, June 4, 2005 - link

    Responses/Musings from an Apple developer.


    They claim that making a new thread is called "forking". No, it’s not. Calling fork() is forking, and fork() makes processes, not threads.

    They claim that Mac OS X is slower at making threads by benchmarking fork() and exec(). I don’t follow this train of thought at all. Making a new process is substantially different from making a new thread, less so on Linux, but very much so on OS X. And, as you can see from their screenshot, there is one mySQL process with 60 threads; neither fork() nor exec() is being called here.

    They claim that OS X does not use kernel threads to implement user threads. But of course it does - see for yourself.
    /* Create the Mach thread for this thread */
    PTHREAD_MACH_CALL(thread_create(mach_task_self(), &kernel_thread), kern_res);

    They claim that OS X has to go through "extra layers" and "several threading wrappers" to create a thread. But anyone can see in that source file that a pthread maps pretty directly to a Mach thread, so I’m clueless as to what "extra layers" they’re talking about.

    They guess a lot about the important performance factors, but they never actually profile mySQL. Why not?
  • orenb - Saturday, June 4, 2005 - link

    Thank you for a very interesting article. A follow up on desktop and workstation performance will be very appreciated... :-)

    Good job!

Log in

Don't have an account? Sign up now