Computer Architecture · Architecture Tradeoffs

Issue Width and Its Diminishing Returns

Why widening an out-of-order issue window costs area and energy faster than it buys performance, and where the wakeup, select, and bypass networks set the ceiling.

An out-of-order core that issues four instructions per cycle is not twice as fast as one that issues two, and the gap is not a matter of tuning. Each additional issue slot widens a set of structures whose cost grows faster than the number of slots: the scheduler’s wakeup and select logic, the bypass network that forwards results to consumers, the register file’s ports, and the load-store queue. Past a certain point the extra slot adds area and switching energy that the performance does not repay, and the ceiling arrives sooner than the naive scaling of instructions per cycle suggests.

The window is a queue with three independent limits

Issue width is one of three numbers that determine how much parallelism the core can exploit. The others are the reorder buffer size, which bounds how many instructions can be in flight, and the scheduler size, which bounds how many of those are candidates for issue in a given cycle. Widening issue without enlarging the window produces a machine that can consume bursts but cannot sustain them; enlarging the window without widening issue produces a machine that fills up and stalls.

The relationship between window size and performance is sublinear for a reason that is structural rather than economic. The amount of independent work available at a given point in a program is not unlimited, and it decays with distance from the current instruction. Dependencies through registers are removed by renaming, but dependencies through memory remain, and branches truncate the window. A larger window reaches further into the instruction stream and therefore finds more independent instructions, but each additional instruction is further from the head, less likely to be reachable because of an unresolved branch, and more likely to be blocked by a store whose address is not yet known.

The measurement that captures this is the ratio of achieved instructions per cycle to the window size. It rises steeply for small windows, flattens, and then rises again only if branch prediction and memory disambiguation improve alongside. That shape, reported in the classic complexity studies of superscalar designs, is the empirical basis for the diminishing returns.

Wakeup and select cost the product of entries and buses

The scheduler holds entries for instructions that have been decoded and renamed but not yet issued. Each cycle it must determine which entries have all their operands ready and choose among them. The first part, wakeup, is a broadcast: when a result is produced, every waiting entry that names that physical register as a source must be marked ready. A naive implementation compares the tag against every entry, which is a comparator per entry per result bus, and the number of result buses grows with issue width.

The second part, select, is an arbitration: among the ready entries, pick the oldest as many as the issue width allows. A naive implementation is a priority encoder over the ready set, which for $n$ entries is $O(n)$ delay with a carry chain, and the delay grows with $n$ in a way that competes directly with the cycle time.

The standard fix is to break the scheduler into smaller queues with limited wakeup scope, so a result is broadcast only to the entries that could plausibly need it. That is what a clustered or partitioned scheduler does, and it trades a small loss in scheduling freedom for a large reduction in the fanout. The result is that a machine with a wide window is built as several narrower schedulers, which means the effective window per scheduler stays moderate even when the total window is large.

# Growth of the two structures that dominate a scheduler's cost.
# Comparators scale with entries x result buses; the select chain
# scales with entries and adds directly to the cycle-critical path.
def wakeup_comparators(entries, result_buses):
    return entries * result_buses

def select_chain_depth(entries):
    # ripple priority encoder: one stage per entry
    return entries

for entries in (16, 32, 64, 128):
    for buses in (2, 4, 8):
        print(entries, buses, wakeup_comparators(entries, buses),
              select_chain_depth(entries))

The comparator count is the area term and the chain depth is the timing term. A design that doubles entries and doubles issue width quadruples the comparator count while doubling the length of the select chain; the area cost is the one that shows up in the budget, and it is paid on every cycle because the comparators switch whenever a result is broadcast.

Bypass networks grow with the square of the producers

The bypass network forwards a result from the producing unit to the consuming instruction without writing it to the register file and reading it back. For a single-issue pipeline with one producer, the network is one multiplexer input per operand. With $w$ issue slots there are $w$ producers, each of which may produce a result that up to $2w$ operand reads need, so the crossbar is roughly $w \times 2w$ paths.

