<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Tommaso Coviello — notes</title><description>Projects and notes on what I build and learn.</description><link>https://kovdev.me/</link><language>en</language><item><title>The Work That Disappears</title><link>https://kovdev.me/notes/the-work-that-disappears/</link><guid isPermaLink="true">https://kovdev.me/notes/the-work-that-disappears/</guid><description>The beauty of mathematical optimization: finding better answers, removing unnecessary work, and knowing what a program must preserve.</description><pubDate>Mon, 14 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;the-work-that-disappears&quot;&gt;The Work That Disappears&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;On mathematical optimization, programming, and the pleasure of understanding what is necessary.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I find it difficult to leave a repeated calculation alone. Once I have noticed that its answer is already available somewhere, I start wondering why the program needs to ask again.&lt;/p&gt;
&lt;p&gt;That instinct is good at finding puzzles. It is less reliable at finding bottlenecks. I can spend an hour removing work that costs almost nothing, or make a piece of code shorter and leave it harder to understand.&lt;/p&gt;
&lt;p&gt;The improvements I enjoy most change what I can explain about a program. I can point to a set of possibilities it no longer explores and say why none of them can help. I can account for an intermediate result that no longer needs to exist. There is less uncertainty in the design, as well as less work in the execution.&lt;/p&gt;
&lt;p&gt;A smaller running time matters. But so does understanding what made it possible.&lt;/p&gt;
&lt;h2 id=&quot;what-a-better-answer-owes-us&quot;&gt;What a better answer owes us&lt;/h2&gt;
&lt;p&gt;Before comparing two implementations, we have to decide what counts as an improvement. Average running time, the longest acceptable delay, and memory use are different objectives. The output may have to remain identical; the inputs we care about may be only part of the space the program accepts. A faster implementation that silently abandons a requirement has changed the question.&lt;/p&gt;
&lt;p&gt;Mathematical optimization makes those commitments explicit: here are the choices, here is how we compare them, and here are the constraints a choice must satisfy. Once those are written down, &lt;em&gt;better&lt;/em&gt; becomes a claim we can examine. &lt;a href=&quot;#source-1&quot;&gt;[1]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Consider ten units to distribute between two destinations, with this cost model:&lt;/p&gt;
&lt;p&gt;Minimize &lt;var&gt;x&lt;/var&gt;² + 4&lt;var&gt;y&lt;/var&gt;², subject to &lt;var&gt;x&lt;/var&gt; + &lt;var&gt;y&lt;/var&gt; = 10 and &lt;var&gt;x&lt;/var&gt;, &lt;var&gt;y&lt;/var&gt; ≥ 0.&lt;/p&gt;
&lt;p&gt;Equal shares cost 125. We could try other allocations and see how far the number falls. The constraint gives us a more conclusive route: for every feasible allocation, we can rewrite the cost as&lt;/p&gt;
&lt;p&gt;&lt;var&gt;x&lt;/var&gt;² + 4&lt;var&gt;y&lt;/var&gt;² = 80 + (&lt;var&gt;x&lt;/var&gt; − 4&lt;var&gt;y&lt;/var&gt;)² / 5.&lt;/p&gt;
&lt;p&gt;The useful part of that square is its sign. It cannot be negative, so no feasible allocation can cost less than 80. Choosing &lt;em&gt;x&lt;/em&gt; = 8 and &lt;em&gt;y&lt;/em&gt; = 2 makes the square zero and reaches the bound.&lt;/p&gt;
&lt;p&gt;We now have both an answer and a reason to stop. Finding an allocation that costs 80 would, on its own, leave open the possibility of something better. The lower bound closes that possibility. It rules out an entire region of the search without asking us to visit it.&lt;/p&gt;
&lt;p&gt;Convex optimization develops this kind of reassurance further: when the objective and feasible set are convex, every local minimum is global. Duality gives us another way to certify an answer: a valid lower bound proves a feasible solution optimal when their values agree. Neither idea makes every optimization problem easy. Each tells us what structure would let us reach a definite conclusion. &lt;a href=&quot;#source-1&quot;&gt;[1]&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-the-future-needs-to-remember&quot;&gt;What the future needs to remember&lt;/h2&gt;
&lt;p&gt;Finding an optimal answer and optimizing the program that finds it are different tasks. A scheduling problem lets us watch them meet.&lt;/p&gt;
&lt;p&gt;Each job has a fixed start, a fixed end, and an integer value. Only one job may run at a time, and we want the compatible selection with the greatest total value. A job ending at time five may be followed by one starting at time five: the intervals include their start and exclude their end.&lt;/p&gt;
&lt;p&gt;Taking the most valuable job first is tempting. But a job running from zero to five and worth ten loses to two smaller jobs: zero to three for five, then three to five for six. We could settle the matter by checking every subset. With &lt;em&gt;n&lt;/em&gt; jobs, that means 2ⁿ candidates.&lt;/p&gt;
&lt;p&gt;The way out starts with an order. Sort the jobs by finishing time, and consider the last job in the list. If we take it, every earlier job that could precede it lies in a compatible prefix: all the jobs ending no later than it starts. We only need the best schedule within that prefix. If we leave it out, we need the best schedule among the remaining jobs.&lt;/p&gt;
&lt;p&gt;Numbering the sorted jobs from one, let &lt;code&gt;best[j]&lt;/code&gt; be the largest total value available among the first &lt;code&gt;j&lt;/code&gt; jobs. Let &lt;code&gt;p(j)&lt;/code&gt; be the number of earlier jobs ending no later than job &lt;code&gt;j&lt;/code&gt; begins. The two choices become:&lt;/p&gt;
&lt;figure class=&quot;code&quot;&gt;&lt;div class=&quot;code-body&quot;&gt;&lt;pre class=&quot;astro-code plate&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;best[0] = 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;best[j] = max(best[j - 1], value[j] + best[p(j)])&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;This is the standard weighted interval scheduling recurrence. Its justification is exhaustive without being an exhaustive search: every solution either includes job &lt;code&gt;j&lt;/code&gt; or excludes it, and both cases lead to smaller problems of the same form. &lt;a href=&quot;#source-2&quot;&gt;[2]&lt;/a&gt; &lt;a href=&quot;#source-3&quot;&gt;[3]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The interesting compression is in what we no longer ask about the past. For deciding whether to take job &lt;code&gt;j&lt;/code&gt;, a compatible earlier schedule matters through the value it contributes. We do not need every history that could produce that value. We can recover a particular history afterward, when we reconstruct the chosen schedule. &lt;a href=&quot;#source-3&quot;&gt;[3]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The implementation below returns both the optimal value and a schedule achieving it. Sorting and binary searches take O(&lt;em&gt;n&lt;/em&gt; log &lt;em&gt;n&lt;/em&gt;) time; the recurrence and reconstruction take O(&lt;em&gt;n&lt;/em&gt;). Storage is O(&lt;em&gt;n&lt;/em&gt;), under the usual model that treats integer comparisons and arithmetic as constant-cost operations. &lt;a href=&quot;#source-2&quot;&gt;[2]&lt;/a&gt;&lt;/p&gt;
&lt;details&gt;&lt;summary&gt;Full Python implementation, including schedule reconstruction&lt;/summary&gt;&lt;p&gt;Time is measured in integer ticks, and values are integers too. Negative values are allowed; selecting nothing is valid. Python’s &lt;code&gt;bisect_right&lt;/code&gt; includes jobs whose end equals the next start, matching the interval convention above. &lt;a href=&quot;#source-4&quot;&gt;[4]&lt;/a&gt;&lt;/p&gt;&lt;figure class=&quot;code&quot;&gt;&lt;div class=&quot;code-body&quot;&gt;&lt;pre class=&quot;astro-code plate&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;from bisect import bisect_right&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;from collections.abc import Iterable&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;from dataclasses import dataclass&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;@dataclass(frozen=True)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;class Job:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    start: int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    end: int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    value: int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    def __post_init__(self) -&amp;gt; None:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;        if any(type(x) is not int for x in (self.start, self.end, self.value)):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;            raise TypeError(&lt;/span&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;&amp;quot;Job fields must be integers.&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;        if self.start &amp;gt;= self.end:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;            raise ValueError(&lt;/span&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;&amp;quot;A job must end after it starts.&amp;quot;&lt;/span&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;def optimal_schedule(jobs: Iterable[Job]) -&amp;gt; tuple[int, list[Job]]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    ordered = sorted(jobs, key=lambda job: job.end)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    ends = [job.end for job in ordered]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    previous: list[int] = []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    best = [0]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    for i, job in enumerate(ordered):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;        # This prefix length is also an index into best.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;        prefix = bisect_right(ends, job.start, 0, i)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;        previous.append(prefix)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;        best.append(max(best[-1], job.value + best[prefix]))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    chosen: list[Job] = []&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    i = len(ordered)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    while i:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;        if best[i] == best[i - 1]:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;            i -= 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;        else:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;            chosen.append(ordered[i - 1])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;            i = previous[i - 1]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    chosen.reverse()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    return best[-1], chosen&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;jobs = [Job(0, 3, 5), Job(3, 5, 6), Job(0, 5, 10), Job(5, 6, 2)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;score, schedule = optimal_schedule(jobs)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;assert score == 13&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;assert schedule == [jobs[0], jobs[1], jobs[3]]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;The example adds a fourth job, from five to six and worth two. The best schedule takes the two shorter jobs followed by this fourth job, for a total value of thirteen.&lt;/p&gt;&lt;/details&gt;
&lt;p&gt;Forgetting is safe only after we have identified what the future can depend on. Add a rule that consecutive jobs require setup time depending on their identities, and a prefix’s best value no longer tells us enough. We would also need to know which job came last. The state would have to change.&lt;/p&gt;
&lt;h2 id=&quot;an-order-worth-keeping&quot;&gt;An order worth keeping&lt;/h2&gt;
&lt;p&gt;The scheduler uses finishing-time order to make compatible prefixes easy to find. Sometimes an order is useful often enough that we choose to maintain it in advance.&lt;/p&gt;
&lt;p&gt;Consider a SQLite query that asks for one owner’s events, ordered by start time:&lt;/p&gt;
&lt;figure class=&quot;code&quot;&gt;&lt;div class=&quot;code-body&quot;&gt;&lt;pre class=&quot;astro-code plate&quot; tabindex=&quot;0&quot; data-language=&quot;sql&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;CREATE TABLE events (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    id INTEGER PRIMARY KEY,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    owner_id INTEGER NOT NULL,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    starts_at INTEGER NOT NULL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;CREATE INDEX events_by_owner_and_start&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;    ON events(owner_id, starts_at);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;EXPLAIN QUERY PLAN&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;SELECT starts_at&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;FROM events&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;WHERE owner_id = 7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;ORDER BY starts_at;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;The &lt;code&gt;ORDER BY&lt;/code&gt; is still there. The index makes it possible to satisfy it without a separate sort.&lt;/p&gt;
&lt;p&gt;Index entries are ordered first by owner, then by start time. SQLite can locate one owner’s entries and walk through them chronologically. The requested column is already in the index, so this query need not consult the table either. It is a &lt;em&gt;covering index&lt;/em&gt;: the representation contains everything the query needs. &lt;a href=&quot;#source-5&quot;&gt;[5]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For this schema and query, a local check with SQLite 3.46.1 produces:&lt;/p&gt;
&lt;figure class=&quot;code&quot;&gt;&lt;div class=&quot;code-body&quot;&gt;&lt;pre class=&quot;astro-code plate&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;SEARCH events USING COVERING INDEX events_by_owner_and_start (owner_id=?)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;There is no separate sorting step in that plan. This is the kind of change I want to see in &lt;code&gt;EXPLAIN QUERY PLAN&lt;/code&gt;, rather than infer from how economical the SQL looks. &lt;a href=&quot;#source-5&quot;&gt;[5]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Of course, maintaining the order is work too. The index takes space and must be updated as the data changes. Whether that exchange is worthwhile depends on the workload. The requested order has become part of the representation, and we pay to keep it there. &lt;a href=&quot;#source-5&quot;&gt;[5]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The program does not need a cleverer answer to “how should I sort these rows?” It needs to notice when that question has already been answered.&lt;/p&gt;
&lt;h2 id=&quot;doing-more-arithmetic-to-finish-sooner&quot;&gt;Doing more arithmetic to finish sooner&lt;/h2&gt;
&lt;p&gt;The index earns its space by saving later work. That makes the next example awkward for my dislike of repeated calculations.&lt;/p&gt;
&lt;p&gt;A conventional dense-attention implementation stores a large matrix of interactions between sequence positions. Keeping that result sounds sensible. Using it later, however, means moving data between the GPU’s larger memory and its smaller, faster on-chip memory. Those transfers have a cost. &lt;a href=&quot;#source-6&quot;&gt;[6]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The original FlashAttention algorithm works in blocks and avoids materializing the full attention matrix in the larger memory. During training, it deliberately recomputes some intermediates instead of retrieving them. In the authors’ experiments, the extra arithmetic accompanied faster execution because it reduced expensive memory traffic. &lt;a href=&quot;#source-6&quot;&gt;[6]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The repeated calculation has earned its place: fetching a saved value can cost more than producing it again. &lt;a href=&quot;#source-6&quot;&gt;[6]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For a fixed head dimension, dense attention’s arithmetic remains quadratic in sequence length. FlashAttention has not made those interactions disappear. It has changed where data lives and when it is needed. &lt;a href=&quot;#source-6&quot;&gt;[6]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Remembering helped the scheduler; recomputing helped this GPU algorithm. The apparent contradiction disappears once we name the resource each technique saves. Counting arithmetic alone would miss the reason to prefer the second design.&lt;/p&gt;
&lt;p&gt;This is where a cost model has to resemble a machine. Some of the most expensive work may be work our equations barely mention.&lt;/p&gt;
&lt;h2 id=&quot;the-promise-inside-a-transformation&quot;&gt;The promise inside a transformation&lt;/h2&gt;
&lt;p&gt;Memory traffic is not the only place where a mathematical description can miss the machine. Even rearranging an addition can change the answer.&lt;/p&gt;
&lt;figure class=&quot;code&quot;&gt;&lt;div class=&quot;code-body&quot;&gt;&lt;pre class=&quot;astro-code plate&quot; tabindex=&quot;0&quot; data-language=&quot;python&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;a, b, c = 1e16, -1e16, 1.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;print((a + b) + c)  &lt;/span&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;# 1.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;print(a + (b + c))  &lt;/span&gt;&lt;span style=&quot;color:var(--fg)&quot;&gt;# 0.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/figure&gt;
&lt;p&gt;Over the real numbers, those expressions are equal. In ordinary binary64 floating-point arithmetic, rounding makes the grouping observable. The first expression cancels the large values before adding one. In the second, adding one to the large negative value rounds back to that value, and the final addition produces zero. &lt;a href=&quot;#source-7&quot;&gt;[7]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A transformation justified in real arithmetic therefore needs a second justification before being applied to floating-point code. LLVM makes the distinction explicit: its &lt;code&gt;reassoc&lt;/code&gt; fast-math flag permits algebraically equivalent transformations that may substantially change floating-point results. &lt;a href=&quot;#source-8&quot;&gt;[8]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The same distinction applies to FlashAttention. Computing &lt;em&gt;exact&lt;/em&gt; attention is not, by itself, a promise of identical floating-point bits. Equivalence in real arithmetic does not guarantee identical floating-point results. &lt;a href=&quot;#source-6&quot;&gt;[6]&lt;/a&gt; &lt;a href=&quot;#source-7&quot;&gt;[7]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There is nothing inherently wrong with accepting a controlled numerical difference. But the tolerance must belong to the specification. It cannot be invented after a benchmark improves.&lt;/p&gt;
&lt;p&gt;Before changing an implementation, I want to know what it owes its caller. Sometimes that is identical output. Sometimes it is an error bound. Sometimes the order of otherwise equal results matters. These details define which transformations are available to us.&lt;/p&gt;
&lt;h2 id=&quot;where-attention-belongs&quot;&gt;Where attention belongs&lt;/h2&gt;
&lt;p&gt;A tenfold speedup is easy to like. Its importance becomes clearer when we ask how much of the program it affects.&lt;/p&gt;
&lt;p&gt;Suppose the part we improve accounts for 10% of a fixed workload’s original running time. Make it ten times faster, leaving everything else unchanged and adding no overhead, and its contribution falls to 1% of the original total. The other 90% is still there. The whole program now takes 91% of its former time: an overall speedup of about 1.10.&lt;/p&gt;
&lt;p&gt;Even making that part instantaneous would leave 90% of the time untouched. The best possible overall speedup would be 1 / 0.9, approximately 1.11. &lt;a href=&quot;#source-9&quot;&gt;[9]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Amdahl’s argument about the limits of parallel execution gives the general form of this fixed-workload calculation. If a fraction &lt;em&gt;p&lt;/em&gt; of the original running time belongs to the improved part, and that part becomes &lt;em&gt;s&lt;/em&gt; times faster under the same assumptions, then:&lt;/p&gt;
&lt;p&gt;overall speedup = 1 / ((1 − &lt;var&gt;p&lt;/var&gt;) + &lt;var&gt;p&lt;/var&gt; / &lt;var&gt;s&lt;/var&gt;). &lt;a href=&quot;#source-9&quot;&gt;[9]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I appreciate this limit because it puts a boundary around my own attention. The code that keeps attracting me is not necessarily the code that deserves another evening.&lt;/p&gt;
&lt;p&gt;Knuth’s discussion of optimization makes room for both restraint and care. He warns against pursuing efficiencies in noncritical code while defending worthwhile improvements in the parts identified as important. Measurement is what lets us distinguish the two. &lt;a href=&quot;#source-10&quot;&gt;[10]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What we count in that measurement matters. Python’s &lt;code&gt;timeit&lt;/code&gt; excludes setup from the timed section and disables garbage collection by default. Those are useful conditions for some experiments, but they can leave out costs an application still has to pay. Repeated measurements help reveal timing interference; they do not make an unrepresentative workload representative. &lt;a href=&quot;#source-11&quot;&gt;[11]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For the scheduler, I would vary both the number of jobs and their pattern of overlaps, and include sorting in an end-to-end comparison. I would also check the selected schedules against exhaustive search on small inputs before timing larger ones. For the indexed query, I would count the cost of maintaining the index if writes matter to the application.&lt;/p&gt;
&lt;p&gt;“Faster” should tell a reader what was measured, what was preserved, and which costs were counted.&lt;/p&gt;
&lt;h2 id=&quot;a-stopping-condition-for-the-programmer&quot;&gt;A stopping condition for the programmer&lt;/h2&gt;
&lt;p&gt;A program can meet its requirements and still leave me thinking about another improvement. There is no obvious end to that kind of attention.&lt;/p&gt;
&lt;p&gt;Multiple objectives make the word &lt;em&gt;optimal&lt;/em&gt; more modest. A Pareto-optimal choice is one for which no feasible alternative improves an objective without worsening another. There may be many such choices; the definition alone does not select the trade-off we should prefer. &lt;a href=&quot;#source-1&quot;&gt;[1]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That leaves room for judgment. I might accept a little more memory to make a latency requirement dependable. I might keep a slower implementation because the faster one would be difficult to verify and the difference is irrelevant to its use. Those decisions need reasons, but they need not apologize for declining the smallest number on a chart.&lt;/p&gt;
&lt;p&gt;I also want room to study an optimization simply because it interests me. An evening spent understanding why a recurrence works can be worthwhile even when no application needs the result. Curiosity and engineering have different stopping conditions. Confusing them makes a learning exercise look like a delivery failure, or makes a private fascination look like a product requirement.&lt;/p&gt;
&lt;p&gt;The beauty I am looking for survives that distinction. It is there in the allocation whose lower bound meets its cost, in the scheduling state that remembers exactly enough, and in the data arrangement that makes a later operation unnecessary.&lt;/p&gt;
&lt;p&gt;After a good optimization, I want to be able to explain both the answer and the absence of the work we removed. The program still owes its caller the same promise. We have understood enough to keep it with less.&lt;/p&gt;
&lt;h2 id=&quot;sources&quot;&gt;Sources&lt;/h2&gt;
&lt;div id=&quot;source-1&quot;&gt;&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Stephen Boyd and Lieven Vandenberghe, &lt;em&gt;Convex Optimization&lt;/em&gt;, Cambridge University Press, 2004. Sections 4.1, 4.2.2, 4.7.5, and 5.5.1: formulation, local and global optima, multiple objectives, and optimality certificates. &lt;a href=&quot;https://web.stanford.edu/~boyd/cvxbook/bv_cvxbook.pdf&quot;&gt;Author-hosted book&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;source-2&quot;&gt;&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; Kevin Wayne, &lt;em&gt;Dynamic Programming I&lt;/em&gt;, lecture slides accompanying Jon Kleinberg and Éva Tardos’s &lt;em&gt;Algorithm Design&lt;/em&gt;, Princeton University; revision dated February 10, 2021. Slides 9–18: weighted interval scheduling, recurrence, reconstruction, and complexity. &lt;a href=&quot;https://www.cs.princeton.edu/~wayne/kleinberg-tardos/pdf/06DynamicProgrammingI.pdf&quot;&gt;Lecture slides&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;source-3&quot;&gt;&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; University of Washington, CSE 417, &lt;em&gt;Weighted Interval Scheduling&lt;/em&gt;, Autumn 2025. Sections 2–3: subproblems, memory structure, and reconstructing selected events. &lt;a href=&quot;https://courses.cs.washington.edu/courses/cse417/25au/readings/weighted_interval_scheduling.html&quot;&gt;Course notes&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;source-4&quot;&gt;&lt;p&gt;&lt;strong&gt;4.&lt;/strong&gt; Python Software Foundation, &lt;em&gt;bisect — Array bisection algorithm&lt;/em&gt;. The semantics and performance of binary search, including the right-hand insertion boundary. &lt;a href=&quot;https://docs.python.org/3/library/bisect.html&quot;&gt;Official documentation&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;source-5&quot;&gt;&lt;p&gt;&lt;strong&gt;5.&lt;/strong&gt; SQLite, &lt;em&gt;Query Planning&lt;/em&gt;, sections 1.6–1.7, 2.3, and 3.2; and &lt;em&gt;EXPLAIN QUERY PLAN&lt;/em&gt;, sections 1.1–1.2. Multi-column and covering indexes, ordered traversal, and temporary sorting structures. &lt;a href=&quot;https://sqlite.org/queryplanner.html&quot;&gt;Query planning&lt;/a&gt;; &lt;a href=&quot;https://sqlite.org/eqp.html&quot;&gt;plan inspection&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;source-6&quot;&gt;&lt;p&gt;&lt;strong&gt;6.&lt;/strong&gt; Tri Dao, Daniel Y. Fu, Stefano Ermon, Atri Rudra, and Christopher Ré, &lt;em&gt;FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness&lt;/em&gt;, NeurIPS 2022. Sections 3.1–3.2. &lt;a href=&quot;https://proceedings.neurips.cc/paper_files/paper/2022/file/67d57c32e20fd0a7a302cb81d36e40d5-Paper-Conference.pdf&quot;&gt;Published paper&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;source-7&quot;&gt;&lt;p&gt;&lt;strong&gt;7.&lt;/strong&gt; Python Software Foundation, &lt;em&gt;Floating-Point Arithmetic: Issues and Limitations&lt;/em&gt;. Binary representation and rounding error. &lt;a href=&quot;https://docs.python.org/3/tutorial/floatingpoint.html&quot;&gt;Official tutorial&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;source-8&quot;&gt;&lt;p&gt;&lt;strong&gt;8.&lt;/strong&gt; LLVM Project, &lt;em&gt;LLVM Language Reference Manual&lt;/em&gt;, “Fast-Math Flags,” particularly &lt;code&gt;reassoc&lt;/code&gt;. &lt;a href=&quot;https://llvm.org/docs/LangRef.html#fast-math-flags&quot;&gt;Official language reference&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;source-9&quot;&gt;&lt;p&gt;&lt;strong&gt;9.&lt;/strong&gt; Gene M. Amdahl, &lt;em&gt;Validity of the Single Processor Approach to Achieving Large Scale Computing Capabilities&lt;/em&gt;, AFIPS Spring Joint Computer Conference, 1967, pp. 483–485. The fixed-workload argument underlying the speedup calculation. &lt;a href=&quot;https://doi.org/10.1145/1465482.1465560&quot;&gt;Original publication&lt;/a&gt;; &lt;a href=&quot;https://people.cs.umass.edu/~emery/classes/cmpsci691st/readings/Conc/Amdahl-04785615.pdf&quot;&gt;2007 reprint hosted by the University of Massachusetts Amherst&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;source-10&quot;&gt;&lt;p&gt;&lt;strong&gt;10.&lt;/strong&gt; Donald E. Knuth, &lt;em&gt;Structured Programming with go to Statements&lt;/em&gt;, &lt;em&gt;ACM Computing Surveys&lt;/em&gt; 6(4), 1974, pp. 261–301, especially p. 268. Measurement, critical code, and the costs of misplaced optimization. &lt;a href=&quot;https://doi.org/10.1145/356635.356640&quot;&gt;ACM publication&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;
&lt;div id=&quot;source-11&quot;&gt;&lt;p&gt;&lt;strong&gt;11.&lt;/strong&gt; Python Software Foundation, &lt;em&gt;timeit — Measure execution time of small code snippets&lt;/em&gt;. Setup exclusions, garbage collection, and repeated measurements. &lt;a href=&quot;https://docs.python.org/3/library/timeit.html&quot;&gt;Official documentation&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;</content:encoded><category>optimization</category><category>programming</category><category>mathematics</category></item><item><title>Anthon</title><link>https://kovdev.me/projects/anthon/</link><guid isPermaLink="true">https://kovdev.me/projects/anthon/</guid><description>An AI assistant for sports mental coaching, from an initial client brief to a working product.</description><category>project</category></item><item><title>Drivewise</title><link>https://kovdev.me/projects/drivewise/</link><guid isPermaLink="true">https://kovdev.me/projects/drivewise/</guid><description>A vehicle purchase assistant that turns budget and usage preferences into explainable recommendations.</description><category>project</category></item><item><title>Amber</title><link>https://kovdev.me/projects/amber/</link><guid isPermaLink="true">https://kovdev.me/projects/amber/</guid><description>A local-first workspace for writing, connecting, and studying technical notes in ordinary files.</description><category>project</category></item><item><title>CP Lab</title><link>https://kovdev.me/projects/c-code-lab/</link><guid isPermaLink="true">https://kovdev.me/projects/c-code-lab/</guid><description>A browser workspace for practicing university C exercises, with an editor, local execution, and test feedback.</description><category>project</category></item><item><title>Physic Engine</title><link>https://kovdev.me/projects/physic-engine/</link><guid isPermaLink="true">https://kovdev.me/projects/physic-engine/</guid><description>Interactive physics simulations in C, with live vectors, graphs, and adjustable parameters.</description><category>project</category></item></channel></rss>