Microprocessors & Embedded Systems

Improving Cache Performance
Using Stride Prefetching in gem5

Elias Abuamer

ID: 2220206976

Fares Etweabi

ID: 2200208829

01
Problem Statement & Project Objective

The Problem

Modern processors can execute instructions much faster than data can be retrieved from main memory. To reduce this gap, computer systems use a hierarchy of caches (L1 and L2) that store frequently accessed data closer to the CPU.

However, when the required data is not available in the cache, a cache miss occurs and the processor must access the slower DDR3 main memory. This significantly increases memory access latency and reduces overall system performance.

Baseline System

CPU Model

X86TimingSimpleCPU

L1 I-Cache

16 KB

L1 D-Cache

64 KB

L2 Cache

256 KB

Main Memory

DDR3

Project Objective

The objective of this project is to reduce memory access latency and improve performance by introducing a hardware prefetching mechanism into the cache hierarchy and comparing its performance with the original baseline system.

02
Project Implementation & Proposed Solution

Custom Workload Development

The default Hello World program provided by gem5 is too small to generate meaningful memory activity. Therefore, a custom C program called array_sum.c was developed and compiled.

This program creates a large array and sequentially accesses all elements while computing their sum. Because the memory accesses follow a predictable pattern, the workload is suitable for evaluating cache behavior and prefetching techniques.

Stride Prefetcher Integration

To improve cache performance, a Stride Prefetcher was attached to the L2 cache.

The prefetcher monitors memory access patterns and attempts to predict future memory requests. When a sequential pattern is detected, it fetches future cache blocks before the CPU explicitly requests them.

Configurations Evaluated

Config 1

Degree = 2

Config 2

Degree = 4

Config 3

Degree = 8

03
Implementation Overview
01

Create the Benchmark

We wrote a custom C program array_sum.c that allocates a 10-million element integer array, initialises each element, then sums them all. The sequential memory-access pattern makes it ideal for exercising the cache and evaluating prefetching.
We then compile it using gcc -O2 array_sum.c -o array_sum

array_sum.c
#include <stdio.h>
#define N 10000000

int A[N];

int main() {
    long long sum = 0;
    for (int i = 0; i < N; i++) A[i] = i;
    for (int i = 0; i < N; i++) sum += A[i];
    printf("Sum = %lld\n", sum);
    return 0;
}
02

Baseline Run — No Prefetcher

We simulated the benchmark on the two-level cache system without any prefetcher to establish a performance baseline. The following command was used:

shell
build/X86/gem5.opt \
  configs/learning_gem5/part1/two_level.py \
  ./array_sum

The key metrics were then extracted from m5out/stats.txt:

grep "simTicks"         m5out/stats.txt
grep "simInsts"         m5out/stats.txt
grep -i "overallMissRate" m5out/stats.txt

Result: 237.71 B ticks with an L2 miss rate of 99.98% — confirming severe cache pressure.

04
Implementation Overview
03

Enable the Stride Prefetcher (Degree 4)

We edited configs/learning_gem5/part1/caches.py to attach a StridePrefetcher to the L2 cache. Two changes were required:

caches.py — import
import m5
from m5.objects import Cache, StridePrefetcher
caches.py — L2Cache class
class L2Cache(Cache):
    size         = "256KiB"
    assoc        = 8
    tag_latency  = 20
    data_latency = 20
    ...
    prefetcher = StridePrefetcher(degree=4)

Re-run the same gem5 command and collect simTicks, simInsts, and overallMissRate from stats.txt to compare against baseline.

04

Tune Prefetch Degree (8 & 2)

To find the optimal configuration, the degree parameter was changed and the experiment was repeated. Degree controls how many future cache lines the prefetcher fetches ahead on each stride detection.

Degree 2

degree=2

Conservative

Degree 4 ★

degree=4

Optimal

Degree 8

degree=8

Aggressive

edit in caches.py
# degree 2
prefetcher = StridePrefetcher(degree=2)

# degree 8
prefetcher = StridePrefetcher(degree=8)

After each change, re-run the simulation and record the stats. Results from all three degrees are compared in the Experimental Results slide.

05
Experimental Results

Performance Comparison

Configuration simTicks (B) L2 Miss Rate Accuracy Coverage
No Prefetch 237.71 99.98% — —
Degree 2 143.56 5.00% 48.7% 98.2%
Degree 4 142.25 1.79% 25.6% 98.3%
Degree 8 142.74 1.73% 13.2% 98.3%

Key Observations

  • All prefetching configurations significantly reduced execution time compared to the baseline.
  • The L2 miss rate dropped dramatically after introducing the prefetcher.
  • Degree 4 achieved the best overall execution time.
  • Degree 8 produced the lowest miss rate but did not provide the best performance.
06
Discussion & Conclusion

Prefetch Efficiency Analysis

Degree Prefetches Issued Useful Prefetches
2 2.44 M 1.19 M
4 4.81 M 1.23 M
8 9.30 M 1.23 M

Discussion

Although Degree 8 generated nearly twice as many prefetch requests as Degree 4, the number of useful prefetches remained almost unchanged. As a result, memory traffic increased while the performance improvement became negligible.

This demonstrates that increasing prefetch aggressiveness beyond a certain point can reduce efficiency rather than improve it.

Conclusion

This project successfully demonstrated the effectiveness of hardware prefetching in reducing memory access latency.

By developing a custom memory-intensive workload and integrating a Stride Prefetcher into the L2 cache, execution time was reduced from 237.7B ticks to approximately 142.3B ticks.

Among all tested configurations, Degree 4 provided the best balance between performance improvement and prefetch efficiency, making it the optimal configuration for this workload.

← FaresHere