A chain of four arithmetic operators runs over a ten-million-row column - how many full-length results get allocated?
answer
- one result per operator
- count the operators, not the columns
- the middles are throwaway but real
- peak is what matters, not the total
- deferred evaluation never builds them
basics
~20 sUnder eager evaluation, four: each operator returns a complete ten-million-value result before the next operator reads it, so three intermediates exist on the way to the answer. Under deferred evaluation the chain is one recorded expression and the middles need never be built.
solid answer
~50 sIt depends on the evaluation strategy, and that is the whole answer. Under **eager** evaluation - where each operator runs as soon as it is written and returns a complete result - a chain of k operators leaves k **full-length temporaries** behind it: the freshly allocated whole-length result each operator produces before the next one consumes it. Four operators over ten million eight-byte values is roughly 320 MB of allocation, three of those four buffers being throwaway middles. Under **deferred** evaluation - where writing the expression only records what is to be done and nothing runs until the answer is asked for - the chain is one recorded expression that can be fused into a single pass, and the intermediates never exist. The in-place operator forms are a third case: they write into a buffer that already exists and allocate nothing.
code
pseudocode · 8 lines# eager: each operator completes before the next one reads it
t1 = price * quantity # full-length result 1
t2 = t1 - discount # full-length result 2
t3 = t2 * tax_rate # full-length result 3
total = t3 + shipping # full-length result 4 - the one you wanted
# the same expression written on one line allocates the same four;
# it just does not give you a name for the first threego deeper
Know that each arithmetic step over a column produces a whole new column of results, and that a chain of steps therefore produces several of them rather than editing one in place.
Count them: k operators, k full-length results under eager evaluation. Then say what a deferred strategy changes, and why the claim is false without that condition attached.
Separate total allocation from peak. Know which things keep a middle alive - a name, a branch - and know that the chain also costs k traversals of the data, not just k buffers.
The call is whether the team's default expression style should assume eager or deferred semantics, since that decides whether long chains are a memory hazard to police or a free optimisation opportunity.
## Counting the allocations An expression like `((price * quantity) - discount) * tax_rate + shipping` is four operators. Under **eager evaluation** - the commonest default in this family, where each operator runs the moment it is written and hands back a complete result - each operator must produce something the next operator can read. That something is **a full-length temporary**: a freshly allocated buffer the full length of the column, holding the complete result of that one step. So the count is one per operator. Four operators, four full-length results, of which the last is the answer you wanted and the other three are middles that exist only to be consumed. Over ten million values at eight bytes each, that is about 80 MB per buffer and roughly 320 MB of allocation in total for a single line of code. ## What is live at the peak matters more than the total The total allocated is not the number that kills a process - the **peak** is. Under a straightforward eager evaluation, the middles are consumed one after another, so an implementation can release each one as soon as the next operator has read it. That means the peak tends to be two or three full-length buffers at once, not all four, plus the original operands which are still referenced by your program. Two things push the peak the wrong way: - **Keeping a name on an intermediate.** The moment you assign a middle step to a variable you will use later, it cannot be released, and it is live for the rest of its scope. - **A branching expression.** If two different steps both consume the same intermediate, that intermediate stays live until the later of them runs. This is the concrete cost of the column-wise habit, and it is the honest counterweight to the speed argument: the loop you replaced held one record at a time, and the chain holds whole columns. ## Three evaluation strategies, three counts | Strategy | Intermediates for a k-operator chain | When the work happens | What you give up | |---|---|---|---| | Eager | k full-length results | as each operator is written | peak memory, and k passes over the data | | Deferred | none needed - the chain is one recorded expression | when the answer is actually asked for | the ability to inspect a middle step as you go | | In-place forms | none - writes into an existing buffer | immediately | safety: every other reference to that buffer sees the write | Attach the count to the strategy, not to the operator. "A chain of k operators allocates k full-length temporaries" is the right cost model for the commonest design and is simply false of a design that records the expression and evaluates it once. Saying which one you mean is the difference between an answer that travels and an answer that only works on the tool you learned first. ## The passes, not just the buffers There is a second cost that rides along with the intermediates, and it is easy to miss. Under eager evaluation, each operator is its own pass over the full length of the data. Four operators means four traversals of ten million values - four times reading a buffer in, four times writing a buffer out. A fused evaluation reads the operands once, does all four operations per position while the values are already to hand, and writes one result. That is why deferred designs can be faster even when memory is not the constraint: they are not doing less arithmetic, they are doing far less moving of data. ## What to do about it 1. **Notice the chain length before you assume it is free.** One operator over a wide column is unremarkable; six operators over a column that is already most of your memory is a different proposition. 2. **Do not name intermediates you do not need.** An unnamed middle can be released; a named one cannot. 3. **Reach for the in-place form deliberately** where a buffer is genuinely private to the code you are writing, and never where anything else holds a reference to it. 4. **If your tool offers a deferred form, the long chain is exactly where it pays.** A single operator gains nothing from being recorded rather than run; a six-operator chain gains a great deal. ## What a strong answer sounds like "Under eager evaluation, k operators means k full-length results, and although the middles can be released as they are consumed, the peak is still a few whole columns rather than one record. Under deferred evaluation the chain is recorded and fused, so the middles never exist and the data is traversed once." Naming both, and saying which is the common default, is the answer.
- If three of the four buffers are throwaway, why is the peak not four full columns?Because each middle can be released as soon as the next operator has read it, so under a simple linear chain two or three are live at once rather than all four. What defeats that is naming an intermediate you will use later, or a branching expression where two steps consume the same middle.
- Does writing the whole chain on one line instead of four reduce the allocations?No. The operators are the same operators and each still produces a complete result under eager evaluation. What one line changes is only that the middles are unnamed, which makes it easier for them to be released promptly - a real but much smaller effect than fusing the chain.
- Beyond memory, what else does a four-operator eager chain cost that a fused evaluation does not?Four separate traversals of the full column instead of one. Each operator reads its operands and writes its result end to end. A fused pass touches each position once and applies all four operations while the values are already to hand, so it moves far less data for the same arithmetic.
Four rounds of edits on a thousand-page report. Eager evaluation photocopies the whole report after every round, so you end up with four complete stacks and only the last one matters. A deferred design writes the four instructions on a cover sheet and makes one pass with all four in hand - one stack out, and the paper the other three would have cost never leaves the cupboard.
saying these in an interview costs you the question
- States that a chain of operators allocates one temporary per operator with no condition attached
- Assumes writing the chain on one line removes the intermediate results
- Thinks all k intermediates are necessarily live at the same moment
- Believes a deferred design is just an eager design with a delay
- Counts the columns in the expression rather than the operators