Why This Mac Runs Slow

iMac18,3 (2017 27") · i5-7500 4 cores @ 3.4 GHz · 40 GB RAM · Radeon Pro 570 4 GB · Retina 5K 5120×2880 · macOS 13.7.8 · second pass measured 2026-09-13 18:30 CDT

Widened from the 13:56 pass, which asked only about MTG Arena. That version is archived at public/_archive/mac-slow-diagnosis-2026-09-13-1356/. Two of its rulings are revised below.

Verdict: a cold random read on the boot volume costs 17 to 23 milliseconds. The same test on an external SSD, through the identical code path, costs 0.71 milliseconds. Anything not already sitting in RAM pays a 23 to 32 times penalty. The boot volume is a Fusion Drive with a 28 GB flash tier in front of a 7200 rpm platter, and it is 93% full, so the flash tier cannot hold the working set. CPU, RAM, and heat all have headroom to spare.

measuredThe number that explains the feeling

200 files per sample, 4 KB read from each, chosen at random from trees that had not been touched. Five independent runs across three directories, each with its own seed.

Boot volume, cold
17–23 ms
per file, 44 to 60 files/sec
External SSD, cold
0.71 ms
1,412 files/sec, T7 Shield
Already in RAM
0.03 ms
36,258 files/sec
Penalty vs the SSD
23–32×
on every cold read
SampleTreeSeedms per filefiles/sec
Cold 1~/Older Downloads HUGE717.2058
Cold 2~/Older Downloads HUGE10122.9244
Cold 3~/Older Downloads HUGE20219.9050
Cold 4~/Archive30322.6844
Cold 5~/Music40416.6160

A bare 7200 rpm drive is specified around 8 to 12 ms for a random seek. The measured 17 to 23 ms sits above that, which is seek time plus queueing behind other work.

validatedThe controls, run before publishing the number

A latency figure means nothing until the instrument is shown to separate fast from slow. Three controls ran through the same Python code path.

ControlExpectationMeasuredResult
Re-read the identical 200 filesNear zero, the page cache should hold them0.03 ms/file · 36,258/sInstrument separates warm from cold by 430×
Same test on /Volumes/T7 ShieldSub-millisecond, it is a real SSD0.71 ms/file · 1,412/sConfirms the boot volume is the outlier
Same test on /Volumes/BlackBetween the two1.65 ms/file · 606/sStill 10× faster than the boot volume

causeThe boot disk is two disks, and one of them spins

diskutil info disk2 reports Fusion Drive: Yes.

Fast tier, disk0
APPLE SSD SM0032L · 28.0 GB · Solid State: Yes
Slow tier, disk1
APPLE HDD ST1000DM003 · 1.0 TB · Solid State: No · 7200 rpm platter

Boot volume, df -h /System/Volumes/Data: 872 Gi used of 957 Gi, 93% full, 69 Gi free

28 GB of flash has to cache macOS, every open app, Chrome, Firefox, Obsidian, and 40 loaded launch agents, against roughly 900 GB of data. Almost everything is cold almost all the time, and cold means the platter.

second laneThe display renders at a size the panel is not

Independent of the disk, and it costs GPU on everything continuously. system_profiler SPDisplaysDataType -json:

FieldValueMeaning
spdisplays_pixelresolution5120 × 2880The physical Retina 5K panel
_spdisplays_pixels4096 × 2304The framebuffer actually being rendered
_spdisplays_resolution2048 × 1152The logical desktop

The display sits in a scaled mode, so macOS draws the whole desktop into a 4096×2304 buffer and rescales it onto a 5120×2880 panel at a non-integer ratio. That cost is paid on every frame of every app.

MTG Arena's own log records the knock-on at launch:

Current screen dimensions of 4096x2096 are not an allowable ratio (1.954198)
so we are forcing windowed mode.


Metal RecreateSurface: surface size 3840x2160

4096×2096 is that framebuffer minus the menu bar and dock. Arena rejects the aspect ratio, refuses fullscreen, and settles on a 3840×2160 surface. Measured at 15:24 with Arena parked on its Home screen doing nothing: 125 to 138% CPU (1.3 of 4 cores) and GPU utilization bursting 78 to 99, with Graphics.GlobalQualityLevel and UnityGraphicsQuality both already at 0, the lowest the game offers.

revisedWhat the 13:56 pass got wrong, and one near miss

Ruling at 13:56Re-measuredStatus
GPU ruled out: “0 to 63%, never pinned”78 to 99 sustained at 15:24, with the scaled framebuffer identified as the causeRevised. The 13:56 sample caught it between bursts
Per-process disk I/O: UNATTRIBUTEDStill unattributed by process. Now attributed by device and by latency, which answers the question betterUpgraded, honestly incomplete
Spotlight ruled out: “idle maintenance”mds and mdworker at 0.0 to 0.1% CPU, both external drives at 0 tpsConfirmed correct. See the near miss below
Near miss worth recording. Six mdworker_shared processes were live with three sitting in uninterruptible disk wait, and syspolicyd showed 198,699 pageins against mds_stores at 45,111. That reads like Spotlight hammering the disk. A 30 second delta test refuted it: pageins across every process totalled 108 in 30 seconds, 3.6 per second, which is nothing. Those counters are cumulative since boot, not rates. Publishing the first reading would have named the wrong culprit.

ruled outWhat is not the problem

SuspectMeasured at 18:08 to 18:30Reading
Memory exhaustion19 GB unusedZero swap, zero compressor, no paging
Heat and throttlinglimit 100No thermal or performance warning ever recorded
CPU saturation49.6% idleLoad 2.49 on 4 cores, nothing above 5.4%
Spotlight indexing0.0 to 0.1%Externals at 0 tps, index not being rebuilt
Memory paging3.6 pageins/sMeasured as a 30 second delta, negligible
Time MachineRunning = 0No backup in progress

fixIn order of measured effect

  1. Free the boot volume down to roughly 80%. About 125 GB to move. Measured candidates: ~/Older Downloads HUGE 108 GB, ~/Downloads 22 GB, ~/Archive 19 GB. Destination /Volumes/Black has 174 Gi free. Per the never-delete rule these move, nothing is removed. This gives the 28 GB flash tier room to promote what is actually in use.
  2. Set the display to Default for display. Removes the continuous non-integer rescale and lets Arena go fullscreen instead of being rejected at 1.954. This enlarges text and UI across the whole Mac, so it is a judgement call, not an automatic win.
  3. Trim the launch agents. 53 plists in ~/Library/LaunchAgents, 40 currently loaded, 9 resident. Each one wakes, reads its interpreter and scripts off the platter, and competes for the same seeks.
  4. The real repair is an internal SSD. 28 GB of flash cannot cache a 900 GB working set under any management scheme. Every fix above buys margin against a structural limit.

gapStill not measured

Per-process disk I/O attribution needs fs_usage, which requires an administrator password, and sudo is not passwordless here. The device-level and latency-level attribution above does not name which process issues the cold reads. That remains UNATTRIBUTED.

Method

Second pass, 18:08 to 18:30 CDT on 2026-09-13, uptime 4h32m. Every figure is observed command output. The latency benchmark and its controls are in pagein_delta.sh and inline Python, run read-only, modifying nothing.

diskutil info disk0 disk1 disk2 · df -h · iostat -d -w 2 -c 8 disk0 disk1 disk3 disk4 · top -l 2 -o cpu · top -l 1 -n 60 -o pageins sampled twice 30 s apart · ps -axo pid,stat,%cpu,comm · memory_pressure · pmset -g therm · mdutil -s · tmutil status · launchctl list · ioreg -r -d 1 -c IOAccelerator · system_profiler SPDisplaysDataType -json · sample 28197 5 · plutil -p com.wizards.mtga.plist · du -sh