cpu_tuple_cost, cpu_index_tuple_cost, and cpu_operator_cost are three of the constants the planner uses to price a query, and the single most useful thing to know about all three is that you should almost certainly never change them. The rest of this post is why.
PostgreSQL’s planner does not estimate query time in milliseconds. It estimates an abstract cost in dimensionless units, anchored to one reference point: seq_page_cost, the cost of reading a single 8kB page sequentially, defined as 1.0. Every other cost parameter is a number relative to that. random_page_cost defaults to 4.0, pricing a random page fetch at four sequential ones. The three CPU constants price the per-row and per-operation work: cpu_tuple_cost is 0.01 (processing one heap row), cpu_index_tuple_cost is 0.005 (processing one index entry during an index scan), and cpu_operator_cost is 0.0025 (evaluating one operator or function). All three have context user and need no restart.
You can watch them add up in any plan. When EXPLAIN prints cost=0.00..52787 for a sequential scan over a two-million-row table, that figure is the page reads (about 32,800 of them, each priced at seq_page_cost) plus two million rows at cpu_tuple_cost of 0.01, which is roughly 32,800 plus 20,000, landing on the number the planner shows. Put an aggregate on top and it charges cpu_operator_cost per row for the transition function. Every plan you have ever read is built out of this handful of constants.
Which is exactly why the three CPU constants are the wrong thing to tune. Their defaults encode ratios, not measurements: an index entry costs half what a heap row costs, an operator a quarter of one, a heap row a hundredth of a sequential page fetch. Those ratios describe how expensive CPU operations are against each other, and that relationship has barely moved in twenty-five years. A row is still cheaper to process than a page is to fetch; an index tuple is still lighter than a heap tuple. What has changed, enormously, is storage. The random_page_cost = 4.0 default assumes a spinning disk, where a seek-and-read runs about four sequential reads. On SSD or cloud block storage that ratio is wrong, often badly so, and lowering random_page_cost toward 1.1 is one of the few cost-model adjustments that is both common and usually correct.
So when a query chooses a sequential scan where you know an index scan belongs, the lever is random_page_cost, not these three. And when a plan is wrong in a way that has nothing to do with index-versus-table, the cause is almost always row-count estimates that are off, which is a statistics problem (ANALYZE, default_statistics_target, extended statistics) and not a cost-constant one. The CPU costs are global. Nudging cpu_tuple_cost to rescue a single query’s plan silently re-prices every plan on the system, and you will fix the one query in front of you and break three others you won’t discover until later.
There are real uses, but they are narrow and deliberate. Raising the CPU costs together, or more cleanly lowering both page costs, is the documented way to tell a fully-cached database that fetching a page from RAM is nearly free, which tilts the balance toward CPU-aware plans. And cpu_operator_cost genuinely matters when you are estimating the cost of expensive functions. Those are whole-system decisions made with a benchmark in hand, not knobs you reach for to coax one statement. Leave all three at their defaults. When the planner is wrong, it is almost never because a row costs 0.01 instead of 0.012; it is because the planner thinks the row count is forty when it is forty thousand.
(The Apple logo and Apple are registered trademarks of Apple Inc., in case you just got here from Mars.)