All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.caliper.worker.AllocationStats Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2013 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.caliper.worker;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Comparator.comparing;

import com.google.caliper.model.Measurement;
import com.google.caliper.model.Value;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.Multiset;
import com.google.common.collect.Multisets;
import com.google.common.collect.Ordering;

/** A set of statistics about the allocations performed by a benchmark method. */
final class AllocationStats {
  private final int allocationCount;
  private final long allocationSize;
  private final int reps;
  private final ImmutableMultiset allocations;

  /**
   * Constructs a new {@link AllocationStats} with the given number of allocations ({@code
   * allocationCount}), cumulative size of the allocations ({@code allocationSize}) and the number
   * of {@code reps} passed to the benchmark method.
   */
  AllocationStats(int allocationCount, long allocationSize, int reps) {
    this(allocationCount, allocationSize, reps, ImmutableMultiset.of());
  }

  /**
   * Constructs a new {@link AllocationStats} with the given allocations and the number of {@code
   * reps} passed to the benchmark method.
   */
  AllocationStats(Multiset allocations, int reps) {
    this(allocations.size(), Allocation.getTotalSize(allocations), reps, allocations);
  }

  private AllocationStats(
      int allocationCount, long allocationSize, int reps, Multiset allocations) {
    checkArgument(allocationCount >= 0, "allocationCount (%s) was negative", allocationCount);
    this.allocationCount = allocationCount;
    checkArgument(allocationSize >= 0, "allocationSize (%s) was negative", allocationSize);
    this.allocationSize = allocationSize;
    checkArgument(reps >= 0, "reps (%s) was negative", reps);
    this.reps = reps;
    this.allocations = ImmutableMultiset.copyOf(allocations);
  }

  int getAllocationCount() {
    return allocationCount;
  }

  long getAllocationSize() {
    return allocationSize;
  }

  /**
   * Computes and returns the difference between this measurement and the given {@code baseline}
   * measurement. The {@code baseline} measurement must have a lower weight (fewer reps) than this
   * measurement.
   */
  AllocationStats minus(AllocationStats baseline) {
    for (Multiset.Entry entry : baseline.allocations.entrySet()) {
      int superCount = allocations.count(entry.getElement());
      if (superCount < entry.getCount()) {
        throw new IllegalStateException(
            String.format(
                "Your benchmark appears to have non-deterministic allocation behavior. "
                    + "Observed %d instance(s) of %s in the baseline but only %d in the actual "
                    + "measurement",
                entry.getCount(), entry.getElement(), superCount));
      }
    }
    try {
      return new AllocationStats(
          allocationCount - baseline.allocationCount,
          allocationSize - baseline.allocationSize,
          reps - baseline.reps,
          Multisets.difference(allocations, baseline.allocations));
    } catch (IllegalArgumentException e) {
      throw new IllegalStateException(
          String.format(
              "Your benchmark appears to have non-deterministic allocation behavior. The"
                  + " difference between the baseline %s and the measurement %s is invalid."
                  + " Consider enabling instrument.allocation.options.trackAllocations to get a"
                  + " more specific error message.",
              baseline, this),
          e);
    }
  }

  /**
   * Computes and returns the difference between this measurement and the given {@code baseline}
   * measurement. Unlike {@link #minus(AllocationStats)} this does not have to be a super set of the
   * baseline.
   */
  public Delta delta(AllocationStats baseline) {
    return new Delta(
        allocationCount - baseline.allocationCount,
        allocationSize - baseline.allocationSize,
        reps - baseline.reps,
        Multisets.difference(allocations, baseline.allocations),
        Multisets.difference(baseline.allocations, allocations));
  }

  /** Returns a list of {@link Measurement measurements} based on this collection of stats. */
  ImmutableList toMeasurements() {
    // Sort allocations before printing by count, then size, then break ties deterministically by
    // description and then location.
    allocations.entrySet().stream()
        .sorted(
            comparing((Multiset.Entry e) -> e.getCount())
                .reversed()
                .thenComparing(
                    Multiset.Entry::getElement,
                    comparing(Allocation::getSize)
                        .reversed()
                        .thenComparing(Allocation::getDescription)
                        .thenComparing(
                            Allocation::getLocation, Ordering.natural().lexicographical())))
        .forEach(
            entry -> {
              double allocsPerRep = ((double) entry.getCount()) / reps;
              System.out.printf(
                  "Allocated %f allocs per rep of %s%n", allocsPerRep, entry.getElement());
            });
    return ImmutableList.of(
        new Measurement.Builder()
            .value(Value.create(allocationCount, ""))
            .description("objects")
            .weight(reps)
            .build(),
        new Measurement.Builder()
            .value(Value.create(allocationSize, "B"))
            .weight(reps)
            .description("bytes")
            .build());
  }

  @Override
  public boolean equals(Object obj) {
    if (obj == this) {
      return true;
    } else if (obj instanceof AllocationStats) {
      AllocationStats that = (AllocationStats) obj;
      return allocationCount == that.allocationCount
          && allocationSize == that.allocationSize
          && reps == that.reps
          && Objects.equal(allocations, that.allocations);
    } else {
      return false;
    }
  }

  @Override
  public int hashCode() {
    return Objects.hashCode(allocationCount, allocationSize, reps, allocations);
  }

  @Override
  public String toString() {
    return MoreObjects.toStringHelper(this)
        .add("allocationCount", allocationCount)
        .add("allocationSize", allocationSize)
        .add("reps", reps)
        .add("allocations", allocations)
        .toString();
  }

  /** The delta between two different sets of statistics. */
  static final class Delta {
    private final int count;
    private final long size;
    private final int reps;
    private final Multiset additions;
    private final Multiset removals;

    Delta(
        int count,
        long size,
        int reps,
        Multiset additions,
        Multiset removals) {
      this.count = count;
      this.size = size;
      this.reps = reps;
      this.additions = additions;
      this.removals = removals;
    }

    /** Returns the long formatted with a leading +/- sign */
    private static String formatWithLeadingSign(long n) {
      return n > 0 ? "+" + n : "" + n;
    }

    @Override
    public String toString() {
      return MoreObjects.toStringHelper(this)
          .add("count", formatWithLeadingSign(count))
          .add("size", formatWithLeadingSign(size))
          .add("reps", formatWithLeadingSign(reps))
          .add("additions", additions)
          .add("removals", removals)
          .toString();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy