static estimateSavingsWithGraphs(simulator, fcpGraph, deferredIds, wastedCssBytesByUrl, Stacks) {
    const {nodeTimings} = simulator.simulate(fcpGraph);
    const adjustedNodeTimings = new Map(nodeTimings);

    let totalChildNetworkBytes = 0;
    const minimalFCPGraph = fcpGraph.cloneWithRelationships(node => {
      adjustNodeTimings(adjustedNodeTimings, node, Stacks);

      // If a node can be deferred, exclude it from the new FCP graph
      const canDeferRequest = deferredIds.has(node.id);
      if (node.type !== Lantern.Graph.BaseNode.types.NETWORK) return !canDeferRequest;

      const isStylesheet =
        node.request.resourceType === NetworkRequest.TYPES.Stylesheet;
      if (canDeferRequest && isStylesheet) {
        // We'll inline the used bytes of the stylesheet and assume the rest can be deferred
        const wastedBytes = wastedCssBytesByUrl.get(node.request.url) || 0;
        totalChildNetworkBytes += (node.request.transferSize || 0) - wastedBytes;
      }
      return !canDeferRequest;
    });

    if (minimalFCPGraph.type !== 'network') {
      throw new Error('minimalFCPGraph not a NetworkNode');
    }

    // Recalculate the "before" time based on our adjusted node timings.
    const estimateBeforeInline = Math.max(...Array.from(
      Array.from(adjustedNodeTimings).map(timing => timing[1].endTime)
    ));

    // Add the inlined bytes to the HTML response
    const originalTransferSize = minimalFCPGraph.request.transferSize;
    const safeTransferSize = originalTransferSize || 0;
    minimalFCPGraph.request.transferSize = safeTransferSize + totalChildNetworkBytes;
    const estimateAfterInline = simulator.simulate(minimalFCPGraph).timeInMs;
    minimalFCPGraph.request.transferSize = originalTransferSize;
    return Math.round(Math.max(estimateBeforeInline - estimateAfterInline, 0));
  }
Estimates how much faster this page would reach FCP if we inlined all the used CSS from the
render blocking stylesheets and deferred all the scripts. This is more conservative than
removing all the assets and more aggressive than inlining everything.

*Most* of the time, scripts in the head are there accidentally/due to lack of awareness
rather than necessity, so we're comfortable with this balance. In the worst case, we're telling
devs that they should be able to get to a reasonable first paint without JS, which is not a bad
thing.

@param {LH.Gatherer.Simulation.Simulator} simulator
@param {LH.Gatherer.Simulation.GraphNode} fcpGraph
@param {Set<string>} deferredIds
@param {Map<string, number>} wastedCssBytesByUrl
@param {LH.Artifacts.DetectedStack[]} Stacks
@return {number}