Is Graviton5 Really Faster for Your Workloads? Debunking Performance Myths
Skeptical about Graviton5 25% uplift claims? This post cuts through hype to debunk myths and reveal true performance gains.
Myth: Just switching to Graviton5 instances will automatically make my application 25% faster. This promise feels like a free lunch, but reality is more nuanced. The impressive benchmarks you see are for specific, highly-optimized workloads, and your mileage will absolutely vary.
TL;DR: AWS Graviton5 performance gains are real but not automatic. The 25% uplift hinges on your workload being multi-threaded and memory-bound, not I/O-bound. This post provides a simple benchmark framework and a decision checklist to see if your application is a good fit before you migrate.
You’ll walk away with:
- A clear mental model for which workloads benefit most from Graviton5.
- A simple, copy-pasteable
heycommand to benchmark your own HTTP services. - A pre-migration checklist to make a data-driven decision.
- The one compiler flag that makes the biggest difference for compiled languages.
The AWS marketing material for M9g instances claims they “deliver up to 25% better compute performance… than comparable Graviton4-based M7g instances.” The key here is “up to”. Those gains don’t come from magic; they come from tangible architectural improvements: faster DDR5 memory and higher core density.
This means your application must actually be in a position to use that extra memory bandwidth and those extra cores. A single-threaded Python script waiting on a slow database query won’t get any faster. But a Go web service handling thousands of concurrent requests can see a massive improvement.
At its core, vCPU on Graviton represents a physical core, not a hyper-thread as it is on x86. This distinction is critical: when you scale up cores on Graviton, you are adding real, physical execution units, which dramatically benefits truly parallel workloads.
graph TD
subgraph "Graviton5 Sweet Spot"
A["Web Server (Go, Rust, Java)"] -- "many concurrent requests" --> B["Parallel Task Processing"];
C["ML Inference (Batch Processing)"] -- "utilizes all cores" --> D["In-Memory Database (Redis)"];
end
subgraph "Gains Will Be Marginal"
X["Single-threaded Python Script"] -- "waits on API call" --> Y["Network I/O"];
Z["Legacy App (single process)"] -- "cannot parallelize" --> W["Disk I/O Bound"];
end
The myth is that the instance type is the variable that matters most. The truth is that the bottleneck in your application architecture is what dictates performance. Graviton5 only fixes CPU and memory bottlenecks.
How Do You Benchmark Your Application for Graviton?
To benchmark your application, you must test the entire stack under a realistic load, not just a synthetic CPU test. Deploy identical versions of your application to both an M7g (Graviton4) and an M9g (Graviton5) instance. Then, use a load generation tool like hey to hammer the endpoints and compare requests per second and latency distributions.
Here’s a simple, effective test for an HTTP API. Run this from a separate EC2 instance in the same VPC to get a clean network measurement.
1
2
3
4
5
# First, benchmark the existing M7g instance to get a baseline
hey -z 2m -c 100 http://<m7g-instance-ip>:8080/api/v1/workload
# Then, run the exact same test against the new M9g instance
hey -z 2m -c 100 http://<m9g-instance-ip>:8080/api/v1/workload
Look for a significant increase in “Requests/sec” and a decrease in the average and 99th percentile latency in the output.
Click to see example 'hey' output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Summary:
Total: 120.0059 secs
Slowest: 0.1588 secs
Fastest: 0.0088 secs
Average: 0.0249 secs
Requests/sec: 4007.8206
Total data: 2048.00 MB
Size/req: 4259 bytes
Response time histogram:
0.009 [1] |
0.024 [179782] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
0.039 [199701] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
0.054 [78001] |■■■■■■■■■■■■■
0.069 [15600] |■■■
0.084 [5200] |■
0.099 [1820] |
0.114 [520] |
0.129 [1] |
0.144 [1] |
0.159 [1] |
Latency distribution:
10% in 0.0150 secs
25% in 0.0190 secs
50% in 0.0250 secs
75% in 0.0310 secs
90% in 0.0380 secs
95% in 0.0430 secs
99% in 0.0560 secs
My strong recommendation: do not use generic CPU benchmarks like sysbench cpu run. They are misleading because they test a workload that lives entirely in L1 cache and ignores memory bandwidth and I/O, which is where real-world gains (or losses) happen.
Test your actual application under a production-like load; synthetic benchmarks lie.
What Compiler Flags Actually Matter for Graviton?
For compiled languages like Go, Rust, or C++, the single most important optimization is telling the compiler which specific CPU it’s building for. Use the -march=native flag to allow the compiler to generate instructions that leverage newer features of the Arm architecture, like the Scalable Vector Extension (SVE). This often yields a bigger performance boost than tweaking optimization levels.
Without this flag, the compiler targets a generic arm64 baseline, leaving performance on the table.
Here’s the change in a typical Makefile:
1
2
3
4
5
6
7
8
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,3 @@
CC=gcc
-CFLAGS=-O2 -Wall
+CFLAGS=-O3 -march=native -Wall
myapp: myapp.c
For interpreted languages like Python, Ruby, or Node.js, you don’t need to worry about compiler flags for your own code. The performance gains come from ensuring your runtime (e.g., the Python interpreter, the Node.js binary) is a modern version compiled specifically for Arm64. Use official arm64 builds and you’ll inherit the benefits automatically.
For C++/Rust/Go, always compile with
-march=nativeon the build server to let the compiler use the best instructions for the target CPU.
Is Your Workload Ready for Graviton5?
Before you commit to a migration, run through this checklist. If you can’t check at least three of these, you may not see the performance gains you’re hoping for.
- My workload is multi-threaded, multi-process, or handles many concurrent requests.
- My application is CPU-bound or memory-bound, not primarily I/O-bound.
- I have an established performance baseline (requests/sec, latency) on my current instance type.
-
My entire software stack, including OS, runtimes, and libraries, has official
arm64support. -
For compiled code, my build pipeline can use target-specific flags like
-march=native.
This isn’t about just lifting and shifting. It’s about shifting to an architecture that rewards parallelism and efficient memory access.
In our next post, we’ll dive deep into specific perf and eBPF tooling you can use to find the exact functions in your code that are CPU-bound and ripe for optimization on Graviton5.
Bottom Line
Graviton5 delivers on its performance promises, but only for the right workloads. Stop treating it like a magic button and start treating it like an architectural choice. If your application is bottlenecked by CPU or memory bandwidth, you’ll see substantial price-performance improvements. If it’s waiting on a disk or a network call, you’re just paying for cores you can’t use.
FAQ
Is Graviton5 cheaper than Graviton4?
No, M9g instances typically have a higher on-demand price than comparable M7g instances. The value proposition is in price-performance: you can potentially run your workload on a smaller instance or fewer instances, leading to an overall cost reduction.
Do I need to recompile my application for Graviton5?
If you are already running on a Graviton instance, a recompile is not strictly required but is highly recommended. Using a modern compiler with flags like -march=native enables the binary to use instruction sets available on newer processors for a significant performance boost.
What is the main difference between Graviton4 and Graviton5?
The key architectural upgrades in the Graviton5 processors powering M9g instances are support for DDR5 memory, which offers 50% more bandwidth than the DDR4 used with Graviton4, and a higher physical core count per socket.
Can I run Docker containers on Graviton5?
Absolutely. As long as your Docker image is built for the linux/arm64 architecture, it will run without modification. Most official upstream images (like node, python, golang) provide multi-architecture manifests, so docker pull automatically fetches the correct version.
What’s the easiest way to build a multi-arch Docker image?
Use docker buildx. The command docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest . --push will build your image for both architectures and push them to the registry under a single tag.
Further Reading
- https://aws.amazon.com/ec2/graviton/
- https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/graviton-instances.html
🚀 Ready to get hands-on? Spin up an interactive AI or Kubernetes Sandbox at Aicademy Labs for free.
