Serverless applications are built by chaining small functions into workflows, but today's orchestrators run them control-flow style — a central controller waits for a function to finish, saves its output to remote storage, then triggers the next function to reload it — which double-copies every intermediate result through slow storage, idles the CPU while the network works and vice versa, and delays each trigger by tens of milliseconds of bookkeeping; DataFlower (Shanghai Jiao Tong) flips the whole thing to data-flow, splitting each container into a compute unit and a data unit so data streams directly function-to-function the instant it is ready while the next request already runs — cutting 99th-percentile latency up to 35.4%, lifting peak throughput up to 3.8×, and using up to 69.3% less memory.
The argument in one glance
-
Problem
A workflow that stops and files paperwork between every step
Serverless computing runs small functions that auto-scale, and complex applications are built by chaining functions into WORKFLOWS — a video pipeline, an image-processing chain, a map-reduce. The platform orchestrates them so the programmer only declares dependencies. But the standard orchestrator works control-flow style: a central controller watches each function's completion status and triggers the next one only when its predecessors report done. Between every pair of functions, the intermediate data is written to remote storage by the sender and reloaded by the receiver, and the controller does tens of milliseconds of state bookkeeping. For functions that themselves run only briefly, this overhead is the dominant cost.
-
What people did
Speed up the control-flow pipeline
Prior systems attacked pieces of this. FaaSFlow decentralized the scheduler and cached data in local memory for functions that happen to be co-located on the same node. SONIC caches a function's output in its own container and lets the destination fetch it peer-to-peer when triggered, skipping remote storage. Both keep the control-flow shape — controller triggers, then data moves — but make the data movement cheaper.
-
Why it fell short
The overhead is baked into the control-flow shape itself
Even with faster data movement, three costs survive because they come from the paradigm, not the implementation. DATA PERSISTENCE: intermediate data is still transferred twice (source→store, store→destination) over bandwidth-capped container links, and this communication is 26–89% of end-to-end latency in real benchmarks. SEQUENTIAL RESOURCE USE: because load-compute-store run in strict order inside a function, the CPU sits idle while the network works and the network sits idle while the CPU works — one resource is always wasted. LATE TRIGGERING: the controller triggers functions in topological order and only after full completion, so a function waits ~63ms of state management even when its input was ready earlier. Caching data faster does not overlap compute with communication, nor trigger functions early.
-
This paper's idea
Switch paradigms: trigger on data, stream it, overlap everything
DataFlower (Shanghai Jiao Tong University) abandons control-flow for the DATA-FLOW paradigm — a function runs the instant all its input data is ready, with no central controller in the loop. The enabling abstraction splits each container into a FUNCTION LOGIC UNIT (FLU) that runs the code and a DATA LOGIC UNIT (DLU) that moves data asynchronously, so a container can compute the next request while the DLU still streams the previous request's output — compute and communication overlap. Data flows directly function-to-function (no round trip through storage) via a streaming pipe connector, starting the moment a chunk is generated. Because the destination container may not exist yet, a host-side "data sink" caches in-flight data with a time-to-live until the function is triggered. A pressure-aware scaler adds containers when a DLU's outflow lags its inflow. Result versus state-of-the-art serverless: up to 35.4% lower 99th-percentile latency, up to 3.8× peak throughput, up to 69.3% less memory.
Why this problem is worth solving
Serverless has become the default way to run event-driven
cloud code, and the way complexity gets expressed is the
a complex application decomposed
into many small functions chained by their dependencies, where
the cloud platform (AWS Step Functions, Alibaba Serverless
Workflow, OpenWhisk) handles the orchestration, the
intermediate data passing, and fault tolerance — so the
programmer only declares which function feeds which. A video
service becomes decode → process → encode; an image pipeline
becomes a fan-out of transforms then a merge. The functions
are deliberately small and short-lived, which is what makes
per-step overhead loom so large
a central controller (or
“orchestrator”) that maintains every function’s state and
triggers functions in topological order: when all of a
function’s predecessors report COMPLETE, the controller
triggers it. Each function then loads its inputs from backend
storage, runs, and stores its outputs back to storage for the
next function to reload. The controller is the conductor;
functions are triggered by control signals about
completion
The paper’s framing is that these are not implementation
warts but consequences of the control-flow SHAPE, so the
faster-caching fixes (FaaSFlow, SONIC) hit a ceiling: they
still move data only after the controller triggers, still run
load-compute-store in sequence, still wait on completion
signals. The alternative is a different orchestration
philosophy borrowed from computer architecture’s own history
—
an execution model where a
function is triggered by the READINESS of its input data
rather than by a control signal from a scheduler. The
dependencies form a data-flow graph; the moment all of a
function’s inputs have arrived, it fires, with no central
conductor. This is the same idea that gave processors
out-of-order execution: stop enforcing program order, let
each operation proceed as soon as its operands are ready, and
parallelism emerges for free. DataFlower brings that
instruction-level insight up to the granularity of serverless
functions
What people did before, and where it broke
- Control-flow orchestrators (AWS Step Functions, OpenWhisk, the default). A central controller triggers on completion and routes all intermediate data through backend storage. Simple and correct, but pays all three taxes — double data transfer, staggered resources, late triggering — in full.
- Decentralized scheduling with local caching (FaaSFlow). Removes the central-scheduler bottleneck and passes data through local memory for co-located functions. Real gains, but data still moves only after triggering, and compute/communication still do not overlap.
- Application-aware peer-to-peer data passing (SONIC). Caches output in the source container and lets the destination fetch it directly, skipping remote storage. But each container’s bandwidth is capped, so under parallel load the direct transfers queue — SONIC optimizes a single invocation and cannot see the transmission bottleneck when many invocations scale out.
- Stateful-function data caches (AWS Step Functions state machine). Pass small data (under 256 KB) without storage. Helps tiny payloads; large intermediate data still goes through remote storage.
- The gap. No serverless system separated a function’s compute logic from its data-transfer logic, so none could overlap the two or trigger on data readiness. The computation and communication were tangled inside the user code, and control-flow orchestration sequenced them. That is the coupling DataFlower breaks.
How DataFlower works
Split the container, stream the data, trigger on readiness
-
Declare a data-flow graph new
Instead of control dependencies, the programmer writes functions that call DataFlower.DLU.Put(data) whenever an output is ready, and the platform builds a data-flow graph recording each function's input sources and output destinations. A function with multiple possible destinations (a switch) picks the right one from the data itself, so dynamic graphs are natural.
-
FLU: the function logic unit new
Each container's compute half. The FLU runs the user function code — nothing else. A container can hold multiple FLUs (multiple in-flight requests, or a function split for intra-function pipelining), and because the FLU no longer blocks on data transfer, it can start the NEXT request's computation while the previous request's data is still being shipped out.
-
DLU: the data logic unit new
Each container's data half, running ASYNCHRONOUSLY from the FLU. The DLU takes the FLU's output and pushes it toward the destination functions per the data-flow graph — the moment a Put() fires, without waiting for the function to finish. This decoupling is what makes compute and communication overlap: FLU computes, DLU communicates, concurrently.
-
Pipe connector: stream, do not store new
Between a source and destination function, a streaming pipe connector (built on Kafka) carries data directly — no round trip through remote storage. Streaming means a data chunk can move before the whole output is generated. Same-node pairs use a fast local pipe straight into the data sink; cross-node pairs stream over the network.
-
Host-container data sink: catch data before the catcher exists new
Data-flow creates a hazard: the destination container may not be started yet when data arrives (serverless cold starts). So each host node keeps a data SINK that accepts and caches in-flight data for all its functions, holding it with a time-to-live — proactively released once the destination loads it, passively expired (spilled to disk) on timeout — so a freshly-started container instantly finds its inputs waiting, with bounded memory.
-
Pressure-aware scaling new
A new bottleneck appears: if a FLU generates data faster than its DLU can ship it (bandwidth is capped per container for fairness), backpressure builds and requests queue. A decentralized engine monitors each DLU's outflow-versus-inflow pressure and scales OUT more containers for that function when its data stream backs up — reacting to communication pressure, not just CPU load.
The idea
Out-of-order execution, lifted from instructions to functions. The paper’s deepest move is recognizing that serverless workflow orchestration made the same mistake early processors made: enforcing program order. A control-flow orchestrator triggers functions in topological sequence and waits for completion, exactly as an in-order pipeline stalls until each instruction retires. Computer architecture solved this decades ago with data-flow execution — fire an operation the moment its operands are ready, and parallelism falls out. DataFlower applies the identical principle at the granularity of functions: trigger on data readiness, not on a controller’s completion signal, and the workflow’s inherent parallelism (compute overlapping communication, downstream functions starting early) emerges without a conductor. The site has seen this reify-a-known-formalism pattern before; here the borrowed formalism is the dataflow machine.
Split the container so its two halves can run at once. The enabling abstraction is almost embarrassingly simple and is the whole reason the overlap is possible: a serverless function’s code tangles computation with data transfer, so as long as they live in one thread they must take turns — that is the sequential-resource waste. DataFlower cleaves the container into an FLU (compute) and a DLU (data), running asynchronously, so the CPU-bound work and the network-bound work proceed concurrently and each container’s two resources stop idling in alternation. Everything else — streaming, early triggering, the data sink — is machinery to exploit the concurrency this split unlocks. It is the same decouple-the-tangled-logic move that separates a control plane from a data plane, applied inside a single container.
Data-flow is faster, but it breaks assumptions the whole stack relied on — so re-earn them. The elegant part is that switching paradigms is not free: data-flow assumes the receiver is ready to receive, but serverless containers are created and recycled on demand, so the destination often does not exist when its data is produced. Control-flow never had this problem because it triggered the destination FIRST. The data sink re-earns that guarantee from the other direction — cache the data host-side until the container appears, with a time-to-live so the cache stays bounded — and pressure-aware scaling re-earns load balancing for a world where the new bottleneck is a backed-up data stream rather than a busy CPU. The contribution is not just adopting data-flow but paying back the invariants control-flow provided for free.
A two-function chain under load
FunA feeds FunB, control-flow versus DataFlower
A workflow where FunA's output feeds FunB, running many requests. Numbers from the paper's benchmarks (video, image, SVD, word-count workflows) on a 5-node cluster.
Step 1 of 5
Control-flow: the resource seesaw
FunA loads its input from remote storage (network busy, CPU idle), computes (CPU busy, network idle), then stores its output back to remote storage (network busy, CPU idle). Only after the store completes and the controller registers FunA "done" — about 63ms of state bookkeeping later — does it trigger FunB, which loads that same output from storage all over again. The data crossed the network twice, and every phase wasted one resource.
communication is 26–89% of end-to-end latency across the four real benchmarks profiling: either CPU or network is idle throughout each container's life — never both busy
Why it works
- Control-flow routes intermediate data through remote storage twice → data-flow streams it directly function-to-function via a pipe connector → the data crosses the network once and communication, the 26–89% latency share, shrinks.
- Compute and data transfer are tangled in one thread under control-flow, forcing them to alternate → splitting the container into an async FLU and DLU lets them run at once → each container’s CPU and network stop idling in turn, and the next request starts before the previous one’s data finishes shipping.
- A central controller triggers in topological order after completion → data-flow fires each function the instant its inputs are ready → the ~63ms per-hop bookkeeping vanishes and downstream functions start early.
- Data-flow risks data arriving before the destination container exists → a host-side data sink caches in-flight data with a time-to-live → a cold-started container finds its inputs waiting locally, with bounded memory.
- Per-container bandwidth caps create a data-stream bottleneck invisible to CPU autoscalers → pressure-aware scaling monitors DLU outflow-versus-inflow and scales on communication backpressure → throughput keeps climbing under parallel load where prior systems stall.
What it costs
- A programming-model change. Functions must call DLU.Put() to emit outputs and signal an end for terminal functions — a redesigned interface, not a drop-in for existing control-flow workflow code, though the paper argues it maps naturally onto workflow logic.
- New moving parts to operate. A streaming pipe connector (Kafka), a host-side data sink (Redis + SSD), and a pressure-aware scaler are more infrastructure than a plain orchestrator, and their fault tolerance is inherited from those underlying systems rather than designed afresh.
- Memory for in-flight data. The data sink holds data the destination has not yet consumed; the time-to-live keeps it bounded, but a badly tuned TTL trades cache hit rate against memory (too short spills useful data to disk, too long wastes memory).
- Evaluation scope. A 5-node cluster with four workflows against two academic baselines; production serverless at scale, across heterogeneous hardware and adversarial bursts, is beyond what is shown, and the gains vary widely by workflow (latency 5.7% to 35.4%).
- Backpressure is managed, not eliminated. Pressure-aware scaling reacts to a stream backing up; a sudden burst still incurs some queuing before the scaler responds, and scaling out consumes resources of its own.
The evaluation
Four real serverless workflows (video-FFmpeg, ML image processing, singular value decomposition, word count) on a 5-node cluster, against FaaSFlow (decentralized scheduling, local-memory caching) and SONIC (peer-to-peer output fetching), under both synchronous (closed-loop, for peak throughput) and asynchronous (open-loop, for tail latency) invocation.
Three ways to run a serverless workflow
| Property | Control-flow orchestrator (default) | Faster control-flow (FaaSFlow / SONIC) | DataFlower (data-flow) |
|---|---|---|---|
| What triggers a function | |||
| How intermediate data moves | |||
| Compute / communication overlap | None — load, compute, store run in sequence; one resource always idle | ||
| Behavior under parallel / bursty load | Requests queue; storage contention worsens | ||
| Results (vs FaaSFlow / SONIC) | Reference (worse than both) | Improve on control-flow but hit the paradigm ceiling |
Check yourself
Check your understanding
1. FaaSFlow and SONIC already make serverless data movement much cheaper (local caching, peer-to-peer fetch), yet DataFlower argues their approach hits a ceiling. Predict which costs survive faster data movement, and why.
2. DataFlower splits each container into a Function Logic Unit (FLU) and a Data Logic Unit (DLU). Why is this seemingly simple split the load-bearing idea that makes everything else possible?
3. Switching to data-flow creates a new hazard control-flow never had: data can arrive at a destination whose container does not exist yet. Predict why this happens and how DataFlower re-earns the guarantee control-flow provided for free.
4. DataFlower's memory savings (up to 69.3%) are even LARGER than its latency savings (up to 35.4%). Predict why the two diverge, given how the benchmarks are structured.