Master the fundamental concepts of pipelining & out-of-order execution through this focused micro-challenge.
You have read the whole brief, and the concepts above stay free on every task. Writing and running the code needs a plan.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksIPC (retired instructions per cycle) summarizes how well the CPU utilized its front end and execution units. IPC of 1.0 on a 4-wide machine means most slots went idle; values above 2 are common on wide out-of-order cores with SIMD.
bashLoading…
For example, a tight ADD loop on contiguous arrays might exceed 2.0 IPC while pointer-chasing serial loads sit near 0.3.
For this exercise, you will measure IPC on provided kernels and classify bottlenecks. This task asks you to connect perf counters to the pipeline concepts from earlier tasks instead of treating IPC as a magic score.
Keep the relevant datasheet, ISA manual, or architecture textbook chapter open while you implement. When your output disagrees with the reference trace on the same program, the bug is usually a mis-decoded opcode, a stale register read, or a flag bit left unchanged after arithmetic.
For this exercise, you will use those habits while implementing the requirement in the starter code. Microarchitectural product names change across CPU generations, but the control ideas (fetch, bypass, cache lines, vector lanes) stay stable enough to debug from first principles.
Write the analysis you would otherwise do by eye on perf stat output. Given hardware counters for a set of kernels, compute IPC and the derived rates, attribute the lost cycles to memory stalls, branch mispredictions and dependency stalls, and classify each kernel's bottleneck. This is the standard top-down method: IPC alone tells you that something is slow, the ratios tell you what.
cLoading…
config comes first. Counter values are non-negative integers, and cycles is at least 1.
IPC = instructions / cycles, peak = W, efficiency = IPC / W.branch rate = branches / instructions, mispredict rate = branch-misses / branches (0 when there are no branches).L1 miss rate = l1-misses / loads, LLC miss rate = llc-misses / l1-misses (0 when the denominator is 0).memory = min(llc-misses · P, cycles), branch = min(branch-misses · Q, cycles − memory), other = max(cycles − instructions / W − memory − branch, 0). Report each as a percentage of cycles.Pick the first rule that matches:
memory ≥ 30% of cycles → memory boundbranch ≥ 20% of cycles → branch boundIPC ≥ 0.75·W → well parallelisedother ≥ 40% of cycles → dependency boundmixedPer kernel:
cLoading…
IPC and peak have two decimals, percentages one. Print a blank line between kernels. After the last one:
cLoading…
Ties go to the kernel that appeared first.
Input:
cLoading…
Output:
cLoading…
Hidden tests cover a branch-bound kernel, a dependency-bound one with almost no misses, a mixed verdict, kernels with zero branches and zero loads, and a run where the fastest and slowest kernels are neither first nor last.