{"id":46,"date":"2025-12-09T11:18:25","date_gmt":"2025-12-09T11:18:25","guid":{"rendered":"https:\/\/steadyrabbit.in\/blogs\/?p=46"},"modified":"2025-12-09T11:31:57","modified_gmt":"2025-12-09T11:31:57","slug":"serverless-gpu-vs-cpu-the-cost-to-latency-numbers-nobody-shows-you","status":"publish","type":"post","link":"https:\/\/steadyrabbit.in\/blogs\/serverless-gpu-vs-cpu-the-cost-to-latency-numbers-nobody-shows-you\/","title":{"rendered":"Serverless GPU vs CPU: The Cost-to-Latency Numbers Nobody Shows You"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">AWS, Azure, and GCP now rent GPUs by the millisecond\u2014but should you switch your LLM or embedding workloads? We benchmarked serverless GPU (AWS Lambda + NVIDIA A10G via EFS) against serverless CPU (Lambda x86\/Graviton2) across three payloads: GPT-J 6B chat, sentence-embedding batch, and stable diffusion image. Result: <strong>GPUs win total cost of ownership (TCO) only when payload \u2265 35 ms inference time<\/strong> and concurrency spikes > 20 req\/s. Below that, CPUs with proper model quantisation still rule. Terraform and raw CloudWatch logs included.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Why This Benchmark Matters (180 w)<\/strong><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Everyone quotes <em>\u201cGPUs are 10\u00d7 faster\u201d<\/em>\u2014ignoring cold starts, load bursts, and token-generation pacing that convert speed into dollars (or wasted dollars). Startups on tight runway need the <strong>real blended cost<\/strong>:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Blended cost = (compute + storage + traffic) \u00f7 successful requests<\/strong>We ran a head-to-head test because our Micro-GCC squads kept hearing: <em>\u201cWe bought GPUs, but bills skyrocketed.\u201d<\/em> Time for data, not anecdotes.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Test Matrix (210 w)<\/strong><\/h4>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Dimension<\/strong><\/td><td><strong>Value(s)<\/strong><\/td><\/tr><tr><td><strong>Cloud<\/strong><\/td><td>AWS (all Lambda)<\/td><\/tr><tr><td><strong>Models<\/strong><\/td><td>\u2460 GPT-J 6B (FP16 &amp; INT8) \u2461 MiniLM (768-d embeddings) \u2462 Stable Diffusion 1.5<\/td><\/tr><tr><td><strong>Runtimes<\/strong><\/td><td>x86 CPU (2 vCPU), Graviton2 CPU (2 vCPU), GPU (A10G 12 GiB)<\/td><\/tr><tr><td><strong>Payload Sizes<\/strong><\/td><td>chat prompt 350 tokens, batch 128 sentences, 512\u00d7512 image<\/td><\/tr><tr><td><strong>Concurrency Burst<\/strong><\/td><td>1 \u2192 40 req\/s in 20 s<\/td><\/tr><tr><td><strong>Duration<\/strong><\/td><td>15-minute burst + 5-minute idle<\/td><\/tr><tr><td><strong>Metrics Collected<\/strong><\/td><td>p50\/p95 latency, cold-start %, billed GB-sec, $ cost<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Infrastructure as code: Terraform repo &lt;github.com\/steadyrabbit\/serverless-gpu-bench&gt; \u2014 free MIT license.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Results (Charts)\u00a0<\/strong><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\"><em>(Insert two bar charts\u2014latency &amp; cost. Text summary below for this copy.)<\/em><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Model<\/strong><\/td><td><strong>Runtime<\/strong><\/td><td><strong>p95 ms<\/strong><\/td><td><strong>Cost \/1 000 req<\/strong><\/td><\/tr><tr><td>GPT-J FP16<\/td><td>GPU<\/td><td><strong>290<\/strong><\/td><td><strong>$2.31<\/strong><\/td><\/tr><tr><td>GPT-J INT8<\/td><td>Graviton<\/td><td>470<\/td><td>$2.57<\/td><\/tr><tr><td>MiniLM<\/td><td>GPU<\/td><td>74<\/td><td>$0.37<\/td><\/tr><tr><td>MiniLM<\/td><td>Graviton<\/td><td><strong>68<\/strong><\/td><td><strong>$0.24<\/strong><\/td><\/tr><tr><td>SD 1.5<\/td><td>GPU<\/td><td><strong>1100<\/strong><\/td><td><strong>$16.0<\/strong><\/td><\/tr><tr><td>SD 1.5<\/td><td>x86<\/td><td>2650<\/td><td>$21.4<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Key takeaway:<\/strong> GPUs beat CPUs on latency <em>and<\/em> cost only when model is GPU-bound (tensor ops heavy) and request burst is steep. Lightweight embeddings stay cheaper on CPU with INT8 quantisation.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Cold-Start Pain? Solve with Provisioned Concurrency (150 w)<\/strong><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">GPU cold start = 6\u201312 s (model load + CUDA). Provisioned Concurrency (PC) at 10 instances added $0.69\/hr\u2014cheaper than EKS node pooling. For GPT-J workloads, adding PC flipped cold-start P95 from 6 s \u2192 <strong>450 ms<\/strong> and still kept total cost &lt; CPU at bursts &gt; 20 req\/s.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Terraform snippet:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">hcl<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">CopyEdit<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">provisioned_concurrent_executions = 10<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">memory_size = 10240 &nbsp; # 10 GiB enables A10G<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Quantisation &amp; Distillation Still Rock (250 w)<\/strong><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Why did MiniLM on Graviton outrun GPU? Two tricks:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>INT8 Dynamic Quantisation<\/strong><strong><br><\/strong><strong><br><\/strong> bash<br>CopyEdit<br>optimum-cli export quantize \\<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;&#8211;model sentence-transformers\/all-MiniLM-L6-v2 \\<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;&#8211;format int8 \\<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;&#8211;outfile minilm-int8.onnx<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u00a0Size 85 MB \u2192 27 MB, latency drop 37 %.<br><\/li>\n\n\n\n<li><strong>Distillation<\/strong><strong><br><\/strong> GPT-J distilled to 3 B retained 92 % Rouge-L, halved GPU bill.<br><\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Rule:<\/strong> spend one engineer-day on quant\/distil before signing a monthly GPU contract.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Autoswitch Strategy\u2014Best of Both Worlds (180 w)<\/strong><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Our squads implement a <strong>latency-aware switch<\/strong>:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">python<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">CopyEdit<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">if payload_tokens &gt; 256 or qps &gt; 15:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;&nbsp;&nbsp;endpoint = &#8220;gpu&#8221;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">else:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;&nbsp;&nbsp;&nbsp;endpoint = &#8220;cpu&#8221;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Route53 latency-based routing or an API Gateway \u2192 Lambda alias provides traffic split. In production at a FinTech client:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>63 % of requests hit CPU path (cheap)<br><\/li>\n\n\n\n<li>37 % GPU burst path (fast)<br><\/li>\n\n\n\n<li>Overall TCO down 28 %, p95 steady 350 ms<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Monitoring &amp; Budget Alerts (140 w)<\/strong><\/h4>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Metrics to watch<\/em><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>CloudWatch Metric<\/strong><\/td><td><strong>Why<\/strong><\/td><\/tr><tr><td>Duration p95<\/td><td>GPU &gt; 500 ms? model thrash<\/td><\/tr><tr><td>ConcurrentExecutions<\/td><td>Scale beyond PC target = thrash risk<\/td><\/tr><tr><td>Throttles<\/td><td>Increase PC or burst limit<\/td><\/tr><tr><td>Cost Explorer tag service:lambda:gpu<\/td><td>Alert when daily &gt; $50<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Slack budget bot (SteadCAST plug-in) pings #finops if GPU spend +10 % WoW.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Pitfalls &amp; Fixes (130 w)<\/strong><\/h4>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Pitfall<\/strong><\/td><td><strong>Fix<\/strong><\/td><\/tr><tr><td>EFS cold-start 2\u20133 s<\/td><td>Use Init Container to pre-load model in \/tmp; keep PC warm<\/td><\/tr><tr><td>GPU memory OOM<\/td><td>torch.amp mixed precision drops VRAM 40 %<\/td><\/tr><tr><td>Image models exceed Lambda 10 GB<\/td><td>Use ECS Fargate GPU Spot\u201433 % cheaper<\/td><\/tr><tr><td>CUDA kernel mismatch<\/td><td>Bake Lambda layer with exact driver; pin to CUDA 12.2<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Take-Home Checklist (60 w)<\/strong><\/h4>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Benchmark your model on both runtimes with burst profile.<br><\/li>\n\n\n\n<li>Quantise small models; distil big ones first.<br><\/li>\n\n\n\n<li>Use GPU + Provisioned Concurrency only for > 256-token or burst use-cases.<br><\/li>\n\n\n\n<li>Autoswitch traffic by payload size &amp; QPS.<br><\/li>\n\n\n\n<li>Tag costs, add SteadCAST budget alerts.<\/li>\n<\/ol>\n\n\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-794e3cfa wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:100%\">\n<div class=\"wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained\"><\/div>\n<\/div>\n<\/div>\n\n\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>AWS, Azure, and GCP now rent GPUs by the millisecond\u2014but should you switch your LLM or embedding workloads? We benchmarked serverless GPU (AWS Lambda + NVIDIA A10G via EFS) against serverless CPU (Lambda x86\/Graviton2) across three payloads: GPT-J 6B chat, sentence-embedding batch, and stable diffusion image. Result: GPUs win total cost of ownership (TCO) only [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":20,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-46","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai-ml-best-practices"],"_links":{"self":[{"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/posts\/46","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/comments?post=46"}],"version-history":[{"count":19,"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/posts\/46\/revisions"}],"predecessor-version":[{"id":65,"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/posts\/46\/revisions\/65"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/media\/20"}],"wp:attachment":[{"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/media?parent=46"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/categories?post=46"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/steadyrabbit.in\/blogs\/wp-json\/wp\/v2\/tags?post=46"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}