Daniel Broadhurst

ArticlesAWSServerless

Direct, SQS, EventBridge or Step Functions? What 160 Lambda Benchmarks Say

27 Sept 2026 · 6 min read

Ink sketch of four railway tracks leaving one station: a direct line, a platform with a queue of parcels, a signal box, and a toll booth where a small figure collects a fee

Most teams choose how to chain their Lambda functions by habit. Step Functions because the docs recommend it, EventBridge because it feels decoupled, a queue because that’s what the last project used. I did it too, for years.

So for my MSc in Software Engineering I measured it instead. I recently finished, with a distinction, and the results changed how I design serverless systems. This post is the practical version: what I tested, what surprised me, and the rules I use now.

If you’d rather skip to the answer, the decision tool applies everything below to your workload.

What I tested

Four ways to chain five Lambda functions together:

  • Direct invocation: each function calls the next in code.
  • Queue (SQS): each function drops a message for the next.
  • Event-driven (EventBridge): each function publishes an event a rule routes onwards.
  • Step Functions: a state machine runs the chain.

Each chain ran two kinds of work. A CPU-bound task (a Fibonacci busy loop) and an I/O-bound task (waiting on a simulated external API). I varied memory from 128 MB to 3,008 MB and concurrency from 1 to 50, and ran everything with and without Provisioned Concurrency.

Every resource was defined in AWS SAM and every run sent a fixed number of requests, so strategies compared fairly. That came to 160 run summaries of latency, billed duration, cold starts and cost.

Finding 1: managed orchestration charges a routing tax

This was the headline. EventBridge and Step Functions add roughly 1.5 seconds to a chain that direct invocation and SQS don’t pay. It isn’t your code; it’s the platform routing work between your functions.

Mean end-to-end latency for a five-function CPU-bound chain: direct invocation 553 ms and SQS 561 ms, against Step Functions 1.31 s and EventBridge 2.09 s, most of it spent between functions.
Fig. 1 · Five-function chain, CPU-bound, 512 MB, 50 concurrency
Strategy Mean latency Cost per million requests
Direct invocation 553 ms $0.88
Queue (SQS) 561 ms $0.86
Step Functions 1.31 s $1.78
EventBridge 2.09 s $2.44

The gap held for I/O-bound work too, and it didn’t care what the functions were doing. In a separate cross-check on CPU-bound work, direct invocation was 52.6% faster and 43.3% cheaper than Step Functions.

Step Functions has a second cost that hides until scale: it bills per state transition, so a five-step chain pays at least five transition fees on every run, however quick the functions are.

The routing tax sets a floor on how small your functions should be. If a function runs for 50 ms and the hop to the next one costs 1,500 ms, the platform spends thirty times longer moving work than doing it. Under about half a second of work per function, heavy orchestration is the wrong tool; merge the functions or chain them directly.

Finding 2: memory has a sweet spot, and it depends on the work

Lambda gives you CPU in proportion to memory, so more memory usually means faster functions. For CPU-bound work, up to a point:

Memory Mean latency Cost per million requests
128 MB 738 ms $1.90
512 MB 415 ms $1.76
1,024 MB 243 ms $1.86
3,008 MB 219 ms $3.03

512 MB was the cheapest configuration of all, and 44% faster than 128 MB. Past about 1.8 GB the function gets a full vCPU, and a single-threaded Node.js function can’t use a second one: going from 1,792 MB to 3,008 MB bought 23 ms and cost 18% more.

I/O-bound work was the opposite. Latency sat at about 1.3 seconds at every memory size, because the function spends its time waiting on the network, not computing. More memory only raised the price: 3,008 MB cost around 19 times as much as 128 MB for the same speed.

So there’s no single right memory setting. Give CPU-bound functions 512 MB and I/O-bound functions the minimum. Over-provisioning “to be safe” is pure waste on network-heavy functions, in money and in energy.

Finding 3: queues catch up

I expected SQS to be the slow, resilient option. At low concurrency it was: with one request at a time, a queued chain took 1.9 seconds against 1.7 for direct invocation.

At 50 concurrent executions, it finished within 0.6% of direct invocation. AWS’s pollers scale out to meet the traffic, and once they’re warm the queue costs almost nothing in latency while giving you buffering, retries and dead-letter queues.

That’s the reason SQS is my default now. It’s nearly as fast and cheap as calling functions directly, and it doesn’t fall over when a downstream service does.

Finding 4: Provisioned Concurrency is a capacity, not a switch

Provisioned Concurrency keeps functions warm. It worked: cold starts fell from 39% of requests to 3 to 4%.

The remaining cold starts weren’t bad luck. I’d provisioned 10 instances, and bursts of 100 requests overflowed them onto ordinary on-demand instances, cold starts and all. Provisioned Concurrency only protects traffic up to the capacity you buy, so size it to your peak burst, not your average.

The cost can invert, too. At low volume, paying for idle warm instances raised unit costs two to three times; with EventBridge, cost per million requests went from $0.86 to $6.10.

Paired with a queue, though, it shone. The queue smooths bursts so the warm instances stay busy without overflowing: latency fell 87.5% (5.1 s to 0.64 s) and cost per million requests halved.

The decision framework

Put together, the results give a short set of rules:

When you want Use The catch
The lowest latency or cost Direct invocation (512 MB for CPU work, 128 MB for I/O) No backpressure: one failure stops the chain
Resilience and control SQS Pollers need to be warm
Many consumers for one event EventBridge A ~1.5 s tax, so never for linear chains
Auditing and complex branching Step Functions The highest cost and a ~1.5 s floor

Default to the simple patterns. Reach for EventBridge or Step Functions when the workflow genuinely needs fan-out or visual state, and treat the extra 1.5 seconds and the bill as what you’re paying for that.

Try the framework on your own workload →, or review a whole workflow hop by hop and take away a decision record for your team.

Where this stops

It’s AWS only. The routing tax is an implementation detail of EventBridge and Step Functions; Azure Durable Functions and Google Cloud Workflows need their own measurements. I tested linear chains up to 50 concurrent executions, not complex branching or thousands of users, and the numbers assume warm infrastructure. Sporadic traffic can leave queues and event buses cold.

What the research taught me

The best lesson wasn’t in the numbers. Twice, my first results looked like serverless being flaky: a huge latency spike for queues at moderate load, and Provisioned Concurrency apparently failing. Both turned out to be real mechanisms, throttled pollers and capacity overflow, and both became findings.

An odd result is usually a system telling you something. And because the whole environment was infrastructure as code, re-running a test group to check a theory took minutes rather than days. That’s the part I’ve carried straight back into everyday engineering.

ShareLinkedIn

Liked this?

Get the next field note

New posts and tools on engineering with AI agents and serverless, when they're ready. No schedule, no spam.

RSS feedFollow on LinkedIn

Related reading