KeyMob
All articles

Chasing frame drops on hardware you did not choose

3 min readPerformanceQA

“Sixty frames per second” is a claim about an average. Stutter is a claim about the worst frames. Those are different measurements, and only one of them is what users feel.

The frame budget is fixed

On a 60 Hz screen you have 16.7 ms per frame. On a 120 Hz ProMotion display you have 8.3 ms. That budget does not scale with how fast the chip is; a faster chip just means more of your work fits inside it.

Which is why a stutter that never appears on the newest phone appears reliably on a three-year-old one. Nothing is wrong with the old device. Your work simply does not fit in its budget.

Read the dips

Take a capture of the interaction that stutters and do three things in order:

  1. Find where the line drops. Not the average — the drops.
  2. Note when they happen. Launch? First scroll? Every time an image appears? On a transition?
  3. Read CPU, GPU and memory at that moment. This is the diagnosis.
At the dip You are
CPU spikes, GPU flat CPU-bound on the main thread
GPU spikes, CPU flat Render-bound
Memory jumps Allocating something large mid-frame
Network active Waiting on I/O somewhere it should not be
Nothing moves Blocked outside the process, or throttled

Four counters and a timestamp resolve most of these in a minute.

The five usual causes

Decoding full-size images

A 4000-pixel photo displayed in a 200-pixel cell still decodes at 4000 pixels. It costs CPU, it costs a large allocation, and if it happens during a scroll it costs frames. Downsample before display, and do it off the main thread.

This is the single most common cause of scroll stutter in list-heavy apps, and the easiest to fix.

Layout work that scales with content

Constraint solving is not linear in view count. A deep hierarchy rebuilt for every cell shows up as CPU that rises with scroll speed — the faster the user scrolls, the worse it gets, which is exactly the opposite of what people expect.

Shadows and blur

Cheap on a desktop GPU, expensive on a phone. A shadow on a moving view without an explicit shadowPath forces the system to compute the shape every frame. Blur is worse: it is an offscreen pass, and stacking several of them is how you become render-bound without writing any expensive code.

Synchronous work in a scroll callback

Anything touching disk, the keychain, or the network during a drag will be visible. UserDefaults reads look free until they are in a callback that fires sixty times a second.

Doing work you are about to throw away

Prefetching six screens ahead, laying out cells that will never be seen, parsing a whole response when the view needs three fields. None of it is wrong in isolation; all of it competes for the same 16.7 ms.

Stutter that never shows in FPS

Some of the worst-feeling problems do not move the frame-rate line at all:

  • A tap that takes 300 ms to do anything. The frames were fine; nothing happened in them.
  • An animation that starts late. Smooth once it starts, and it started a beat after the finger lifted.
  • A transition that drops one frame. Statistically invisible, extremely noticeable.

For these, watch the CPU trace around the interaction and read the log alongside it. A gap in log output during a drag is often the clearest evidence you will get that something blocked.

Testing so the result means something

  • Use the oldest device you support. If it is smooth there, it is smooth everywhere.
  • Let the device cool first. A throttled capture measures temperature.
  • Repeat the same interaction. Same list, same distance, same speed.
  • Capture over the cable. Wireless sampling adds jitter you will mistake for the bug.
  • Compare two captures, not one. Before and after a change. A single capture is a number; two are a result.

Where to stop

Chasing the last two frames per second on a device three generations old is usually not where the value is. The goal is not a flat line — it is no dip a user notices during the interactions they perform constantly. Fix those, verify on the slowest hardware you support, and move on.