As of Blender 2.83 (currently in alpha at the time of this post), this can be solved by using the "Adaptive Sampling" option in Cycles. Adaptive sampling will automatically stop sampling in areas of the frame that are no longer noisy. To enable it, check the "Adaptive Sampling" box in Render Settings > Sampling
Once adaptive sampling is enabled, the render and viewport samples settings become the maximum number of samples that a pixel can have. How many samples a pixel actually gets will be adjusted automatically based on how noisy it is. Once Cycles sees a pixel as no longer being noisy, it will stop sampling it.
Under Sampling > Advanced, there are 2 additional options, Adaptive Min Samples, and Adaptive Threshold. Both are set to 0 (automatic) by default.
Adaptive Min Samples dictates how many samples Cycles will take before beginning to check if a region is noise-free. Checking for completion adds some overhead, so you don't want to do it when the image is still hopelessly noisy. The default setting of 0 puts this in automatic mode, which will set it to the square root of the maximum samples setting. This is good enough in most cases, although if you're using a high number of samples (such as several thousand) and have blank areas in your frame, manually setting it to a value such as 32 may give some performance improvement.
Adaptive Threshold sets a metric that defines when a pixel is "no longer noisy". The default of 0 puts this in automatic mode, which is usually fine if you're using denoising. If you need to adjust it manually, smaller values correspond to less noise. For final renders, values such as 0.001 are common.
In this example image, adaptive sampling allows the render to finish twice as fast (10sec instead of 20sec) at 1024 samples while producing the same final image.
By enabling the "sample count" pass, you can see a "heatmap" of where Cycles needed to use more samples. Darker values show fewer samples, with the darkest being areas that stopped right away at the minimum number of samples. Brighter values show more samples, with white being areas that went all the way to the maximum number of samples.