The price of dirtying a page (modifying it so it must be written back): 20 units, the dominant term of the cost model. This is deliberate: reads are cheap and repeatable, but every dirtied page becomes a write plus WAL that the whole replication chain has to absorb. A vacuum that removes a lot of dead rows dirties most pages it visits, which is why the first pass after long neglect is the slow one.
DEMO — one vacuum pass, toy table14 GB heap · 1,740,000 pages
defaults (200 · 2 ms)1 min · 154.7 MB/s
sliders15 min · 15.5 MB/s
autovacuum_vacuum_cost_limit200
cost units the worker may spend before it sleeps · default 200
autovacuum_vacuum_cost_delay20 ms
how long it sleeps · default 2 ms since Postgres 12, 20 ms before