Each path is a wire and a multiplexer input, and both have capacitance. The wires must span the distance between the execution units and the operand read ports, so the network’s area is not just transistor count but routing resources, which compete with everything else on the die. The switching energy of the network is proportional to the number of paths that change state, which for a wide machine is a substantial share of the core’s dynamic power even when the arithmetic units are modest.

The alternative to a full crossbar is to accept a bypass latency. A result produced in cycle $n$ can be forwarded to a consumer that needs it in cycle $n+2$ through a shorter path than one needed in cycle $n+1$. Accepting one extra cycle of latency for some producer-consumer pairs removes a layer of multiplexing at the cost of a pipeline bubble in the dependent case. This is the same trade a scalar pipeline makes when it accepts a stall instead of a forwarding path, applied at a larger scale.

Ports, queues, and the rest of the widening bill

The register file needs $2w$ read ports and $w$ write ports for an $w$-wide machine. Multiported SRAM cells scale badly: a cell with $p$ ports needs roughly $2p$ access transistors plus the storage, and the bit lines lengthen with the port count, so the area per bit rises faster than the port count and the access time rises with it. Designs work around this by replicating the register file, one copy per execution cluster, which converts ports into area and adds a cost to moving values between clusters.

The load-store queue widens with issue width too, since each slot may issue a memory operation. Its associative search for disambiguation compares each load against pending stores, so the comparator count is the product of the two sizes. A queue of 64 loads and 32 stores with full disambiguation is a large structure, and its energy per cycle is proportional to the number of entries searched.

Branch prediction and instruction fetch widen alongside, because a machine that can issue six instructions needs to fetch at least six per cycle and predict the branch at the end of that fetch group. Fetch bandwidth is bounded by the instruction cache’s line size and by the number of predictions per cycle, which is why the front end often becomes the limit before the back end does.

The energy argument is the one that binds

Area is a budget that can sometimes be bought; energy is not, because the power density limit in Power Density and the Dark Silicon Constraint caps how much can be switched at once. Widening a core raises its energy per instruction even when it raises throughput, because the wider structures switch whether or not the extra slots are used.

That asymmetry is decisive. A four-wide core executing a workload with an average parallelism of 2.5 has issue slots that idle most cycles while their associated wakeup, select, and bypass logic still switches. A two-wide core running the same workload at a higher frequency, or with a larger cache, may deliver the same throughput for less energy, because it does not carry the idle width.

This is why the industry settled on moderate widths for general-purpose cores and pushed the remaining parallelism into either specialised vector units, which amortise instruction overhead over many lanes, or into more cores, which give independent instruction streams their own front ends. Both routes avoid the quadratic terms in the scheduler and bypass network. The vector route amortises the supply overhead over many lanes while keeping one front end, and the core-count route is the frequency-for-parallelism trade in The Power Wall.

Where the ceiling actually is

There is no single number, because the ceiling is set by the ratio of the cost of the marginal slot to the parallelism the workload offers. For workloads with abundant independent work, such as dense numeric kernels over large arrays, the parallelism is there and the limit is bandwidth rather than issue. For workloads with modest parallelism, such as compilers and interpreters walking pointer structures, the marginal slot is mostly idle and the cost is pure.

The practical consequence for architecture is that issue width should be chosen against a workload distribution and not against a peak. A design that is four-wide serves a broad mix; a design that is eight-wide serves the narrow set of codes that can use it and pays for the rest. Since the power budget is shared with cache and with other cores, the marginal slot competes against a marginal megabyte of cache or a marginal core, and the comparison usually favours those, because their cost grows linearly while the slot’s grows quadratically.

References

  1. Computer Architecture: A Quantitative Approach — John L. Hennessy and David A. Patterson, Morgan Kaufmann
  2. The Microarchitecture of Superscalar Processors — James E. Smith and Gurindar S. Sohi, Proceedings of the IEEE, 1995
  3. Complexity-Effective Superscalar Processors — Subbarao Palacharla, Norman P. Jouppi, James E. Smith, ISCA 1997