Skip to content

Convert as much at once as the Mac can stand, and no more - #28

Merged
thousandflowers merged 5 commits into
mainfrom
feat/batch-engine
Sep 5, 2026
Merged

thousandflowers merged 5 commits into
mainfrom
feat/batch-engine

Conversation

@thousandflowers

Copy link
Copy Markdown
Owner

One number decided how much ran at once, and it was wrong in both directions: two videos at a time is one too many, because the media engine is a single piece of hardware and asking it for two only makes heat, while two images at a time is far too few on a machine with ten cores.

The limit is now per kind of work, computed from the cores, the memory and whether this is Apple silicon — images bounded by memory, since a decoded photograph is a quarter of a gigabyte and eight at once is how a batch takes a Mac down. Nothing here decides which chip does the work: Core Image goes to the GPU, Vision to the neural engine, VideoToolbox to the media engine, and Apple routes that without being asked.

It backs off mid-batch: a warm Mac, Low Power Mode or memory pressure halves the limits or drops them to one, and they come back when the pressure does not. The screen says so rather than just getting slower.

Pause stops new files starting and lets the ones in flight finish — a native encode cannot be frozen halfway and a button claiming otherwise would be lying. Cancel now reaches one file as well as all of them, and reaches a subprocess: a cancelled conversion used to drop the task and leave ffmpeg encoding a film nobody was waiting for.

Two bugs the tests caught on the way: two waiters could be let in for one slot, because a slot was counted when the waiter woke up rather than when it was given; and stopping a row that had not started yet marked it cancelled on screen and converted it anyway.

Not built, deliberately: a software/high-quality encode toggle. AVFoundation exposes no software-encoder switch — VideoToolbox is already the default path and the quality choice is the export preset. A toggle that commutes nothing would be worse than none.

Test plan

  • swift test — green
  • 13 tests: limits per workload, scaling with mocked cores/memory, the limit held under 10 racing tasks, one kind not blocking another, thermal and memory pressure shrinking and recovering, pause holding new starts, cancel leaving the original intact with no scratch behind, cancel-per-file leaving siblings alone
  • Real 12-file batch through the CLI

🤖 Generated with Claude Code

https://claude.ai/code/session_018H9CZxWumc3ryumgEHd578

thousandflowers and others added 5 commits September 5, 2026 23:10
One number decided how much ran at once, and it was wrong in both
directions: two videos at a time is one too many, because the media
engine is a single piece of hardware and asking it for two only makes
heat, while two images at a time is far too few on a machine with ten
cores. The limit is now per kind of work, computed from the cores, the
memory and whether this is Apple silicon - images bounded by memory,
since a decoded photograph is a quarter of a gigabyte and eight at once
is how a batch takes a Mac down.

Nothing here decides which chip does the work. Core Image goes to the
GPU, Vision to the neural engine, VideoToolbox to the media engine, and
Apple routes that without being asked.

It backs off while a batch is running: a warm Mac, low power mode or
memory pressure halves the limits or drops them to one, and they come
back when the pressure does not. The screen says so rather than just
getting slower.

Pause stops new files starting and lets the ones in flight finish - a
native encode cannot be frozen halfway and a button claiming otherwise
would be lying. Cancel now reaches one file as well as all of them, and
reaches a subprocess: a cancelled conversion used to drop the task and
leave ffmpeg encoding a film nobody was waiting for.

The test caught a real bug on the way: two waiters could be let in for
one slot, because a slot was counted when the waiter woke up rather than
when it was given.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018H9CZxWumc3ryumgEHd578
Per-file cancel reached the coordinator, which cancels a task that is
running. A row still waiting its turn has no task yet, so it was marked
cancelled on screen and then converted anyway a moment later.

The batch is told instead: the rows somebody stopped are asked about
after the wait and before the work.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018H9CZxWumc3ryumgEHd578
@thousandflowers
thousandflowers merged commit 0e9d31e into main Sep 5, 2026
1 check failed
@thousandflowers
thousandflowers deleted the feat/batch-engine branch September 5, 2026 21:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant