Python as Orchestrator: When to Glue, When to Compute - Freya Bhushan Mehta - 2026
Watch on YouTubeVideo summary
The video addresses a common debate in software engineering regarding when to use Python versus compiled languages like C++, Rust, or Go for specific tasks. The speaker argues that while compiled languages are superior for raw computational speed due to factors like ahead-of-time compilation and efficient memory management, Python excels as an orchestrator. This distinction is crucial because modern systems often involve complex data pipelines with four stages: acquisition, transformation, computation, and output. In these scenarios, the goal isn't necessarily to make every part fast, but to determine where heavy lifting should occur versus where logic coordination happens. The core insight presented is that Python's true strength lies in managing workflows, coordinating services, and handling frequently changing business logic rather than performing intensive numerical calculations directly.
Throughout a typical data pipeline, different stages dictate the choice of language based on bottlenecks and requirements. For instance, during data acquisition where systems wait for network responses or handle diverse APIs, Python is ideal because it efficiently manages I/O without consuming CPU cycles. Similarly, in the transformation stage involving complex cleaning rules that evolve often, Python's readability allows developers to adapt quickly without recompiling code. However, when dealing with high-volume binary streams or stable mathematical operations like Monte Carlo simulations and matrix calculations, compiled languages are necessary to avoid performance degradation caused by interpretation overhead. The speaker emphasizes using libraries like NumPy and PyTorch which allow users to write clean Python syntax while leveraging the underlying speed of compiled C++ code for vectorizable operations.
A critical technical challenge discussed is the "boundary tax" incurred when crossing between Python and compiled environments, particularly in tight loops where function calls dominate execution time over actual computation. The speaker illustrates that calling a fast C++ function thousands of times from Python can be slower than doing nothing due to memory management costs and type conversions at each boundary crossing. To mitigate this, the recommended approach is to redesign interfaces so that large batches of data are processed in single calls rather than iterating individually. This requires significant infrastructure investment but ensures that performance gains from compiled code are not wasted on overhead. Consequently, systems should be designed with "chunky" interfaces where Python orchestrates multiple tasks and hands off massive payloads for processing before collecting results in one go.
In conclusion, the talk reframes the narrative around Python's speed by asserting that it is perfectly suited for its intended role as an orchestration language rather than a general-purpose compute engine. The industry standard pattern seen in major frameworks like TensorFlow and PyTorch relies on this separation of concerns: Python handles the glue logic, user interaction, and ecosystem integration, while compiled code performs the heavy mathematical lifting underneath. When developers face skepticism about using Python for high-performance tasks, they should evaluate whether their system is primarily orchestrating or computing; if it involves coordinating components and managing dynamic workflows, Python is not too slow but rather doing exactly what it was designed to do. Ultimately, a smart architecture combines the flexibility of Python with the raw power of compiled languages without sacrificing maintainability or development velocity.
Read the full video transcript
It's a fairly common topic of of talk at
Python conferences about when you should
be doing things in rewriting things in
Rust or in C or whatever. And
this one really stood out to us this
year as something that really goes into
the why and when of when you should be
uh
when you should be doing things in
native code and when you should be doing
things in Python. And I'm really excited
to see this talk. So thank you for that.
Hi everyone.
Uh I think this one is a bit more
technical. This it's it's a bit more on
the technical side. So I'll
do my best to keep everyone awake. Um
my talk is on Python as orchestrator,
when to glue and when to compute. Um my
name is Freya Mehta. I'm a software
engineer at Bloomberg in London, where I
work on the derivatives pricing library
engineering team.
Uh my work lives in the intersection of
software engineering and quantitative
finance.
Uh we're building C++ and Python systems
that price and structure complex
financial instruments.
Um so whenever I tell people I code in
Python at work, I usually get a very
skeptical look. Uh and I know exactly
what they're thinking.
Um isn't Python too slow for the kind of
work I do with the real-time analytics
and low latency.
Um I mean slow compared to the compiled
languages like C++ here. Um
and if you're talking about raw
computation, they are absolutely right.
Um
so I like to give this an analogy to
people that if you think of CPU as a
high-speed chef,
uh C++ gives it a pre-written optimized
recipe in its native language, whereas
Python is more like someone standing
outside the kitchen reading instructions
out loud step by step while the chef is
waiting.
Um also quick note, I'll be using C++ as
my compiled language throughout the
talk, but the framework applies equally
to Rust, Go or anything else compiled.
Um the questions and the talk is more
about where to draw the boundary and not
which language is on the other side.
Um
so yes, if you care about
about squeezing out maximum performance
from the hardware,
Python is at a disadvantage and there
are real reasons for that, right? First,
it's compiled language translating
everything into optimized machine code
ahead of time, whereas Python interprets
bytecode at runtime.
Um second is static typing, right? It
static typing lets the compiler generate
tight specialized instructions. Uh and
Python inspects types at every
operation. Um C++ arrays sit in
contiguous memory that the CPU can blast
through, whereas Python lists are arrays
of pointers to scattered heap objects.
Um and fourth,
now historically the GIL has limited
CPython to one thread of Python bytecode
at a time. Um although Python 3.13 has
introduced an experimental free-threaded
mode that starts to address this, but
it's still not very well adopted. Um so
yes, Python is slower at raw
computation,
um but then why is Python everywhere?
Python powers Netflix's recommendation
engine.
Um it processes petabytes at Spotify and
it supports financial systems handling
trillions of dollars as well. Um the key
insight over here, I'll give you the key
takeaway right now. It's we use Python
for the logic, but the heavy computation
is done
the compiled code does the entire heavy
lifting for you.
>> [snorts]
>> Um so the question isn't is Python fast
enough, it's when should Python compute
and when should it just glue things
together. And that's what this entire
talk is about.
We'll walk through the while we walk
through the talk, we'll also build a
decision framework for choosing that
boundary, when to compute and when to
orchestrate, because getting it right
will let you build systems that are both
fast and easy to evolve.
Um so before we get into specifics, let
me just show you the shape of the
problem over here. Uh
because if you've written any kind of
data pipeline, you've already seen this.
Um there are four stages. Acquire data,
which is basically you're just pulling
external sources, APIs, databases and
message queues. Um transform, you're
preparing that data for computation.
Uh it's coordination work here, not the
math. Uh compute, this is the heavy
lifting stage where you can do Monte
Carlo simulations, there are matrix
operations, there are neural network
trainings as well, the actual work your
system exists to do.
And output, you're packaging results and
delivering them to consumers over here.
Uh but the same shapes shows up
everywhere. ML pipelines scrape data,
clean it, train models and serve
predictions. Video processing ingest
footage, normalizes formats, encodes and
streams. Um in my domain, the pipeline
looks like this. It we acquire market
data,
uh we calibrate mathematical models to
that data. We run pricing simulations,
compute risk sensitivities and finally
show them to the on the on our UI as
well.
Um now here's what makes this a Python
problem. At every stage, you face the
same question. Does this piece of logic
exist in
should it belong in Python or should it
belong to a compiled code?
Um and here's the key insight. The
answer will be different at every stage.
So let's just walk through the pipeline
from left to right and see what we can
learn.
Um on the left side of the pipeline is
data acquisition. This is the inlet
where your system first meets the chaos
of the outside world. Now the here the
choice between Python and compiled code
at this stage comes down to just one
question. What is your system actually
waiting on? If the bottleneck is the
network, then choose Python.
In this world, um
your code will spend most of its time
waiting.
Um waiting for the server, waiting for
uh the database query to return, wait
the actual work,
uh which would be translating schemas,
uh normalizing formats, it's all
coordination, none of it is computation
over here. Uh Python handles this very
naturally. Uh with asyncio especially, a
single thread can manage thousands of
open connections juggling all those IO
weights without burning CPU.
Uh and the coordination logic uh itself,
right? Um it's genuinely complicated,
but it's not very compute intensive. Uh
it needs to be correct, readable and
easy to change, not necessarily fast. So
at also at this stage, fast isn't about
um CPU cycles, it's about like the
throughput and the responsiveness.
Um and Python is fast enough to saturate
your network
uh network pipe
uh while providing the um operational
speed that's that's needed to survive
and adapt to the mess of the external
APIs.
Um and if the bottleneck is CPU,
choose or consider compiled code. Um
this is a different world. Over here,
your data arrives in a raw binary
stream, a high-frequency hardware uh
sensory telemetry, a dense binary
protocol.
You're not waiting on the network
anymore. You're racing to decode bits as
fast as they're arriving. Um there's
also a hybrid approach, by the way,
worth knowing about. Um you can have
like a small small compiled uh service
and which handles the binary decoding,
pushes clean data into a shared buffer
and your Python orchestrator reads from
that buffer to handle all the heavy
um high-level logic. Um
throughput
where it matters and flexibility
everywhere else.
So what should you ask yourself, right?
Um here's the framework for
uh
data acquisition. Uh basically questions
that you I want you guys to take back to
for your own projects over here. Um
first, what is the bottleneck, the
network or the CPU? If your code spends
most of its time waiting on IO, it's
coordination, Python excels here. If
it's racing decode to decode bytes,
that's computation. Consider compiled
code.
Second, how is the data structured? Uh
human-readable formats, JSON, CSV, SQL
results, they all play to Python's
strengths,
uh which has got mature libraries and
readable parsing code. Uh dense binary
formats with bit-level packing lean
towards compiled code, um
which can basically operate directly on
the raw memory.
Um third, how complex is your landscape,
which means how many sources or how
messy are they? If you're pulling from a
dozens of different APIs, each with its
own schemas and failure modes, Python's
readability and ecosystem make that
manageable. Uh if you have one or two
high-volume fire hoses, the problem is
throughput, not coordination. Um and
fourth, and this one is a very easy one
to look overlook. Um
how often does the ingestion logic
change? So data schemas evolve
constantly, right?
Um APIs get versioned, new sources
appear. If this logic lives in C++,
every change triggers a cycle,
recompile, relink, rebuild packages, a
full QA, coordinated deployment. Days,
sometimes even weeks. Uh
in Python, a developer can update a
schema mapping, test it against
production
uh data and even ship it in the same
afternoon.
So,
next before we get into the heavy
computation, there's a stage that often
gets overlooked, which is data
transformation.
Here you're just reshaping the data,
you're cleaning strings, you're
converting types. It's not very
glamorous,
but it's often where performance quietly
disappears.
The interesting thing about the
transformation stage is that the
decision isn't really about the speed
over here. It's about data volume and
logic complexity.
If you have 500 if and else rules for
cleaning messy data, handling edge
cases, normalizing fronts, that's
Python's strength. The logic is complex,
it changes often,
and the readability matters far more
than the raw throughput. But if you have
two simple rules and 10 billion rows to
apply them to,
the equation flips.
At that scale, even a small per row
overhead adds up. Now the good news is
that for most transformation work, you
don't actually have to choose between
Python and C++ because libraries like
NumPy and Pandas
let you stay in Python syntax while the
actual work runs in compiled code
underneath.
So the rule of thumb I would say is that
if your transformation is vectorizable,
you can basically express it in
as an operation on a whole column
rather than a loop over individual rows,
stay in Python with these libraries.
Um
You get compiled code performance with
Python's readability.
So the transform stage reinforces a
similar two questions.
Is the bottleneck in the logic
complexity or the data volume? And how
often does the transformation logic
change?
Complex, evolving cleaning rules belong
in Python. High volume, stable
transformations belong in compiled code.
Or in Python libraries that depend or
delegate to compiled code for you.
So now we move to the heart of the
pipeline, the computation. This is the
stage that justifies the existence of
everything else. Um
whether you're training a neural
network, encoding video frames, or like
me running a Monte Carlo simulation,
this is where C++ genuinely shines,
but it's worth understanding why. C++
gives you control over
over how the data sits in the memory.
So the CPU can grab it without waiting
on the RAM, that's cache locality.
It It lets you also use the SIMD
instructions that process multiple data
elements per instruction.
And it gives you more predictable
latency.
There's no garbage collector that might
pause your code mid simulation over
here.
But here's a very critical question that
a lot of people skip.
Is the math already solved in a library?
So if you're using a standard model,
a known neural network architecture or
like a linear regression or a
well-established pricing formula,
there's a very good chance that a C++
backed Python library already does it.
PyTorch, NumPy, scikit-learn, OpenCV,
these give you compiled code performance
with Python syntax. So you don't need to
write the C++ yourself.
You only need custom C++ when you're
writing a proprietary algorithm that no
existing library supports. And even
then, not all of it belongs in C++.
And now there's the final stage, output.
The results are ready.
Now they need to reach whoever or
whatever consumes them. This stage has
surprisingly a very clean decision rule.
Who is the consumer?
If the consumer is a person, a trader
looking at a dashboard, or data
scientist reading a report, a user
getting a API response,
Python is the clear choice.
The ecosystem for making data useful to
humans is unmatched. We have Matplotlib
for charts, fast APIs for fast API for
web APIs, Streamlit for dashboards.
That's that's completely Python's
territory.
But if the consumer is another machine,
a low latency automated system, a raw
binary stream to another server,
keep it in compiled code.
Don't convert data to a human-readable
format just to send it back to your
machine that doesn't really need it.
Another practical consideration is the
serialization tax.
So if your compute engine just finished
a massive calculation in let's say C++
and the result is like gigabytes of
data,
copying it across the Python boundary
just to write it to disk is wasteful.
Stay in C++ for the output,
but if the result is like a summary,
like a final price, or like a
classification label that you want to
share,
then hand it off to Python
because that'll be cheap and the
flexibility could be worth it.
Okay, now that we've talked about when
Python should glue and when it should
hand off to compiled code,
there's a critical detail that I've been
just glossing over so far.
I keep saying Python calls C++ like it's
free.
Like you just write a function call and
it works.
It does work, but it's not free.
There are several ways. First of all,
Python can connect to compiled code
depending on your language. For C++, you
have PyBind11 or Boost.Python.
Yeah, for Rust you have PyO3 and
Rust-CPython. And for Go you have Cgo
with Python bindings.
Now all of these share a common
characteristic.
Crossing the boundary between Python and
compiled code has a real measurable
cost.
Every time you cross from Python to
compiled code and back, you pay a tax.
Conversion types, which is Python int to
like int64, Python string to char star.
Second would be reference counting and
memory management.
Global interpreter lock management.
Memory layout translation, where you're
translating from Python list to arrays
or Python dictionaries to structure.
For a single call
with a large payload,
where you're saying here here a million
points of data, go compute,
the tax is negligible. You cross once
and the compute dominates.
The computation dominates. There's no
problem over here.
But here's where it gets interesting and
where teams often run into trouble.
Consider a common scenario. You have a
fast numerical computation written in
compiled code with Python bindings.
User calls it from Python, clean
interface, and everyone's happy. But
someone writes a loop in Python that
calls this function thousands of times
with just slightly different inputs.
Now here the individual call function
runs in microseconds, which is blazing
fast, but the boundary crossing, the
Python to compiled code and compiled
code back to Python thousands of times
in a loop, this will dominate your wall
clock time. So when you profile it,
you'll realize that the computation just
took 20% of your run time, whereas
boundary took 80% of your run time.
The computation is found fast, but the
boundary is slow. Now here the fix isn't
about making the computation faster, but
the fix is redesigning the interface. So
instead of calling compute single and
just passing one input, maybe call and
calling that thousand times over, maybe
create a compute batch and pass all
those thousand input parameters and just
send it once.
There's one boundary crossing, one big
payload.
Python bundles up the work and hands C++
the job and gets back a single batch of
results.
A lesson I would let you guys take from
here is that design your boundaries to
be crossed infrequently with larger
payloads.
Have chatty interfaces.
I mean, no, don't have chatty
interfaces.
Many small calls will kill your
performance gains.
So the Yes, so here's another thing.
Making this boundary work well is a real
investment. It's not a one afternoon
tutorial.
You need building tools.
You need to build tooling to compile
extensions alongside Python code.
You need packaging infrastructure so
users can pip install the results.
You need binary compatibility testing
across Python versions and platforms.
And finally, you need documentation for
both the Python APIs and the compiled
internals. This is a lot of
infrastructure work. It takes time, it
takes a huge team.
But once you've made that investment,
the pattern will pay off everywhere.
Python will orchestrate, compiled code
will compute. The boundary is crossed
deliberately in larger chunks if you
design it well.
So finally, this gives me last two
questions of our decision framework,
which is what is the computational
profile? Not just is it heavy, but what
kind of heavy? Batch parallel work
should cross the boundary once with a
large payload. Iterative work that
someone might call in a loop, you need
to redesign the interface so that the
loop moves inside the boundary.
And second question, which is what does
the boundary cost?
The interface between Python and the
compiled code is not free. Measure it,
profile it, design chunky interfaces,
not the chatty ones. Make sure you're
not trading a computation bottleneck for
a boundary bottleneck.
So this brings me back to where we
started. Is Python too slow?
Orchestration, coordinating services,
translating data, managing workflows,
handling logic that changes frequently.
Python is not too slow, it's exactly
right. For heavy computation, like
numerical loops, Monte Carlo
simulations, GPU kernels, yes, Python is
too slow. But that's okay because
Python's superpower was never raw speed.
It's It's being the best orchestration
language we have. When I started working
on rising systems, I thought Python
would be just used for scripting and
testing. And I thought the real work
will be done in C++. But I was wrong.
Python is the reason developers can
install a library with pip install,
write three lines of code, get a
production quality result, even though
gigabytes of compiled C++ are doing the
heavy math underneath.
And that's not scripting, that's
orchestration. And it's one of the most
important layer in the stack.
The entire
scientific Python ecosystem is also
built on this pattern. NumPy, SciPy,
PyTorch, TensorFlow, Pandas. Python
orchestrates, compiled code computes.
This is a very well-established pattern
across the industry.
Yeah, so the next time someone ask you
us or tells you Python is too slow,
tell them
what is Python actually doing in your
system? If the answer is orchestrating,
coordinating and just gluing components
together, then it's not too slow, it's
it's doing exactly what it's supposed to
do. But if the answer is tight loops
over large data sets, yes, move that
loop and but keep the orchestration.
Because
Python doesn't need to be fast, it just
needs to be smart.
Thank you.
>> [applause]