private CostBenefitInfo getCostBenefitInfo(LogSegmentName firstLogSegmentName, LogSegmentName lastLogSegmentName,
      NavigableMap<LogSegmentName, Long> validDataSizePerLogSegment, long segmentCapacity, long segmentHeaderSize,
      long maxBlobSize, boolean weightOnBenefit) {
    // cost = total valid data size (or data that needs to be copied over)
    // benefit = no of segments reclaimed = (total source segments - total target segments)
    // total target segments = Ceil (total cost / (segment capacity - segment header - max blob size))
    long totalCost = 0;
    long adjustedTotalCost = 0;
    int totalSegmentsToBeCompacted = 0;
    LogSegmentName curSegmentName = firstLogSegmentName;
    // weight more on benefits, don't let one single log segment with extreme small valid data leads to small compactions.
    // Be default, the storeStatsBasedCompactionMinCostInPercentage is 0.06. 16G*0.06=1G
    long minSegmentCost = storeConfig.storeStatsBasedCompactionMinCostInPercentage == 0 ? 0
        : (long) (segmentCapacity * storeConfig.storeStatsBasedCompactionMinCostInPercentage);
    while (!curSegmentName.equals(lastLogSegmentName)) {
      totalCost += validDataSizePerLogSegment.get(curSegmentName);
      adjustedTotalCost += validDataSizePerLogSegment.get(curSegmentName) < minSegmentCost ? minSegmentCost
          : validDataSizePerLogSegment.get(curSegmentName);

      curSegmentName = validDataSizePerLogSegment.higherKey(curSegmentName);
      totalSegmentsToBeCompacted++;
    }
    // add the last log segment
    totalCost += validDataSizePerLogSegment.get(curSegmentName);
    adjustedTotalCost += validDataSizePerLogSegment.get(curSegmentName) < minSegmentCost ? minSegmentCost
        : validDataSizePerLogSegment.get(curSegmentName);
    totalSegmentsToBeCompacted++;
    long maxCapacityPerSegment = segmentCapacity - segmentHeaderSize - maxBlobSize;
    int benefit = totalSegmentsToBeCompacted - (int) Math.ceil(totalCost / (maxCapacityPerSegment * 1.0));

    if (weightOnBenefit) {
      if (totalCost < adjustedTotalCost) {
        logger.trace("getCostBenefitInfo {} cost {} adjusted cost {}",
            validDataSizePerLogSegment.subMap(firstLogSegmentName, true, lastLogSegmentName, true).keySet(), totalCost,
            adjustedTotalCost);
        totalCost = adjustedTotalCost;
      }
    }
    return new CostBenefitInfo(new ArrayList<>(
        validDataSizePerLogSegment.subMap(firstLogSegmentName, true, lastLogSegmentName, true).keySet()), totalCost,
        benefit);
  }
Calculates the {@link CostBenefitInfo} for the candidate to compact
@param firstLogSegmentName first log segment name of the candidate to compact
@param lastLogSegmentName last log segment name of the candidate to compact
@param validDataSizePerLogSegment the valid data size for each log segment in the form of a {@link NavigableMap} of segment names to
valid data sizes.
@param segmentCapacity Segment capacity of one {@link LogSegment}
@param segmentHeaderSize Segment header size of a {@link LogSegment}
@param maxBlobSize The max blob size.
@return the {@link CostBenefitInfo} for the candidate to compact.
ambry-store/src/main/java/com/github/ambry/store/StatsBasedCompactionPolicy.java