The panel below runs two versions of the same loop side by side. The original computes the array offset as i×stride on every step — a multiply each time. The optimized version keeps a running total and just adds the stride each step.
<!-- Strength Reduction Demo: multiply vs add per iteration -->
<div class="controls">
<label for="stride-slider">Stride: <span id="stride-val">4</span></label>
<input id="stride-slider" type="range" min="1" max="16" value="4" />
<label for="steps-slider">Steps: <span id="steps-val">8</span></label>
<input id="steps-slider" type="range" min="2" max="16" value="8" />
</div>
<div class="panels">
<div class="panel">
<div class="panel-title">Original (multiply)</div>
<div class="code-box" id="code-orig"></div>
<div class="trace-label">Values produced:</div>
<div id="trace-orig" class="trace"></div>
</div>
<div class="panel">
<div class="panel-title">Optimized (add)</div>
<div class="code-box" id="code-opt"></div>
<div class="trace-label">Values produced:</div>
<div id="trace-opt" class="trace"></div>
</div>
</div>
<div class="status-bar" id="status"></div>
<button id="btn-run" type="button">Rerun</button>
/* two-panel layout: original (multiply) vs optimized (add) */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; margin: 0; color: #222; }
.controls { display: flex; flex-direction: column; gap: .4rem; margin-bottom: .8rem; }
label { font-size: .85rem; font-weight: 600; color: #444; }
input[type=range] { width: 100%; accent-color: #1d3557; }
.panels { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.panel { background: #f4f7fa; border-radius: 10px; padding: .7rem; }
.panel-title { font-size: .78rem; font-weight: 700; text-transform: uppercase;
letter-spacing: .06em; color: #1d3557; margin-bottom: .4rem; }
.code-box { background: #1d3557; color: #e0e8f0; border-radius: 7px; padding: .6rem .75rem;
font: 12px/1.7 ui-monospace, monospace; white-space: pre; }
.trace-label { font-size: .75rem; color: #666; margin: .45rem 0 .2rem; }
.trace { display: flex; flex-wrap: wrap; gap: 4px; min-height: 28px; }
.chip { display: inline-flex; align-items: center; justify-content: center;
min-width: 36px; height: 26px; padding: 0 7px; border-radius: 6px;
font: 700 12px ui-monospace, monospace; }
.chip-mul { background: #fdecea; color: #c0392b; border: 1px solid #e0a9a3; }
.chip-add { background: #e8f5e9; color: #1b7a3e; border: 1px solid #a3d9a5; }
.status-bar { font-size: .9rem; font-weight: 600; min-height: 1.4em; margin: .6rem 0 .4rem; color: #1d3557; }
button { font: 600 14px system-ui; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Strength reduction: replace i*stride (one multiply per step)
// with an accumulator t that advances by stride (one add per step).
// The original loop recomputes i*stride on every iteration.
// The optimized loop keeps t = i*stride and adds stride each step.
let stride = 4, steps = 8;
const strideSlider = document.getElementById('stride-slider');
const stepsSlider = document.getElementById('steps-slider');
const strideVal = document.getElementById('stride-val');
const stepsVal = document.getElementById('steps-val');
// Update displayed slider labels and re-render on every slider move.
strideSlider.addEventListener('input', () => { stride = +strideSlider.value; strideVal.textContent = stride; render(); });
stepsSlider.addEventListener('input', () => { steps = +stepsSlider.value; stepsVal.textContent = steps; render(); });
function codeOrig(s, n) {
// Show the original code pattern with the multiply.
return `t = i * ${s} // multiply every step\n\n` +
`i = 0\n` +
`loop ${n} times:\n` +
` offset = i * ${s}\n` +
` i++`;
}
function codeOpt(s, n) {
// Show the optimized code pattern with the accumulator.
return `t = 0 // init accumulator\n\n` +
`i = 0, t = 0\n` +
`loop ${n} times:\n` +
` offset = t\n` +
` t += ${s} // add every step`;
}
function makeChip(value, kind) {
const d = document.createElement('span');
d.className = 'chip ' + kind;
d.textContent = value;
return d;
}
function render() {
// Rebuild both code boxes and trace rows from current stride and steps.
document.getElementById('code-orig').textContent = codeOrig(stride, steps);
document.getElementById('code-opt').textContent = codeOpt(stride, steps);
const tOrig = document.getElementById('trace-orig');
const tOpt = document.getElementById('trace-opt');
tOrig.innerHTML = '';
tOpt.innerHTML = '';
let acc = 0;
for (let i = 0; i < steps; i++) {
const mulVal = i * stride; // Original: compute i*stride via multiplication each step.
tOrig.appendChild(makeChip(mulVal, 'chip-mul'));
tOpt.appendChild(makeChip(acc, 'chip-add'));
acc += stride; // Optimized: advance accumulator by stride via addition.
}
// Update the summary status line below the panels.
document.getElementById('status').textContent =
i18nFmt('%steps% iterations, stride %stride% — both produce identical values.', { steps: steps, stride: stride });
}
document.getElementById('btn-run').addEventListener('click', render);
render();
<!-- Strength Reduction Demo: multiply vs add per iteration -->
<div class="controls">
<label for="stride-slider">Stride: <span id="stride-val">4</span></label>
<input id="stride-slider" type="range" min="1" max="16" value="4" />
<label for="steps-slider">Steps: <span id="steps-val">8</span></label>
<input id="steps-slider" type="range" min="2" max="16" value="8" />
</div>
<div class="panels">
<div class="panel">
<div class="panel-title">Original (multiply)</div>
<div class="code-box" id="code-orig"></div>
<div class="trace-label">Values produced:</div>
<div id="trace-orig" class="trace"></div>
</div>
<div class="panel">
<div class="panel-title">Optimized (add)</div>
<div class="code-box" id="code-opt"></div>
<div class="trace-label">Values produced:</div>
<div id="trace-opt" class="trace"></div>
</div>
</div>
<div class="status-bar" id="status"></div>
<button id="btn-run" type="button">Rerun</button>
/* two-panel layout: original (multiply) vs optimized (add) */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; margin: 0; color: #222; }
.controls { display: flex; flex-direction: column; gap: .4rem; margin-bottom: .8rem; }
label { font-size: .85rem; font-weight: 600; color: #444; }
input[type=range] { width: 100%; accent-color: #1d3557; }
.panels { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.panel { background: #f4f7fa; border-radius: 10px; padding: .7rem; }
.panel-title { font-size: .78rem; font-weight: 700; text-transform: uppercase;
letter-spacing: .06em; color: #1d3557; margin-bottom: .4rem; }
.code-box { background: #1d3557; color: #e0e8f0; border-radius: 7px; padding: .6rem .75rem;
font: 12px/1.7 ui-monospace, monospace; white-space: pre; }
.trace-label { font-size: .75rem; color: #666; margin: .45rem 0 .2rem; }
.trace { display: flex; flex-wrap: wrap; gap: 4px; min-height: 28px; }
.chip { display: inline-flex; align-items: center; justify-content: center;
min-width: 36px; height: 26px; padding: 0 7px; border-radius: 6px;
font: 700 12px ui-monospace, monospace; }
.chip-mul { background: #fdecea; color: #c0392b; border: 1px solid #e0a9a3; }
.chip-add { background: #e8f5e9; color: #1b7a3e; border: 1px solid #a3d9a5; }
.status-bar { font-size: .9rem; font-weight: 600; min-height: 1.4em; margin: .6rem 0 .4rem; color: #1d3557; }
button { font: 600 14px system-ui; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Strength reduction: replace i*stride (one multiply per step)
// with an accumulator t that advances by stride (one add per step).
// The original loop recomputes i*stride on every iteration.
// The optimized loop keeps t = i*stride and adds stride each step.
let stride = 4, steps = 8;
const strideSlider = document.getElementById('stride-slider');
const stepsSlider = document.getElementById('steps-slider');
const strideVal = document.getElementById('stride-val');
const stepsVal = document.getElementById('steps-val');
// Update displayed slider labels and re-render on every slider move.
strideSlider.addEventListener('input', () => { stride = +strideSlider.value; strideVal.textContent = stride; render(); });
stepsSlider.addEventListener('input', () => { steps = +stepsSlider.value; stepsVal.textContent = steps; render(); });
function codeOrig(s, n) {
// Show the original code pattern with the multiply.
return `t = i * ${s} // multiply every step\n\n` +
`i = 0\n` +
`loop ${n} times:\n` +
` offset = i * ${s}\n` +
` i++`;
}
function codeOpt(s, n) {
// Show the optimized code pattern with the accumulator.
return `t = 0 // init accumulator\n\n` +
`i = 0, t = 0\n` +
`loop ${n} times:\n` +
` offset = t\n` +
` t += ${s} // add every step`;
}
function makeChip(value, kind) {
const d = document.createElement('span');
d.className = 'chip ' + kind;
d.textContent = value;
return d;
}
function render() {
// Rebuild both code boxes and trace rows from current stride and steps.
document.getElementById('code-orig').textContent = codeOrig(stride, steps);
document.getElementById('code-opt').textContent = codeOpt(stride, steps);
const tOrig = document.getElementById('trace-orig');
const tOpt = document.getElementById('trace-opt');
tOrig.innerHTML = '';
tOpt.innerHTML = '';
let acc = 0;
for (let i = 0; i < steps; i++) {
const mulVal = i * stride; // Original: compute i*stride via multiplication each step.
tOrig.appendChild(makeChip(mulVal, 'chip-mul'));
tOpt.appendChild(makeChip(acc, 'chip-add'));
acc += stride; // Optimized: advance accumulator by stride via addition.
}
// Update the summary status line below the panels.
document.getElementById('status').textContent =
i18nFmt('%steps% iterations, stride %stride% — both produce identical values.', { steps: steps, stride: stride });
}
document.getElementById('btn-run').addEventListener('click', render);
render();
Slide the stride and watch both counters stay in lock-step. The optimized loop does exactly the same arithmetic — it just uses addition instead of multiplication. Try a large stride to make the savings concrete: every multiplication in the original loop is a wasted opportunity that the compiler quietly eliminates.
Comments
Loading comments...