The bottleneck was not where I thought it was.
My API endpoint crawled. I blamed the database. I added indexes. I rewrote queries. Nothing helped.
Then I profiled. The truth hurt.
JSON serialization ate 60% of the CPU. The database was innocent.
That moment changed how I debug performance forever.
Here is the three-step process I now use. It works for any slow service.
Step 1: Profile before you touch a single line
Your intuition is a liar. Mine is too.
I used py-spy on a running server. It samples stack traces without restarting anything.
py-spy record --pid 1234 -o profile.svg
Then I opened the flame graph. The serialization stack was huge. The database stack was tiny.
cProfile gave me the function-level breakdown.
python -m cProfile -s cumtime my_service.py
That output told me exactly where to look.
Step 2: Build a minimal experiment
Profiling showed the problem. But I needed proof that a fix worked.
I wrote a small benchmark. It serialized the same payload one thousand times.
import json
import time
import orjson
import ujson
data = {"user": "dakota", "items": list(range(100))}
for lib in (json, ujson, orjson):
start = time.perf_counter()
for _ in range(1000):
lib.dumps(data)
print(lib.__name__, time.perf_counter() - start)
I needed a clean environment. My laptop had background noise. Containers were cheaper and cleaner.
That is where I used MonkeyCode's free server option. It gave me a disposable sandbox for the benchmark. No cost, no cleanup.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
I cloned the repo, installed dependencies, and ran the script. The free model access also helped me review the benchmark code. I asked it one question: "Does this comparison have a flaw?"
It pointed out that I should use orjson.loads too. Fair. I added that.
Step 3: Keep the graph
The benchmark numbers mean little without a visual. I wanted a graph I could show my team.
import matplotlib.pyplot as plt
libraries = ["json", "ujson", "orjson"]
times = [0.42, 0.31, 0.09] # seconds, smaller is better
plt.bar(libraries, times)
plt.ylabel("seconds for 1000 dumps")
plt.title("Serialization speed comparison")
plt.savefig("serialization_bench.png")
The bar chart was undeniable. orjson was 4.6x faster than the stdlib under my workload.
I kept that graph. It still lives in the project's README.
Why the free server mattered
I could have run the benchmark locally. But the team had shared machine debt. My laptop had a dying fan and a video call open.
The free server gave me a reproducible baseline. I could nuke it after the experiment. No one else's work got interrupted.
That alone is worth the price of admission.
Limitations you should know
This experiment was narrow. My payload was small. Your data may be different.
Serialization speed is not the only metric. Memory usage and compatibility matter too.
Don't replace json everywhere just because I did. Benchmark your actual payload first.
And please do not use the free server for load testing. It is a sandbox for experiments, not a stress-test playground. Respect the resource limits.
The deeper lesson
Every vague "it's slow" is a guessing game. Profiling turns it into an experiment.
The graph is your verdict. It ends arguments. It creates shared understanding.
So next time something feels slow, ask yourself: where is the evidence? If you don't have a graph, you don't have an answer.
Profile first. Guess later. Your future self will thank you.
If you want a free environment to run similar experiments, MonkeyCode's open-source project is a reasonable place to start. Just measure everything yourself.