Skip to content

Taking a systematic approach to performance improvement

Process · Chapter 10

  • The entire development team should own performance, not a few individuals — shared ownership yields better results.
  • Follow a systematic, iterative loop rather than guessing where problems are. Measure, don’t assume.
  1. Profile the application → collect precise measurements.
  2. Analyze the results → find bottlenecks.
  3. Implement changes → fix the highest-payoff bottleneck.
  4. Monitor results → confirm the fix, watch for shifted or new bottlenecks; repeat.

Profiling = analysis producing measurements of a system’s execution. Don’t guess where the hotspots are — they’re often not where you expect. Capture: time per method, call counts, communication time, I/O volume, CPU/memory use.

Two ways profilers (profiling tools) collect data — an effective strategy often uses both:

  • Instrumentation — code is injected into the system to collect data (e.g. at method entry/exit for timing and counts). Can be added manually or automatically (some profilers modify source, some work at runtime). Gives great detail, but the instrumentation code perturbs measurements; profilers subtract their own overhead, yet adding code can change CPU optimizations, so very short methods can yield inaccurate results.
  • Statistical (sampling) profilers — let the app run without runtime modification, collecting data during OS interrupts (process-switch points). Less intrusive, near-normal speed; downside is the data is an approximation, not as numerically accurate as instrumentation.

2. Analyzing the results — find the bottlenecks

Section titled “2. Analyzing the results — find the bottlenecks”
  • Bottlenecks are parts of the system that limit performance — they can’t keep pace with the workload and slow everything down.
  • Optimize the bottlenecks, not the non-bottlenecks — only touch non-bottlenecks once all bottlenecks are handled and time remains.
  • Common bottlenecks and typical remedies:
    • Network slow → send less data (compression, caching).
    • Database slow → work with the DBA: add indexes, optimize queries, use stored procedures, possibly denormalize. See Database performance.
    • CPU bound → faster/more processors, cache computed data, improve algorithms.
  • Scaling options a bottleneck may force:
    • Vertical scaling (up): add resources to existing servers (memory, processors, faster CPUs, more disk).
    • Horizontal scaling (out): add servers to the pool to handle more traffic.
  • Only change things after profiling + analysis prove it’s worth it.
  • Implement one set of changes at a time so results don’t blur and new regressions stay visible.
  • Prioritize the most important bottleneck with the biggest expected payoff first.
  • After a change, a bottleneck is either unresolved, fixed, or transferred elsewhere — eliminating one can reveal another. If unresolved, consider undoing the change.
  • Keep monitoring even when performance is satisfactory: new features and bug fixes, and changes in user count/traffic, can introduce fresh bottlenecks over time. Be ready for more iterations.
  • Software Architect’s Handbook (Packt, 2018), Ch.10 “Taking a systematic approach to performance improvement”, pp. 748-754.