CXL · Module 23
Composable Infrastructure
A fixed SKU has a ratio, and a workload that does not match it over-buys. This chapter builds SKU rigidity, compose fit, bind latency, fragmentation, instance lifetime, generation mixing, series reliability, control-plane scale, composable TCO and the assembled model.
23.1 found a quarter of a fleet's DRAM bought and unreachable, and pooled it. This chapter asks the obvious next question: if memory can be attached to any socket at boot, what else can — and what is a server once its parts are chosen at allocation time rather than at purchase time?
The answer is genuinely better on the axis it was built for. A fixed SKU has a ratio, and a workload that does not match it over-buys whichever resource it does not need — 75% of the cores, in the case section 5 opens with. Composing from a pool removes that strand exactly.
It also introduces five costs the fixed SKU did not have, and four of them are ways a machine can fail that a bought machine could not. That asymmetry is what this chapter is for.
1. The Engineering Problem — A SKU Has A Ratio
A fixed SKU strands whichever resource it over-buys. An 8-core, 128 GB SKU serving a workload wanting 8 cores and 512 GB buys 32 cores and strands 24 — 75%. Section 5.
Composing means every resource type must be there, not the one you checked. A machine short only on accelerators is a machine that does not compose. Section 6.
A composed machine boots slower. Sixteen binds at 250 ms each on an 8-second boot is a 50% start-up overhead. Section 7.
And free capacity is not composable capacity. 512 GB free across four domains, none holding more than 128, refuses a 256 GB request. Section 8.
A machine built from four borrowed parts fails four times as often. Four year-long parts make a quarter-year machine. Section 12.
This chapter against 23.1, stated precisely. That one owns pooling one resource type. This one owns assembling a machine from several pools — which is why sections 6, 12 and 13 have no counterpart there: they are all consequences of a machine having more than one borrowed part.
2. The One-Sentence Model
Composing beats buying when machines really are assembled, the SKU strand is genuinely gone, every resource type is checked before the compose is promised, bind time is inside the start-up budget, no machine spans generations, and the fabric manager can track the compose rate — and every defect below is a fleet that assembles machines and is worse off for it.
3. What This Chapter Owns
| Ground | Owner |
|---|---|
| Pooling one resource type, and its costs | 23.1 |
| Utilisation measurement and reporting | 23.3 |
| Hyperscaler deployment patterns | 23.4 |
| Fabric topology and switch depth | 21.3 |
| Multi-tenant isolation and QoS | 19.2 |
| Assembling a machine from several pools | this chapter |
Deferred:
| Deferred ground | Owner |
|---|---|
| Memory blast radius and reclaim | 23.1 §7 · §10 |
| Fabric-attached device discovery | 21.1 |
| Accelerator coherence and attach | 22.1 |
| Cryptographic primitives | out of scope — see §4 |
4. Teaching-Model Boundary
Every model is a small synchronous block isolating one property. A real composable system is a fabric manager, a switch, several device classes, a BMC, a scheduler and an inventory database, and none of that is reproduced. What is reproduced is the arithmetic each has to get right, and the shape of the mistake when it does not.
Each model is built twice — a correct build and a broken build selected by a parameter. Every broken build here is the fixed-SKU mental model applied to a composed machine. The ratio matches. The machine boots. The parts are one generation. The chassis is what fails. Each was true when the server arrived on a pallet, and each stops being true the moment its parts are chosen independently.
Figure 1 — The top path is the whole case for composability, and it is a good one. The bottom path costs nothing on this diagram, which is exactly why sections 6 through 13 exist: the gain is one number and the price is five.
5. RTL 1 — A Fixed SKU Strands Whatever It Over-Buys
// RTL 1 - a fixed SKU has a ratio. A workload whose ratio differs is provisioned
// by whichever resource binds, and the other is bought and unused.
module sku_rigidity #(parameter int ASSUME_RATIO_MATCHES = 0) (
input logic clk, rst_n,
input logic size_it,
input logic [15:0] sku_cpu, sku_mem_gb, need_cpu, need_mem_gb,
output logic [15:0] servers, provisioned_cpu, stranded_cpu, stranded_pct,
output logic balanced,
output logic [7:0] n_sizings, n_rigid,
output logic strand_hidden_err
);
logic [31:0] c_q, m_q, p_q, s_q;
logic [15:0] by_cpu, by_mem;
assign c_q = (sku_cpu == 16'd0) ? 32'd0
: (({16'd0, need_cpu} + {16'd0, sku_cpu} - 32'd1) / {16'd0, sku_cpu});
assign by_cpu = (c_q > 32'd65535) ? 16'hFFFF : c_q[15:0];
assign m_q = (sku_mem_gb == 16'd0) ? 32'd0
: (({16'd0, need_mem_gb} + {16'd0, sku_mem_gb} - 32'd1)
/ {16'd0, sku_mem_gb});
assign by_mem = (m_q > 32'd65535) ? 16'hFFFF : m_q[15:0];
// The SKU count is set by whichever resource needs more servers.
assign servers = (by_cpu > by_mem) ? by_cpu : by_mem;
assign p_q = {16'd0, servers} * {16'd0, sku_cpu};
assign provisioned_cpu = (p_q > 32'd65535) ? 16'hFFFF : p_q[15:0];
// A model that assumes the workload matches the SKU ratio sees no strand.
assign stranded_cpu = (ASSUME_RATIO_MATCHES != 0) ? 16'd0
: ((provisioned_cpu > need_cpu)
? (provisioned_cpu - need_cpu) : 16'd0);
assign s_q = (provisioned_cpu == 16'd0) ? 32'd0
: (({16'd0, stranded_cpu} * 32'd100) / {16'd0, provisioned_cpu});
assign stranded_pct = (s_q > 32'd65535) ? 16'hFFFF : s_q[15:0];
assign balanced = (stranded_pct <= 16'd25);
// Cores bought and unused, reported as matched to the workload.
assign strand_hidden_err = size_it && (provisioned_cpu > need_cpu)
&& (stranded_cpu == 16'd0);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_sizings <= 8'd0; n_rigid <= 8'd0;
end else if (size_it) begin
n_sizings <= n_sizings + 8'd1;
if (!balanced) n_rigid <= n_rigid + 8'd1;
end
end
endmoduleEight sizings. An 8-core, 128 GB SKU.
| Cores / memory wanted | Servers · Cores bought · Stranded · Share |
|---|---|
| 8 / 512 GB | 4 · 32 · 24 cores · 75% — the memory binds |
| 32 / 512 GB | 4 · 32 · 0 · 0% — the ratio matches exactly |
| 8 / 1024 GB | 8 · 64 · 56 cores · 87% |
| 32 / 128 GB | 4 · 32 · 0 · the memory is stranded instead |
| 24 / 512 GB | 4 · 32 · 8 cores · exactly 25% · exactly balanced |
| nothing wanted | 0 · 0 · 0 · nothing to strand |
| 9 / 128 GB | 2 · 16 · 7 cores · 43% — nine cores need two SKUs |
| 8 / 500 GB | 4 · 32 · 24 cores · 75% — 500 GB needs four |
Four sizings were rigid; the ratio-matched model reported none.
This is 23.1 §5's strand with a second dimension, and the second dimension is what makes it unfixable by packing. There, a socket's memory stranded because no other socket could reach it. Here the cores strand because the memory forced a server count, and no amount of bin-packing helps: the ratio is soldered.
Row four is the honest limit of a one-sided model. A core-heavy workload strands no cores — it strands memory, symmetrically, and this model does not measure that. The finding is not "cores strand"; it is "whichever resource does not bind, strands", and section 27 exercise 1 is the symmetric version.
Rows seven and eight are the rounding, and they matter more than they look. Nine cores need two SKUs, not one and a bit. Five hundred gigabytes needs four, not three and a bit. A SKU count is a ceiling, and the remainder is bought in full — which section 17 records as the source of this chapter's only two mutation survivors.
6. RTL 2 — Every Resource Type Must Be There
// RTL 2 - composing needs every resource type. A compose succeeds only if the
// pool has enough of all of them, not enough of the one that was checked.
module compose_fit #(parameter int IGNORE_INVENTORY = 0) (
input logic clk, rst_n,
input logic compose,
input logic [15:0] pool_cpu, pool_mem_gb, pool_accel,
input logic [15:0] need_cpu, need_mem_gb, need_accel,
output logic [1:0] short_types,
output logic [15:0] worst_short,
output logic cpu_ok, mem_ok, accel_ok, composable,
output logic [7:0] n_composes, n_failed,
output logic partial_check_err
);
logic [15:0] cpu_gap, mem_gap, accel_gap, biggest;
assign cpu_ok = (need_cpu <= pool_cpu);
assign mem_ok = (need_mem_gb <= pool_mem_gb);
assign accel_ok = (need_accel <= pool_accel);
assign cpu_gap = cpu_ok ? 16'd0 : (need_cpu - pool_cpu);
assign mem_gap = mem_ok ? 16'd0 : (need_mem_gb - pool_mem_gb);
assign accel_gap = accel_ok ? 16'd0 : (need_accel - pool_accel);
assign biggest = (cpu_gap > mem_gap) ? cpu_gap : mem_gap;
assign worst_short = (biggest > accel_gap) ? biggest : accel_gap;
assign short_types = {1'b0, ~cpu_ok} + {1'b0, ~mem_ok} + {1'b0, ~accel_ok};
// Checking one resource type is what a CPU-shaped scheduler does.
assign composable = (IGNORE_INVENTORY != 0) ? cpu_ok
: (cpu_ok && mem_ok && accel_ok);
// A compose declared possible while some resource type was short.
assign partial_check_err = compose && composable && (short_types != 2'd0);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_composes <= 8'd0; n_failed <= 8'd0;
end else if (compose) begin
n_composes <= n_composes + 8'd1;
if (!composable) n_failed <= n_failed + 8'd1;
end
end
endmoduleSix composes. A pool of 64 cores, 1024 GB and eight accelerators.
| Cores / memory / accelerators wanted | Types short · Worst gap · Composable |
|---|---|
| 16 / 512 GB / 4 | 0 · 0 · yes |
| 16 / 512 GB / 16 | 1 — the accelerators · 8 · no, and a CPU-only check says yes |
| 128 / 512 GB / 4 | 1 — the cores · 64 · no, and the CPU-only check agrees |
| 64 / 1024 GB / 8 | 0 · 0 · yes, exactly emptying the pool |
| nothing wanted | 0 · 0 · yes, trivially |
| 128 / 2048 GB / 16 | 3 — all of them · 1024 · no |
Three composes failed when every type is checked; two when only the cores are.
A compose is a conjunction and a scheduler is usually a CPU scheduler. Row two is the failure that matters: cores and memory are plentiful, the accelerators are not, and a scheduler that grew up placing pods by CPU request finds nothing wrong. The machine is promised, the bind fails, and the error surfaces somewhere with no context.
Row three is why the broken build survives review. When the cores really are the binding resource, the CPU-only check is right — and on a fleet whose workloads are mostly CPU-bound it is right most of the time. A check that is correct on the common case and silent on the rare one is the hardest kind to find.
Row six is worth reporting rather than reducing. All three types short is not "more broken"; it is a different operational answer — the fleet is out of everything, which is a capacity problem rather than a placement one, and short_types exists so the two can be told apart.
7. RTL 3 — A Composed Machine Boots Slower
// RTL 3 - composing takes time. Every resource has to be bound and enumerated
// before the machine boots, and that is added to every instance start.
module compose_latency #(parameter int INSTANT_COMPOSE = 0) (
input logic clk, rst_n,
input logic compose_it,
input logic [15:0] resources, bind_ms, boot_ms,
output logic [15:0] compose_ms, added_ms, overhead_pct,
output logic acceptable,
output logic [7:0] n_composes, n_slow,
output logic bind_ignored_err
);
logic [31:0] c_q, o_q;
assign c_q = (INSTANT_COMPOSE != 0) ? {16'd0, boot_ms}
: ({16'd0, boot_ms} + ({16'd0, resources} * {16'd0, bind_ms}));
assign compose_ms = (c_q > 32'd65535) ? 16'hFFFF : c_q[15:0];
// compose_ms is boot_ms plus a non-negative term, so this cannot underflow.
assign added_ms = compose_ms - boot_ms;
assign o_q = (boot_ms == 16'd0) ? 32'd0
: (({16'd0, added_ms} * 32'd100) / {16'd0, boot_ms});
assign overhead_pct = (o_q > 32'd65535) ? 16'hFFFF : o_q[15:0];
assign acceptable = (overhead_pct <= 16'd25);
// A composed instance costed as if its resources were already attached.
assign bind_ignored_err = compose_it && (resources != 16'd0) && (bind_ms != 16'd0)
&& (compose_ms == boot_ms);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_composes <= 8'd0; n_slow <= 8'd0;
end else if (compose_it) begin
n_composes <= n_composes + 8'd1;
if (!acceptable) n_slow <= n_slow + 8'd1;
end
end
endmoduleSix composes. An 8-second boot, 250 ms to bind each resource.
| Resources bound | Total start · Added · Overhead · Acceptable |
|---|---|
| 4 | 9.0 s · 1.0 s · 12% · yes |
| 16 | 12.0 s · 4.0 s · 50% · no |
| 0 — nothing to bind | 8.0 s · 0 · 0% · yes |
| 8 | 10.0 s · 2.0 s · exactly 25% · exactly acceptable |
| 4, boot unmeasured | 1.0 s · 1.0 s · no baseline · — |
| 4, binds are instant | 8.0 s · 0 · 0% · yes |
One compose was too slow; the instant-compose model reported none.
Bind time is linear in the number of parts, which is the thing composability increases. A machine with four borrowed resources costs 12%; a machine with sixteen costs half its boot again, and the whole point of composability is that the parts count is a free variable.
Row four is the design rule this gives you. At 250 ms per bind on an 8-second boot, eight resources is the threshold — and that is a number a fabric manager's designers can act on, either by reducing per-bind cost or by binding in parallel.
Row five is the model declining to divide by an unmeasured baseline, exactly as 23.1 §8 does. A percentage against a denominator nobody measured is not a small error; it is a number with no meaning at all, and reporting the added milliseconds is the honest alternative.
The bind rows are the whole difference and they are strictly additive. No amount of faster boot recovers them, and the only levers are fewer resources or parallel binding — which is a fabric-manager design decision rather than an operational one.
8. RTL 4 — Free Capacity Is Not Composable Capacity
// RTL 4 - fragmentation. A compose draws from one fabric domain, so free
// capacity spread across domains is not capacity a single machine can have.
module fragmentation #(parameter int IGNORE_FRAGMENTATION = 0) (
input logic clk, rst_n,
input logic allocate,
input logic [15:0] free_gb, largest_domain_gb, request_gb,
output logic [15:0] fragmented_gb, usable_gb,
output logic satisfiable,
output logic [7:0] n_allocations, n_denied,
output logic fragmentation_ignored_err
);
// Capacity that is free but not reachable from any single domain.
assign fragmented_gb = (free_gb > largest_domain_gb)
? (free_gb - largest_domain_gb) : 16'd0;
// A model that ignores fragmentation offers the sum of every domain.
assign usable_gb = (IGNORE_FRAGMENTATION != 0) ? free_gb : largest_domain_gb;
assign satisfiable = (request_gb <= usable_gb);
// A request granted against capacity no single domain can supply.
assign fragmentation_ignored_err = allocate && satisfiable
&& (request_gb > largest_domain_gb);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_allocations <= 8'd0; n_denied <= 8'd0;
end else if (allocate) begin
n_allocations <= n_allocations + 8'd1;
if (!satisfiable) n_denied <= n_denied + 8'd1;
end
end
endmoduleSix allocations. 512 GB free in the fleet.
| Largest domain / request | Fragmented · Usable by one machine · Satisfiable |
|---|---|
| 128 GB / 256 GB | 384 GB · 128 · no — despite 512 free |
| 128 GB / 128 GB | 384 · 128 · yes, exactly |
| 128 GB / 64 GB | 384 · 128 · yes |
| 512 GB / 256 GB | 0 · 512 · yes — one domain holds it all |
| nothing free / 64 GB | 0 · 0 · no |
| 128 GB / 512 GB | 384 · 128 · no, and the whole-pool model thinks it exactly fits |
Three allocations were denied when domains are respected; one when they are not.
A dashboard's free-capacity figure is a sum and a compose needs a maximum. Those are the same number only when the pool is one domain, and they diverge exactly as the fleet grows — which is the direction every composable deployment goes.
Row six is the sharpest form of the failure. A 512 GB request against 512 GB free reads as an exact fit, and there is no single domain that can supply a byte over 128. The whole-pool model does not merely over-count; it produces a machine specification that cannot be built anywhere in the fleet.
This is 23.1 §12's contention with a different cause and the same symptom. There, the pool was short because other sockets held the memory. Here it is not short at all — the capacity is free, idle, and in the wrong place, which is a scheduling problem rather than a capacity one and needs a completely different fix.
9. RTL 5 — Bind Time Is Paid In Full By Short Instances
// RTL 5 - composing and decomposing cost time that a short-lived instance pays
// in full, because the overhead does not shrink with the lifetime.
module bind_lifetime #(parameter int ASSUME_STATIC = 0) (
input logic clk, rst_n,
input logic run,
input logic [15:0] lifetime_min, compose_s, decompose_s,
output logic [15:0] overhead_s, lifetime_s, overhead_pct,
output logic acceptable,
output logic [7:0] n_runs, n_churn,
output logic bind_free_err
);
logic [31:0] l_q, o_q;
// A statically composed machine pays this once and never again; a machine
// composed per workload pays it every time.
assign overhead_s = (ASSUME_STATIC != 0) ? 16'd0 : (compose_s + decompose_s);
assign l_q = {16'd0, lifetime_min} * 32'd60;
assign lifetime_s = (l_q > 32'd65535) ? 16'hFFFF : l_q[15:0];
assign o_q = (lifetime_s == 16'd0) ? 32'd65535
: (({16'd0, overhead_s} * 32'd100) / {16'd0, lifetime_s});
assign overhead_pct = (o_q > 32'd65535) ? 16'hFFFF : o_q[15:0];
assign acceptable = (overhead_pct <= 16'd10);
// Composing and tearing down reported as costing nothing.
assign bind_free_err = run && (compose_s != 16'd0) && (overhead_s == 16'd0);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_runs <= 8'd0; n_churn <= 8'd0;
end else if (run) begin
n_runs <= n_runs + 8'd1;
if (!acceptable) n_churn <= n_churn + 8'd1;
end
end
endmoduleSix runs. Twelve seconds to compose, six to tear down — eighteen either way.
| Instance lifetime | Overhead · Life in seconds · Share · Acceptable |
|---|---|
| 5 minutes | 18 s · 300 · 6% · yes |
| 1 minute | 18 s · 60 · 30% · no |
| 1 hour | 18 s · 3600 · under 1% · yes |
| unstated | 18 s · 0 · unbounded · no |
| 3 minutes | 18 s · 180 · exactly 10% · exactly acceptable |
| 5 minutes, instant bind | 0 · 300 · 0% · yes |
Two runs were churning; the static model reported one.
The overhead is fixed and the lifetime is not, so composability has a minimum useful instance duration. At eighteen seconds of bind and teardown, three minutes is the floor — and a fleet of one-minute jobs spends 30% of its machine-time assembling and dismantling machines.
That is the single most important scoping question for a composable deployment, and it is answered by the workload rather than the hardware. Long-lived database and training instances amortise the bind to nothing. Short-lived batch and serverless workloads are the ones composability is usually pitched at, and they are the ones it suits worst.
Row six is where the static model is right, and it is not a strawman: a machine composed once at rack commissioning and left alone genuinely pays nothing per workload. The error is applying that model to a machine composed per job, which is precisely the deployment being sold.
10. RTL 6 — A Pool Holds Generations
// RTL 6 - a pool holds generations. A machine composed across them runs at the
// slowest part it was given, not at the fastest part available.
module heterogeneity #(parameter int ASSUME_UNIFORM = 0) (
input logic clk, rst_n,
input logic compose,
input logic mixed,
input logic [15:0] gen_a_gbps, gen_b_gbps,
output logic [15:0] fastest_gbps, effective_gbps, loss_gbps, loss_pct,
output logic acceptable,
output logic [7:0] n_composes, n_degraded,
output logic uniform_assumed_err
);
logic [15:0] slowest;
logic [31:0] p_q;
assign fastest_gbps = (gen_a_gbps > gen_b_gbps) ? gen_a_gbps : gen_b_gbps;
assign slowest = (gen_a_gbps > gen_b_gbps) ? gen_b_gbps : gen_a_gbps;
// A mixed machine runs at its slowest part. A model that assumes one
// generation reports the fastest part in the pool.
assign effective_gbps = (ASSUME_UNIFORM != 0) ? fastest_gbps
: (mixed ? slowest : fastest_gbps);
// effective_gbps is either the fastest or the slowest, so this cannot underflow.
assign loss_gbps = fastest_gbps - effective_gbps;
assign p_q = (fastest_gbps == 16'd0) ? 32'd0
: (({16'd0, loss_gbps} * 32'd100) / {16'd0, fastest_gbps});
assign loss_pct = (p_q > 32'd65535) ? 16'hFFFF : p_q[15:0];
assign acceptable = (loss_pct <= 16'd20);
// A machine spanning generations reported at the newer one's rate.
assign uniform_assumed_err = compose && mixed && (gen_a_gbps != gen_b_gbps)
&& (loss_gbps == 16'd0);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_composes <= 8'd0; n_degraded <= 8'd0;
end else if (compose) begin
n_composes <= n_composes + 8'd1;
if (!acceptable) n_degraded <= n_degraded + 8'd1;
end
end
endmoduleSix composes.
| Parts / mixed | Fastest available · Effective · Lost · Share |
|---|---|
| 800 and 400 Gbps / yes | 800 · 400 · 400 · 50% |
| 800 and 400 / no | 800 · 800 · 0 · 0% |
| 800 and 800 / yes | 800 · 800 · 0 · 0% — spanning costs nothing |
| 400 and 800 / yes | 800 · 400 · 400 · 50% — the same, either way round |
| 800 and 640 / yes | 800 · 640 · 160 · exactly 20% · exactly acceptable |
| unmeasured / yes | 0 · 0 · 0 · no rate to report |
Two composes were degraded; the uniform model reported none.
A pool accumulates generations because it is refreshed incrementally, which is the operational model composability makes possible and attractive. The consequence is that a machine drawn from it can span three years of silicon, and it runs at whatever the oldest part can do.
Rows one and four are the same case with the operands swapped, and both are driven deliberately. 21.4 established that symmetric operands hide gaps — testing "the first part is slower" is not testing "the second part is slower" — and the fix is to drive both rather than to reason that they are equivalent.
Row three is the case that makes the check honest. Two parts of the same speed can be spanned freely; the error is not mixing, it is mixing across a performance gap, which is why uniform_assumed_err requires the rates to actually differ.
11. RTL 7 — A Machine's Reliability Divides By Its Parts
// RTL 7 - a composed machine fails when any borrowed part fails, so its mean
// time between failures is the part's divided by the number of parts.
module failure_attribution #(parameter int BLAME_THE_SERVER = 0) (
input logic clk, rst_n,
input logic assess,
input logic [15:0] parts, part_mtbf_h,
output logic [15:0] composite_mtbf_h, lost_h_per_year,
output logic acceptable,
output logic [7:0] n_assessments, n_fragile,
output logic parts_ignored_err
);
logic [31:0] c_q, l_q;
// A model that blames the chassis reports the chassis's own reliability.
assign c_q = (BLAME_THE_SERVER != 0) ? {16'd0, part_mtbf_h}
: ((parts == 16'd0) ? {16'd0, part_mtbf_h}
: ({16'd0, part_mtbf_h} / {16'd0, parts}));
assign composite_mtbf_h = (c_q > 32'd65535) ? 16'hFFFF : c_q[15:0];
assign l_q = (composite_mtbf_h == 16'd0) ? 32'd65535
: (32'd8760 / {16'd0, composite_mtbf_h});
assign lost_h_per_year = (l_q > 32'd65535) ? 16'hFFFF : l_q[15:0];
assign acceptable = (composite_mtbf_h >= 16'd4380);
// A composed machine's reliability quoted as one part's.
assign parts_ignored_err = assess && (parts > 16'd1)
&& (composite_mtbf_h == part_mtbf_h);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_assessments <= 8'd0; n_fragile <= 8'd0;
end else if (assess) begin
n_assessments <= n_assessments + 8'd1;
if (!acceptable) n_fragile <= n_fragile + 8'd1;
end
end
endmoduleSix assessments. Parts that each last a year — 8760 hours.
| Borrowed parts | Machine MTBF · Failures a year · Acceptable |
|---|---|
| 4 | 2190 h — a quarter year · 4 · no |
| 1 | 8760 h · 1 · yes — it is just a server |
| 8 | 1095 h — six weeks · 8 · no |
| 2 | 4380 h · 2 · exactly acceptable |
| none declared | 8760 h · 1 · nothing to divide by |
| 17520 | rounds to nothing · unbounded · not a machine that runs |
Three assessments were fragile; the chassis model reported none.
This is 22.5 §9's arithmetic at the scale of one machine, and it is the same surprise. Reliability divides. A machine assembled from four excellent parts is four times worse than any of them, and nothing about any part changed.
The chassis model is not laziness; it is the only model a bought server ever needed. A server's reliability was a single vendor number for a single sealed unit. Composability turns it into a series calculation over parts from different vendors on different maintenance schedules, and that is a category change in how a fleet is reasoned about, not a worse number.
Row four gives the practical bound. At a half-year acceptance threshold, two borrowed parts is the limit for parts with a one-year life — which means either fewer parts, better parts, or an acceptance threshold that admits the truth.
Figure 3 — Three excellent parts and a fourth off-diagram, each a year between failures, assembled into a machine that fails every eleven weeks. The parts are in series and nothing in the picture is defective — which is why a per-part reliability review passes and the machine still does not stay up.
12. RTL 8 — The Control Plane Is A Resource Too
// RTL 8 - the control plane is a resource too. Every compose is work for the
// fabric manager, and a fleet cannot compose faster than it can be tracked.
module orchestration_scale #(parameter int IGNORE_CONTROL_PLANE = 0) (
input logic clk, rst_n,
input logic schedule,
input logic [15:0] demand_per_min, capacity_per_min,
output logic [15:0] served_per_min, backlog_per_min, backlog_pct,
output logic keeps_up,
output logic [7:0] n_schedules, n_backlogged,
output logic control_plane_ignored_err
);
logic [31:0] p_q;
// A model that ignores the control plane serves whatever is asked for.
assign served_per_min = (IGNORE_CONTROL_PLANE != 0) ? demand_per_min
: ((demand_per_min > capacity_per_min) ? capacity_per_min
: demand_per_min);
// served_per_min is a minimum against demand_per_min, so this cannot underflow.
assign backlog_per_min = demand_per_min - served_per_min;
assign p_q = (demand_per_min == 16'd0) ? 32'd0
: (({16'd0, backlog_per_min} * 32'd100) / {16'd0, demand_per_min});
assign backlog_pct = (p_q > 32'd65535) ? 16'hFFFF : p_q[15:0];
assign keeps_up = (backlog_per_min == 16'd0);
// A compose rate promised beyond what the fabric manager can track.
assign control_plane_ignored_err = schedule && (demand_per_min > capacity_per_min)
&& (served_per_min == demand_per_min);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_schedules <= 8'd0; n_backlogged <= 8'd0;
end else if (schedule) begin
n_schedules <= n_schedules + 8'd1;
if (!keeps_up) n_backlogged <= n_backlogged + 8'd1;
end
end
endmoduleFive schedules. A fabric manager that can track sixty composes a minute.
| Composes demanded | Served · Queued · Share queued · Keeps up |
|---|---|
| 120 a minute | 60 · 60 · 50% · no |
| 60 a minute | 60 · 0 · 0% · yes, exactly |
| 30 a minute | 30 · 0 · 0% · yes |
| 120, control plane down | 0 · 120 · 100% · no |
| none demanded | 0 · 0 · 0% · trivially |
Two schedules were backlogged; the model that ignores the control plane reported none.
Composability replaces a purchasing decision with a per-instance operation, and per-instance operations have a rate limit. A fixed-SKU fleet composes zero machines a minute forever; a composable fleet composes one per instance start, and the fabric manager is the only thing tracking every binding.
Row two is the sizing rule and it is the whole model. The control plane's capacity is a specification, the instance start rate is a workload property, and section 9's short-lived instances are exactly the ones that generate the highest compose rate — the two failure modes reinforce each other.
Row four is the failure nobody plans for. A control plane that is down does not degrade the fleet's existing machines at all; it makes the fleet unable to create new ones, which looks like a total outage to anything that autoscales and like nothing at all to anything already running.
13. RTL 9 — What Composability Costs
// RTL 9 - what composability costs. A better fit against an orchestration layer
// that fixed-SKU racks do not need at all.
module composable_tco #(parameter int IGNORE_ORCHESTRATION = 0) (
input logic clk, rst_n,
input logic price,
input logic [15:0] fixed_cost, composed_resource_cost, orchestration_cost,
input logic [15:0] workloads,
output logic [15:0] composed_total, fixed_per_workload, composed_per_workload,
output logic composing_cheaper,
output logic [7:0] n_pricings, n_wins,
output logic orchestration_ignored_err
);
logic [31:0] t_q, f_q, c_q;
// The orchestration layer is a cost fixed SKUs do not carry.
assign t_q = {16'd0, composed_resource_cost}
+ ((IGNORE_ORCHESTRATION != 0) ? 32'd0 : {16'd0, orchestration_cost});
assign composed_total = (t_q > 32'd65535) ? 16'hFFFF : t_q[15:0];
assign f_q = (workloads == 16'd0) ? 32'd65535
: (({16'd0, fixed_cost} * 32'd100) / {16'd0, workloads});
assign fixed_per_workload = (f_q > 32'd65535) ? 16'hFFFF : f_q[15:0];
assign c_q = (workloads == 16'd0) ? 32'd65535
: (({16'd0, composed_total} * 32'd100) / {16'd0, workloads});
assign composed_per_workload = (c_q > 32'd65535) ? 16'hFFFF : c_q[15:0];
assign composing_cheaper = (composed_per_workload < fixed_per_workload);
// A composed cost that does not carry the orchestration it needs.
assign orchestration_ignored_err = price && (orchestration_cost != 16'd0)
&& (composed_total == composed_resource_cost);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_pricings <= 8'd0; n_wins <= 8'd0;
end else if (price) begin
n_pricings <= n_pricings + 8'd1;
if (composing_cheaper) n_wins <= n_wins + 8'd1;
end
end
endmoduleFive pricings. Fixed racks at 4000 against composed resources at 2848, over forty workloads.
| Orchestration cost | Composed total · Per hundred workloads · Cheaper than the fixed 10,000 |
|---|---|
| 800 | 3648 · 9120 · yes |
| 0 | 2848 · 7120 · yes |
| 2000 | 4848 · 12,120 · no |
| 800, no workloads | 3648 · unbounded · neither is cheaper |
| 1152 | 4000 · exactly 10,000 · exactly equal — not cheaper |
Two wins when the orchestration is costed; four when it is not.
The resource saving is real and the orchestration layer is real, and they are on different budgets. Composed resources cost 2848 against 4000 of fixed racks — a 29% saving that any procurement exercise will find. The fabric manager, its high-availability deployment, its integration and the team that runs it are on nobody's hardware line.
Row five is the break-even and it is the figure to compute first. At an orchestration cost of 1152 the two are exactly equal, which means any orchestration layer costing less than 29% of the fixed-rack spend wins and any layer costing more loses. That is one ratio, available before anything is bought, and it decides the programme.
This is 23.1 §13's structure with a different denominator, and the pairing is deliberate: there the un-costed item was a fabric, here it is a control plane. Both are the thing the new architecture needs and the old one did not, and both fall outside the boundary of the report that justifies the change.
14. RTL 10 — Composable Infrastructure Assembled
// RTL 10 - composable infrastructure assembled. Everything that must hold before
// composing a machine beats buying one that already fits.
module composable_model #(parameter int ASSEMBLED_ONLY = 0) (
input logic clk, rst_n,
input logic evaluate,
input logic servers_composed, // machines are assembled, not bought
input logic ratio_fit_improved, // the strand from the SKU ratio is gone
input logic inventory_sufficient, // every resource type is checked
input logic latency_budgeted, // bind time is in the start-up budget
input logic generations_matched, // no machine spans generations
input logic control_plane_sized, // the fabric manager keeps up
output logic improves,
output logic [5:0] fail_mask,
output logic [7:0] n_eval, n_improves,
output logic false_gain_err
);
assign fail_mask[0] = ~servers_composed;
assign fail_mask[1] = ~ratio_fit_improved;
assign fail_mask[2] = ~inventory_sufficient;
assign fail_mask[3] = ~latency_budgeted;
assign fail_mask[4] = ~generations_matched;
assign fail_mask[5] = ~control_plane_sized;
// The assembled-only build reports that machines are being composed, which is
// the number a composability programme is measured on.
assign improves = (ASSEMBLED_ONLY != 0) ? servers_composed : (fail_mask == 6'd0);
assign false_gain_err = evaluate && improves && (fail_mask != 6'd0);
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
n_eval <= 8'd0; n_improves <= 8'd0;
end else if (evaluate) begin
n_eval <= n_eval + 8'd1;
if (improves) n_improves <= n_improves + 8'd1;
end
end
endmodule| Configuration | Fail mask · Full model · Assembled-only model |
|---|---|
| everything holds | 000000 · improves · improves |
| the SKU strand was never removed | 000010 · does not improve · improves |
| plus the inventory and the bind latency | 001110 · does not improve · improves |
| only the generations are mixed | 010000 · does not improve · improves |
| only the control plane is undersized | 100000 · does not improve · improves |
| nothing was composed | 000001 · does not improve · does not improve |
One improving configuration of six, and four false claims.
The assembled-only definition is the metric a composability programme is measured on — machines composed, as a count or a percentage — and it is right about one of the six. Row two is the purest form: the fabric works, machines are assembled, and the assembled ratios are the SKU ratios because nobody changed the request templates.
Figure 4 — Four exits and one composes. The first question is the one that decides most cases and is asked least: a workload whose ratio a stock SKU already serves has nothing to gain, and the composability programme's own metric counts composing it as a success.
15. Quantitative Reasoning
SKU rigidity. An 8-core, 128 GB SKU serving 8 cores and 512 GB buys four servers, 32 cores, and strands 24 — 75%. At 1024 GB it strands 56 of 64, or 87%.
Compose fit. A pool of 64 cores, 1024 GB and eight accelerators: a request short only on accelerators is refused by a full check and accepted by a CPU-only one.
Bind latency. Four binds at 250 ms on an 8-second boot is 12%; sixteen binds is 50%, and eight is exactly the 25% threshold.
Fragmentation. 512 GB free with no domain above 128 refuses a 256 GB request — and refuses a 512 GB request that reads as an exact fit.
Instance lifetime. Eighteen seconds of bind and teardown is 6% of a five-minute instance and 30% of a one-minute one; three minutes is exactly the 10% threshold.
Generations. An 800 Gbps part and a 400 Gbps part make a 400 Gbps machine — a 50% loss against the fastest part in the pool.
Reliability. Four parts each lasting a year make a 2190-hour machine failing four times a year; eight parts make it six weeks.
Control plane. 120 composes a minute demanded of a manager tracking 60 leaves half the demand queued.
Cost. 9120 per hundred workloads composed against 10,000 fixed — and break-even at an orchestration layer costing 1152, which is 29% of the fixed-rack spend.
The assembled model. Six properties, six configurations, one improves. The assembled-only definition reported five.
| Quantity | Correct · Broken · Ratio |
|---|---|
| Cores stranded, 8 wanted and 512 GB | 24 of 32 · 0 reported · all of it |
| Composes refused, 6 attempted | 3 · 2 · the accelerator case |
| Start-up overhead, 16 binds | 50% · 0 reported · the whole bind |
| Usable by one machine, 512 GB free | 128 GB · 512 offered · 4x |
| Bind share of a one-minute instance | 30% · 0 reported · all of it |
| Machine rate, an 800 and a 400 part | 400 Gbps · 800 claimed · 2x |
| Machine MTBF, four year-long parts | 2190 h · 8760 claimed · 4x |
| Composes served of 120 demanded | 60 · 120 promised · 2x |
| Cost per hundred workloads | 9120 · 7120 claimed · the control plane |
| Configurations called improving, of 6 | 1 · 5 · 4 false claims |
16. Assertions
Every check is an explicit comparison against an exact value. Icarus Verilog 13.0 has no concurrent assertion support, so each is a procedural comparison against 1'b1, and every one is an equality.
Every inclusive threshold in this chapter is driven at exactly equal, constructed rather than swept — the batch-021 carry-forward, kept.
SKU rigidity. A strand of exactly 25% is constructed from a 24-core need against 512 GB, and both non-multiple cases are driven — nine cores and 500 GB.
chk(kGs == 16'd2, "nine cores need two SKUs, not one");
chk(kGt == 16'd7, "stranding seven");Compose fit. A request that exactly empties the pool is driven, and the cores-short case is asserted as one the CPU-only model gets right.
Bind latency. An overhead of exactly 25% is constructed from eight binds, and the unmeasured-boot case is asserted to report added milliseconds without a ratio.
Fragmentation. A request exactly the size of the largest domain is driven, and the request that "exactly fits" the sum is asserted unbuildable.
chk(rGs == 1'b0, "512 GB does not fit a 128 GB domain");
chk(rBs == 1'b1, "though the whole-pool model thinks it exactly fits");Instance lifetime. An overhead of exactly 10% is constructed from a three-minute life, and the instant-bind case is asserted as not a hidden cost.
Generations. The mix is driven both ways round, and a loss of exactly 20% is constructed from an 800 and a 640 part.
Reliability. Exactly two parts is driven at the acceptance threshold, and the case where the part count exceeds a part's life in hours is asserted unbounded.
Control plane. Demand exactly at capacity is driven.
Cost. The orchestration cost at which the two are exactly equal is constructed, and asserted not cheaper.
The assembled model. Every fail mask is asserted as an exact six-bit value, and each of the six bits is driven false alone.
Totals: 279 checks across two testbenches, 145 on the front five models and 134 on the back five, all passing on the unmutated sources.
17. Mutation Testing
Sixty-three mutations were injected one at a time. 63 injected, 63 killed, after two survivors.
| Model · Mutation | Verdict |
|---|---|
| 1 · the server count follows the cores only | killed |
| 1 · the core ceiling rounds down | killed |
| 1 · the memory ceiling rounds down | killed |
| 1 · the ratio is assumed to match in both builds | killed |
| 1 · the strand share divides by what was needed | killed |
| 1 · the balance threshold becomes exclusive | killed |
| 1 · the hidden-strand check drops the over-provision guard | killed |
| 2 · the memory check becomes exclusive | killed |
| 2 · the accelerator type is not checked | killed |
| 2 · only the cores are checked in both builds | killed |
| 2 · the worst shortfall ignores the accelerators | killed |
| 2 · the short-type count drops the memory term | killed |
| 2 · the partial-check test ignores the short types | killed |
| 3 · the resource count is dropped | killed |
| 3 · composing is instant in both builds | killed |
| 3 · the added time is the whole compose | killed |
| 3 · the overhead divides by the compose | killed |
| 3 · the acceptance threshold becomes exclusive | killed |
| 3 · the ignored-bind check drops the zero-cost guard | killed |
| 3 · the unmeasured-boot guard is removed | killed |
| 4 · the whole pool is offered in both builds | killed |
| 4 · the fragmented amount is counted from the wrong side | killed |
| 4 · the satisfiability test becomes exclusive | killed |
| 4 · the ignored-fragmentation check drops the domain test | killed |
| 5 · the teardown is not counted | killed |
| 5 · the bind is free in both builds | killed |
| 5 · the life is read in minutes, not seconds | killed |
| 5 · the overhead share divides the wrong way | killed |
| 5 · the acceptance threshold becomes exclusive | killed |
| 5 · the free-bind check drops the compose guard | killed |
| 5 · the unstated-lifetime guard is removed | killed |
| 6 · the fastest part is taken in both builds | killed |
| 6 · an unmixed machine takes the slowest part too | killed |
| 6 · the fastest and slowest are swapped | killed |
| 6 · the loss share divides by the effective rate | killed |
| 6 · the acceptance threshold becomes exclusive | killed |
| 6 · the uniform check drops the differing-rate guard | killed |
| 6 · the unmeasured-rate guard is removed | killed |
| 7 · the part count does not divide the reliability | killed |
| 7 · the chassis is blamed in both builds | killed |
| 7 · the yearly failure count divides the wrong way | killed |
| 7 · the acceptance threshold becomes exclusive | killed |
| 7 · the ignored-parts check drops the part-count guard | killed |
| 7 · the zero-reliability guard is removed | killed |
| 8 · everything demanded is served in both builds | killed |
| 8 · the served rate is not capped by the capacity | killed |
| 8 · the backlog is counted from the wrong side | killed |
| 8 · the keeps-up test is inverted | killed |
| 8 · the ignored-control check drops the over-demand guard | killed |
| 8 · the no-demand guard is removed | killed |
| 9 · the orchestration is left out in both builds | killed |
| 9 · the fixed cost per workload uses the wrong scale | killed |
| 9 · the comparison is the wrong way round | killed |
| 9 · the comparison becomes inclusive | killed |
| 9 · the ignored-orchestration check drops the cost guard | killed |
| 9 · the no-workload guard is removed | killed |
| 10 · ratio-fit bit dropped from the mask | killed |
| 10 · inventory bit dropped from the mask | killed |
| 10 · bind-latency bit dropped from the mask | killed |
| 10 · generation bit dropped from the mask | killed |
| 10 · control-plane bit dropped from the mask | killed |
| 10 · any-property instead of every-property | killed |
| 10 · false-gain check ignores the mask | killed |
Both survivors were the same omission and it is a new class. Section 5's two ceilings — cores per SKU and gigabytes per SKU — both survived being changed to floors, because every need in the testbench was an exact multiple of its SKU dimension. Eight cores into an 8-core SKU, 512 GB into a 128 GB SKU: ceil and floor agree on every one, so the mutation changed nothing observable.
This is the equality-case gap in a different costume, and that is the finding. Batch 021's survivors were thresholds never driven at the boundary; these are ceilings never driven off the boundary. The rule that catches both is the same one stated more generally: for any operation whose whole purpose is to handle a special case, drive the special case and drive its complement. A <= needs the equality; a ceil needs the remainder.
Constructing the kills needed a case where the rounded resource also binds. Nine cores against 128 GB — the cores bind and nine is not a multiple of eight — kills the core ceiling. Eight cores against 500 GB — the memory binds and 500 is not a multiple of 128 — kills the memory one. A remainder on the resource that does not bind changes nothing, which is why the first attempt at each case failed to kill.
The complete set was re-run after the stimulus change, per standing discipline, and all sixty-three held.
18. Verification Strategy
What a testbench for a composition model must cover.
Drive the special case and its complement. A threshold needs its equality; a ceiling needs its remainder; a minimum needs both operands to win. This chapter's only survivors were the second of those, and the general rule now covers all three.
Make the rounded quantity the binding one. A remainder on a resource that does not decide the answer is invisible. Both survivors needed not just a non-multiple but a non-multiple that binds, which took two attempts to construct.
Drive symmetric operands both ways. Section 10's generation mix is tested with the slow part first and the slow part second, because 21.4 established that reasoning about symmetry is not the same as testing it.
The cases where the fixed-SKU model is right. A workload matching the SKU ratio. Cores as the binding resource for a CPU-only scheduler. One domain holding everything. An hour-long instance. Parts of one generation. A single-part machine. Six cases where the broken build is correct, each exempted explicitly.
Counters as a second signature. Ten models, ten pairs of totals, and the builds differ in all ten — four rigid against none, three failed against two, three fragile against none.
What a real composable system needs that these models do not have. Time and correlation. Fragmentation accumulates; generations drift as the pool refreshes; compose demand spikes when an autoscaler reacts to the same event across a fleet. Every model here is a snapshot, and section 26 exercise 9 is the closest this chapter comes to the dynamics.
19. Synthesis and Implementation Reality
Section 5's arithmetic is a purchasing model, and it is the only part of this chapter that runs before any hardware exists. Its failure mode is a rack order.
Section 6's inventory check lives in the scheduler, and the practical difficulty is that the scheduler and the fabric manager are usually different systems with different views. A conjunction across three inventories held in two places is where the check gets simplified, which is exactly how the broken build arises.
Section 7's bind time is fabric-manager and firmware together. Enumerating a newly attached device is a PCIe and CXL discovery sequence, and its cost is real silicon and real firmware — 21.1 is where the mechanism lives. Binding in parallel is the available optimisation and it is not free, because discovery order matters for some device classes.
Section 8's domains are cabling. Which resources a given host can reach is fixed by the fabric topology at build time, and no software changes it — the same irreversibility 23.1 §19 identified for hop count.
Section 10's generation mixing is an allocator policy and the cheapest of the six to fix: refuse to span generations, at the cost of more fragmentation in section 8's terms. The two policies trade directly against each other.
Section 12's control plane is a distributed system with a database, and its compose rate is a throughput number like any other. It is also a single point of failure for fleet growth, which is a property no fixed-SKU rack had.
20. Silicon Observability
| Counter | Why it matters |
|---|---|
| Requested against provisioned, per resource type, per machine | Section 5's strand, which is the entire justification |
| Compose attempts, and refusals by which resource type was short | Section 6 — the type matters more than the count |
| Bind time per resource, and total compose time | Section 7's overhead, measured rather than specified |
| Instance lifetime distribution | Section 9 — the amortisation is a workload property |
| Free capacity per fabric domain, not summed | Section 8 — the sum is the wrong statistic |
| Generation of every part in every composed machine | Section 10, which is otherwise invisible until a benchmark |
| Parts per composed machine | Section 11's series reliability, before the outage |
| Outages by which borrowed part failed | Section 11 measured rather than modelled |
| Compose demand and control-plane service rate | Section 12's queue |
| Composes counted against composes that improved the fit | Section 14 — the metric against the thing it stands for |
"Free capacity per fabric domain, not summed" is the counter that has to replace one that already exists. Every capacity dashboard reports a total, and section 8 shows the total is unusable for a compose decision. This is harder than adding a counter, because the existing number is not obviously wrong — it is correct about the fleet and wrong about any single machine.
21. Debug Lab
Symptom. A team moves a batch-processing fleet to composed machines. The programme reports 94% of instances composed — its headline metric, met. Three months later: instance start latency is up 60%, one service in five reports intermittently halved throughput, machine outages have tripled, and during traffic spikes new instances stop being created for minutes at a time.
Step 1 — did the fit actually improve? Requested against provisioned, per resource type: the composed machines have the same ratios as the SKUs they replaced. The request templates were copied from the old instance types, so section 5's strand was carried across intact. The headline metric is true and the thing it was standing for did not happen.
Step 2 — the start latency. Bind time per resource: 250 ms, and the composed machines bind sixteen resources because the templates enumerate every device individually. Section 7 exactly: a 50% start-up overhead, on a boot that was already the fleet's biggest complaint.
Step 3 — and the instances are short. Lifetime distribution: median 90 seconds. Eighteen seconds of bind and teardown against a 90-second life is 20% — section 9, twice the acceptable threshold, and the fleet is spending a fifth of its machine-time assembling machines.
Step 4 — the halved throughput. Generation of every part, per machine: 19% of machines span two generations, and those are the ones reporting half rate. Section 10, and the allocator has no generation constraint because nothing asked for one.
Step 5 — the outages. Parts per machine: sixteen. At a one-year part life that is a 548-hour composite MTBF — an outage every three weeks per machine, against the old sealed server's yearly one. Section 11, and the tripling is if anything an under-count.
Step 6 — the spikes. Compose demand against control-plane service rate: demand peaks at 210 a minute against a manager sized for 60. Section 12, and the autoscaler's reaction to a traffic spike is precisely the event that generates a synchronised compose burst.
The finding. Five distinct failures, all predicted by this chapter, and the programme's own metric improved throughout. The root cause of the first is the one that makes the rest worse: copying the old instance templates preserved both the ratios and the sixteen-device enumeration, which drove the bind count, the compose rate and the parts count simultaneously.
The fix, in the order the numbers say. Rewrite the templates to request what the workloads actually need — which fixes section 5, and reduces the parts count, which improves sections 7, 11 and 12 at once. Add a generation constraint to the allocator. And move the 90-second workloads back to pre-composed machines, because section 9's arithmetic says they cannot amortise a bind at any parts count.
What made this hard. Nothing failed. The fabric worked, the fabric manager worked, every machine composed successfully, and the metric the programme was accountable for went up and stayed up. Five of the six counters that would have shown the problem are ones a fixed-SKU fleet had no reason to collect.
22. Design Review
1. Does a stock SKU already fit this workload's ratio? If it does, composing it is a metric, not a gain. Section 5.
2. What is the strand, per resource type, before and after? The programme's actual objective, and section 21 is what happens when nobody measures it.
3. Does the scheduler check every resource type, or the one it grew up checking? Section 6.
4. How many resources does a machine bind, and at what cost each? Bind time is linear in parts. Section 7.
5. What is the instance lifetime distribution? Below three minutes, composability costs more than it saves. Section 9.
6. Is free capacity reported per domain or summed? A compose needs a maximum, not a sum. Section 8.
7. Can a machine span generations, and does anything stop it? A 50% rate loss with nothing broken. Section 10.
8. How many borrowed parts, and what is the composite MTBF? Reliability divides. Section 11.
9. What is the peak compose rate, and what does the fabric manager serve? An autoscaler makes these synchronised. Section 12.
10. What does the orchestration layer cost, against the fixed-rack spend it replaces? Break-even is 29%. Section 13.
23. How This Appears In Real Engineering
A platform team running a composability programme owns section 5 and usually measures section 14 instead. The two are different questions and only one of them is the objective — "machines composed" is easy to count and "strand removed" is what was promised, and section 21 is a fleet where the first hit 94% and the second never moved.
A scheduler team owns section 6, and the difficulty is architectural rather than careless. Resource inventories for cores, memory and accelerators are frequently held by different systems, so a conjunction across all three is a distributed query on the hot path of every placement. Simplifying it to the one inventory the scheduler owns is the natural engineering response and the exact defect.
A reliability function meets section 11 as a step change, exactly as 23.1 §23 describes for blast radius. A bought server's MTBF was a vendor number; a composed machine's is a series calculation the organisation now owns, and there is rarely anyone whose job it was to notice the transfer.
A capacity or finance function owns section 13, and the reporting boundary is the problem again. The resource saving is a hardware line and the orchestration layer is a software and headcount line, so the comparison that decides the architecture spans two budgets that no single report covers.
24. Common Misconceptions
"A composable machine is just a server you configure." It is a server whose parts are in series and whose reliability divides by their count. Section 11.
"Composability removes waste." It removes the SKU-ratio strand, if the request templates change. Sections 5 and 21.
"The scheduler will find a machine that fits." It will find one that fits on the resource it checks. Section 6.
"Boot time is boot time." Sixteen binds add half the boot again. Section 7.
"We have 512 GB free." No single machine can have more than 128 of it. Section 8.
"Composing is cheap once it works." Eighteen seconds is 30% of a one-minute instance. Section 9.
"The pool is the pool." It holds three generations, and a machine runs at the oldest part it was given. Section 10.
"Our parts are enterprise-grade." Four year-long parts make a quarter-year machine. Section 11.
"The fabric manager is control-plane software." It is a throughput bottleneck on every instance start, and a single point of failure for fleet growth. Section 12.
"94% of instances are composed." One property of six. Section 14.
25. Interview Reasoning
Q. What does composability actually buy?
The removal of the SKU-ratio strand. A workload needing eight cores and 512 GB on an 8-core, 128 GB SKU buys four servers and strands 24 of 32 cores — 75%, and no packing fixes it because the ratio is soldered. Composing takes eight cores and 512 GB from two pools with nothing over-bought.
Q. What does it cost that a fixed SKU did not?
Five things. Bind time on every start, linear in the parts count. Fragmentation, because a compose draws from one fabric domain and a dashboard reports a sum. Generation mixing, because a pool refreshes incrementally. Series reliability, because the machine fails when any borrowed part does. And a control plane that is a rate limit on instance creation. Four of those five are new ways to fail, not worse versions of old ones.
Q. Why does a composed machine fail more often than the server it replaced?
Because it is four or eight things in series instead of one sealed unit. Four parts each lasting a year make a 2190-hour machine — four outages annually instead of one — and no part is defective. A per-part reliability review passes and the machine still does not stay up.
Q. Your fleet has 512 GB free and a 256 GB request fails. Explain.
The 512 is a sum across fabric domains and a machine draws from one. If no domain holds more than 128, the largest composable allocation is 128 — the capacity is free, idle, and unreachable from the host that wants it. The fix is a per-domain counter, which means replacing a dashboard number rather than adding one.
Q. When is composability the wrong answer?
When the instances are short. Eighteen seconds of bind and teardown is 6% of a five-minute instance and 30% of a one-minute one — so below about three minutes it costs more than the strand it recovers. The workloads composability is most often pitched at, short-lived and bursty, are the ones it suits worst.
Q. Your programme reports 94% of instances composed and everything got worse. Where do you look?
At requested-against-provisioned per resource type. If the request templates were copied from the old instance types, the composed machines have the SKU ratios and the strand never moved — the metric measures the mechanism rather than the objective. That one finding also explains a high parts count, which drives the bind time, the compose rate and the failure rate together.
26. Exercises
1. Extend RTL 1 to report the memory strand as well as the core strand, and show that every unbalanced workload strands exactly one of them.
2. Give RTL 2 a fourth resource type and show that the probability of a partial check being wrong grows with the type count.
3. Make RTL 3's binds parallel with a fixed concurrency and find the concurrency at which sixteen resources become acceptable.
4. Drive RTL 4 with a domain-size distribution and compute the largest request that succeeds 99% of the time.
5. Combine RTL 5 and RTL 3: for a given lifetime distribution, find the parts count above which composing loses.
6. Add a generation-matching constraint to RTL 6 and show what it costs in RTL 4's terms.
7. Extend RTL 7 to parts with different lifetimes and find which part dominates the composite.
8. Make RTL 8's demand bursty rather than steady and find the control-plane capacity that bounds the queue.
9. Model section 21 end to end: templates copied, parts count high, generations mixed, and the compose burst during a spike.
10. Add a seventh property to RTL 10. If it is implied by one of the six, say which; if not, give the fleet it catches that the current mask calls improving.
27. Summary
23.1 pooled one resource and priced six costs. This chapter assembles a machine from several pools, and the shape of the answer is the same with the ratio changed: one clean gain, and five costs of which four are new failure modes.
A fixed SKU has a ratio and strands whatever it over-buys. Eight cores and 512 GB on an 8-core, 128 GB SKU buys 32 cores and strands 24 — 75% — and at 1024 GB it strands 87%.
Composing is a conjunction and a scheduler is usually a CPU scheduler. A machine short only on accelerators does not compose, and a CPU-only check finds nothing wrong.
Bind time is linear in the parts count, which is the variable composability frees. Four binds cost 12% of an 8-second boot; sixteen cost 50%, and eight is exactly the threshold.
Free capacity is not composable capacity. 512 GB free with no domain above 128 refuses a 256 GB request — and a 512 GB request that reads as an exact fit is a machine that cannot be built anywhere.
Short instances pay the bind in full. Eighteen seconds is 6% of five minutes and 30% of one — so composability has a minimum useful instance duration of about three minutes, and short bursty workloads are the ones it suits worst.
A pool holds generations. An 800 Gbps part and a 400 Gbps part make a 400 Gbps machine, and the allocator has no reason to refuse unless something tells it to.
Reliability divides by the parts. Four parts each lasting a year make a 2190-hour machine failing four times annually — and every part passes its own review.
The control plane is a rate limit on fleet growth. 120 composes a minute demanded of a manager tracking 60 queues half of them, and an autoscaler is exactly what makes the demand synchronised.
The orchestration layer is the un-costed item. 9120 per hundred workloads against 10,000 fixed — break-even at an orchestration cost of 29% of the fixed-rack spend, which is one ratio available before anything is bought.
Two mutations survived because every need was an exact multiple of its SKU. Ceilings and floors agreed everywhere, so the rounding was untested — the equality-case gap in a new costume, and the rule that catches both is to drive a special case and its complement.
Composing machines is one property of six. The metric a composability programme is measured on called five of six fleets improved when one was — and section 21 is a fleet that hit 94% composed while its strand never moved at all.
23.3 — Resource Utilisation takes the measurement problem that has now appeared twice, in 23.1 §14 and here, and makes it the subject: what a utilisation number actually says, what it cannot say, and how to build one that means what a capacity plan needs it to mean.
Continue learning
Related tutorials
- Related topic
Fabric Managers
A CXL fabric is configured by a manager that sits off the data path. This chapter builds what it may command, how it changes a fabric that is carrying traffic, why its path must be its own, and what happens to the fabric when it is lost.
- Related topic
The CXL 3.x Future Vision
Composability is what the fabric is for. This chapter builds composed machines against fixed ratios, stranded capacity, assembly latency, the borrowed-resource failure domain, the utilisation case, decomposition and leaks, heterogeneous pools, the access-latency penalty, scheduler uptake and the assembled composability model.
- Related topic
Memory Disaggregation
A quarter of a fleet's DRAM is bought and unreachable. This chapter builds stranded capacity, pool provisioning, blast radius, the latency tax, allocation granularity, reclaim time, shared bandwidth, pool contention, disaggregation TCO and the assembled model.
- Related topic
Resource Utilisation
A utilisation figure is a choice of numerator, denominator, weighting and window. This chapter builds allocated-against-touched, sampling, denominator choice, aggregation, headroom, time weighting, attribution, zombie capacity, the value of a gain and the assembled model.
Standards & specifications
- Governing standard
- CXL Specification (CXL Consortium)(opens CXL Consortium in a new tab)
Defines CXL.io, CXL.cache and CXL.mem, and the coherence and memory-pooling behaviour built on them. System design and deployment topology are not mandated.
This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.
Where this fits
Part of the CXL curriculum.
