c7g.xlarge Crib Sheet
Originally written by Annie Zhang and Sam Bai, with revisions by Ryan Lee and the CS 152 course staff.
This lab uses the AWS c7g.xlarge, an ARM CPU based compute-optimized instance powered by Graviton3 (the third generation of AWS Graviton processors). For more architectural context, see Computer Architecture: A Quantitative Approach, Chapter 6.
c7g.xlarge
The instance provides four Graviton3 cores, each running one hardware thread, and 8 GiB of RAM. These are four cores from a full 64-core Graviton3 processor; the host-level last-level cache is shared, not exclusively allocated to the instance. See the AWS Graviton guide and Arm documentation.

Graviton3 cache hierarchy
| Cache | Per-core size | Associativity | Sets | Coherency/Line size |
|---|---|---|---|---|
| L1 data | 64 KiB | 4-way | 256 | 64 B |
| L1 instruction | 64 KiB | 4-way | 256 | 64 B |
| L2 unified | 1 MiB | 8-way | 2048 | 64 B |
| L3 unified (full host) | 32 MiB | 16-way | 32768 | 64 B |
The four allocated cores collectively have 256 KiB of L1 data cache, 256 KiB of L1 instruction cache, and 4 MiB of private L2 cache.
SIMD / Vector Programming
Two vector programming options are Neon and SVE (Scalable Vector Extension). Neon is closer to fixed-length vector programming (Lecture 15, slide 27), while SVE has dynamic width (Lecture 15). In AArch64, Neon has 32 vector registers, each 128 bits wide (four 32-bit floats per vector). SVE is vector-length-agnostic; on Graviton3 it uses 256-bit vectors (eight 32-bit floats).
For ten 32-bit values, Neon can process two groups of four, then handle the remaining two with scalar operations. SVE on Graviton3 can process eight values, then use a predicate to select the two remaining lanes.
SVE uses vector-length-agnostic instructions and predicates, allowing the same
binary to work with different vector widths; svcntw() queries the number of
32-bit lanes. Graviton2 supports Neon but not SVE. See
Arm’s Neon and SVE comparison.
Traditional C
#include <stddef.h>
void add_c(float *out, const float *a, const float *b, size_t n) {
for (size_t i = 0; i < n; ++i) out[i] = a[i] + b[i];
}
Neon
#include <stddef.h>
#include <arm_neon.h>
void add_neon(float *out, const float *a, const float *b, size_t n) {
size_t i = 0;
for (; n - i >= 4; i += 4) {
float32x4_t x = vld1q_f32(a + i);
float32x4_t y = vld1q_f32(b + i);
vst1q_f32(out + i, vaddq_f32(x, y));
}
for (; i < n; ++i) out[i] = a[i] + b[i];
}
float32x4_t holds four floats; vld1q_f32, vaddq_f32, and vst1q_f32 load, add, and store four lanes. For more details, see the ARM Neon Intrinsics Reference.
SVE
#include <stddef.h>
#include <arm_sve.h>
void add_sve(float *out, const float *a, const float *b, size_t n) {
for (size_t i = 0; i < n; i += svcntw()) {
svbool_t pg = svwhilelt_b32(i, n);
svfloat32_t x = svld1_f32(pg, a + i);
svfloat32_t y = svld1_f32(pg, b + i);
svst1_f32(pg, out + i, svadd_f32_x(pg, x, y));
}
}
svcntw() gives the 32-bit lane count; svwhilelt_b32 creates a predicate for valid indices, so no scalar tail loop is required. For more details, see the ARM SVE Reference.
Instance Variety
AWS instance names combine a family, generation, and size. c7g.xlarge has four vCPUs and 8 GiB of memory; c7g.large has two vCPUs and 4 GiB. The C family is compute-optimized (2 GiB per vCPU), M general-purpose (4 GiB per vCPU), and R memory-optimized (8 GiB per vCPU).
| Suffix | Meaning |
|---|---|
d |
Local SSD storage |
n |
Higher network throughput |
b |
Higher EBS (network-attached storage) throughput |
z |
Higher CPU frequency (on families that support it) |
Graviton families include 7g (Graviton3), 8g (Graviton4), and 9g (Graviton5). Storage-oriented i4g(I) families and bursty T4g(T) families target different workloads. Exact generation and suffix availability varies by family.
For profiling instructions, see the APerf Guide.