Small Mac performance optimizations #472
Replies: 1 comment
|
Sorry for not properly replying soon but only today I had the chance to check your changes properly an M1 and measured them here. One thing before anything else. The branches sit on 2d38cad from August 26, and main's 54 commits past that now, so all three conflict. Small ones, a hunk or two each, nothing semantic. A rebase would save me guessing at your intent. The AAC change is the one I'm most confident about. Your CPU number came out almost the same here: 3 minute file at 192k, three runs each, 2.39s user CPU on native aac against 1.70s on aac_at. 29 percent against your 30. I also ran the actual mux command the video export builds, with -c:v copy and fragmented mp4 to a pipe, since that felt like the place a framework encoder might get fussy. It didn't. And the bundled ffmpeg here (8.1.1-tessus) does carry aac_at, so that part holds on the binary that ships. MPS flag, yes, taking it. The chain you describe is what the code does, a nonzero exit sends the whole job back through the CPU path, and setting it on the env you hand Popen is the only place it can work. Thanks for saying outright that you saw zero fallbacks. Easy thing to leave out. Prewarm I want too, but I couldn't reproduce the number and I'd rather say so than quietly ship it. The mechanism's fine, I checked that first. The worker does load the model before it reads stdin, so the overlap is real, and it's inside the pipeline semaphore so nothing spawns twice. But measuring your metric here, dispatch to first progress line on a 3 minute source, I get 1.27s total. 0.83 to worker ready, 0.20 in load_track, 0.24 to the first progress line. So the most prewarm can hide on this machine is under a second. My guess is your metric spans load_track and the first GPU dispatch, which prewarm can't touch, and that the 9.9 to 18 was a cold page cache. That's plausible, site-packages here is 953 MB with torch 323 of it. I couldn't evict the cache to check without purging the whole machine, so I'm not calling your number wrong. If you've got a cold boot reading of worker ready on its own, that settles it. I want the patch either way. I just don't want to write "8 to 16 seconds" in a changelog if it's nearer one. Two things on that branch I'd rather you decide than fix. prewarm() has no platform gate, so it runs on Linux, Windows and Docker too. Your summary says none of the three touch Windows or Linux. I looked at what it costs there and it's mild: the model loads to CPU and only moves to the device inside apply_model, so no VRAM is held during the download, which was the way this could have hurt CUDA users. What does change is 351 MB of RSS held from the start of the download rather than after it. Nothing on a desktop, something in a memory capped container. Honestly I suspect the win is bigger on CUDA and Windows than on macOS, so I lean toward leaving it unconditional. I just want that to be a choice. The other is cancellation. Every _kill_worker call lives inside _run_demucs. Cancel during the download and the prewarmed worker never reaches any of them, so it just sits there. I don't think it's harmful, a worker that never ran a job has clean state and gets reused. But it's a new resting state for the thing and I'd rather agree to it on purpose. Nothing on the test side was yours, by the way. I saw two failures, they fail on unmodified main too. My ffmpeg here only has the experimental native vorbis rather than libvorbis. Ruff and format were clean on all three. Open them whenever suits you. The AAC and MPS ones I can take quickly. Disclaimer : AI used to sumarize my notes but in essence it contains 70% a well resumed text. |
Uh oh!
There was an error while loading. Please reload this page.
Hi! I went looking for Mac/Apple Silicon optimizations for StemDeck and ended up with three small, independent, tested changes. PR creation here is currently limited to collaborators, so I can't open them directly - they live on public branches of my fork, each with a full write-up, tests, docs, and on-device measurements. Posting here per CONTRIBUTING's "Questions" pointer to ask how you'd like to receive them.
Test machine: MacBook Air, Apple M2 (4P+4E), 24 GB, macOS 15.6, packaged arm64 build (Python 3.12.13, torch 2.6.0).
1.
perf:load the demucs model while the source is still downloadingSpawns the persistent worker (#309) at the top of the pipeline so interpreter start + torch import + model load overlap the yt-dlp/ffmpeg/analyze stages instead of serializing after them. Measured
separate_startup(dispatch → first demucs progress line):That's ~8-16 s off the first job after every app launch, trending to zero whenever download + analyze outlast the model load. Warm-worker reuse across consecutive jobs is unchanged.
→ full write-up · branch
claude/demucs-worker-prewarm-dhafc52.
fix:spawn mps workers withPYTORCH_ENABLE_MPS_FALLBACK=1Currently, one operation in the MPS backend does not terminate the worker process, and the #276 fallback then re-runs the entire job on the CPU, which takes three to five times longer. With this flag enabled, only that specific operation will run on the CPU. To be clear about the scope: using torch 2.6 and
htdemucs_6stoday, I observed zero fallbacks, meaning full MPS operation coverage. Therefore, this is a safeguard against a future torch update or aSTEMDECK_DEMUCS_MODELoverride, and does not provide a speed increase in the current configuration. The flag is only applied formpsprocesses initiated viasetdefault; it has no effect on Windows, Linux, or CUDA.→ full write-up · branch
claude/mps-op-fallback-dhafc53.
feat:prefer Apple's AudioToolbox AAC (aac_at) for MP4 export on macOSThe bundled macOS ffmpeg carries
aac_at; probing for it and using it gets audibly better AAC at the same 192k (per the public listening-test literature) at ~30% less CPU. A quality win, not speed - measured wall-clock is a wash at 60-70x realtime. Nativeaaceverywhere else and on any probe failure.→ full write-up · branch
claude/macos-aac-at-export-dhafc5All three: your CI gates run locally (ruff check/format on
app/ tests/, full pytest - 833/832/834 passing -bash -n run.sh, bandit-llclean), Conventional Commits, README docs updated, no Windows/Linux behavior changes. The fork'smainis a clean ancestor of yours, so the branches apply directly.Disclosure: I developed these materials using Claude Code. Every commit includes the co-author trailer. I verified all work on the machine mentioned above according to the process described.
Please lemme know how you would like to receive these changes. I can open formal draft pull requests if you increase the limit or add me as a collaborator. Alternatively, you can pull or cherry-pick the branches directly. I won't force-push, so the branches will remain stable. You may also decline any of these suggestions, natch.
All reactions