Friday, November 22, 2024

macos – APFS filesystem performance is very poor

I am curious why the performance of my logging routines (using O_APPEND) are so poor on MacOS, and did some testing. The performance is quite bad. Is this a known problem with APFS?

These tests are on a Mac Mini 2018 (Intel) running Sonoma 14.5.

(a) Internal SSD formatted as APFS. (And the performance is worse if the file already exists!)

bll-mac:/Volumes/Users/bll/bdj4/src$ rm -f tt-out.txt; time $HOME/bdj4/src/tt & time $HOME/bdj4/src/tt &
real    0m11.294s
user    0m0.073s
sys     0m6.144s

real    0m11.391s
user    0m0.143s
sys     0m12.304s

(b) /Volumes/spare is an External HDD formatted as HFS. (system info says the protocol is USB).

bll-mac:/Volumes/spare$ rm -f tt-out.txt; time $HOME/bdj4/src/tt & time $HOME/bdj4/src/tt & 
real    0m4.223s
user    0m0.058s
sys     0m2.681s

real    0m4.241s
user    0m0.057s
sys     0m2.680s

(c) On the same external HDD on an APFS partition.

bll-mac:/Volumes/Avail-big$ rm -f tt-out.txt; time $HOME/bdj4/src/tt & time $HOME/bdj4/src/tt &
real    0m10.238s
user    0m0.136s
sys     0m11.497s

real    0m10.239s
user    0m0.203s
sys     0m17.234s

(d) Just for comparison, on Linux, on an SSD (SATA).

bll-g7:bll$ time ./tt & time ./tt &
real    0m2.213s
user    0m0.033s
sys     0m1.200s

real    0m2.227s
user    0m0.047s
sys     0m1.228s

(e) And on MacOS, the internal SSD with O_APPEND off:
The speed here is more reasonable, though twice as slow as Linux.

bll-mac:/Volumes/Users/bll/bdj4/src$ rm -f tt-out.txt; time $HOME/bdj4/src/tt x & time $HOME/bdj4/src/tt x &
real    0m2.316s
user    0m0.039s
sys     0m1.119s

real    0m2.331s
user    0m0.118s
sys     0m3.339s

Code:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

enum {
   LCOUNT = 100000,
   TCOUNT = 8,
};

int
fileSharedOpen (const char *fname, int appflag)
{
  int         fd;
  int         flags;

  if (fname == NULL || ! *fname) {
    return -1;
  }

  flags = O_WRONLY | O_CREAT;
  if (appflag) { 
    flags |= O_APPEND;
  }

  fd = open (fname, flags, 0600);
  return fd;
}

ssize_t
fileSharedWrite (int fd, const char *data, size_t len)
{
  ssize_t rc;

  rc = write (fd, data, len);
  return rc;
}

void
fileSharedClose (int fd)
{
  close (fd);
  return;
}

void
process (int flag)
{
  int     fd;
  char    tdata [400];
  size_t  len;

  strcpy (tdata, "aaaaaaaaaaaaa");
  strcat (tdata, "bbbbbbbbbbbbb");
  strcat (tdata, "ccccccccccccc");
  strcat (tdata, "\n");
  len = strlen (tdata);
  
  fd = fileSharedOpen ("tt-out.txt", flag);
  for (int i = 0; i < LCOUNT; ++i) {
    fileSharedWrite (fd, tdata, len);
  }
  fileSharedClose (fd);
}

int
main (int argc, char *argv [])
{
  int       flag;

  flag = true;
  if (argc > 1) {
    flag = false; 
  }

  for (int i = 0; i < TCOUNT; ++i) {
    pid_t   pid;

    pid = fork ();
    if (pid != 0) {
      process (flag);
      exit (0);
    }
  }
  process (flag);
  return 0;
}

Related Articles

Latest Articles