package com.insightfullogic.honest_profiler.core.aggregation; import java.util.HashMap; import java.util.Map; import com.insightfullogic.honest_profiler.core.aggregation.aggregator.FlatProfileAggregator; import com.insightfullogic.honest_profiler.core.aggregation.aggregator.TreeProfileAggregator; import com.insightfullogic.honest_profiler.core.aggregation.grouping.CombinedGrouping; import com.insightfullogic.honest_profiler.core.aggregation.result.Aggregation; import com.insightfullogic.honest_profiler.core.aggregation.result.straight.Flat; import com.insightfullogic.honest_profiler.core.aggregation.result.straight.Node; import com.insightfullogic.honest_profiler.core.aggregation.result.straight.Tree; import com.insightfullogic.honest_profiler.core.profiles.lean.LeanNode; import com.insightfullogic.honest_profiler.core.profiles.lean.LeanProfile; import com.insightfullogic.honest_profiler.core.profiles.lean.LeanThreadNode; import com.insightfullogic.honest_profiler.core.profiles.lean.info.NumericInfo; /** * An AggregationProfile is a wrapper for a {@link LeanProfile} which provides methods for creating {@link Aggregation}s * on the wrapped {@link LeanProfile}. The class caches these {@link Aggregation}s. * <p> * It also calculates the "global aggregated data", i.e. the result of aggregating all data from the profile. */ public class AggregationProfile { // Instance Properties private static final FlatProfileAggregator flatAggregator = new FlatProfileAggregator(); private static final TreeProfileAggregator treeAggregator = new TreeProfileAggregator(); private final LeanProfile source; private NumericInfo global; private Map<CombinedGrouping, Flat> cachedFlats; private Map<CombinedGrouping, Tree> cachedTrees; // Instance Constructors /** * Constructor which specifies the wrapped {@link LeanProfile}. * <p> * @param source the wrapped {@link LeanProfile} */ public AggregationProfile(LeanProfile source) { this.source = source; global = new NumericInfo(); cachedFlats = new HashMap<>(); cachedTrees = new HashMap<>(); // ThreadInfo objects are stored separately in the LeanLogCollector (to avoid the assumption that a ThreadMeta // will always be emitted before the first sample for the thread comes in), so we put them into the root // LeanThreadNodes here. source.getThreads().forEach((id, node) -> node.setThreadInfo(source.getThreadInfo(id))); // Calculate the overall aggregation for the LeanProfile global = aggregateGlobal(); } // Instance Accessors /** * Returns the wrapped {@link LeanProfile}. * <p> * @return the wrapped {@link LeanProfile} */ public LeanProfile getSource() { return source; } /** * Returns the global aggregated data for the wrapped {@link LeanProfile}. * <p> * @return the global aggregated data for the wrapped {@link LeanProfile} */ public NumericInfo getGlobalData() { return global; } /** * Returns the {@link Flat} {@link Aggregation} obtained by aggregating the {@link LeanProfile} using the provided * {@link CombinedGrouping}. * <p> * @param grouping the {@link CombinedGrouping} which determines the aggregation key while aggregating * @return the resulting {@link Flat} aggregation */ public Flat getFlat(CombinedGrouping grouping) { return cachedFlats.computeIfAbsent(grouping, g -> flatAggregator.aggregate(this, g)); } /** * Returns the {@link Tree} {@link Aggregation} obtained by aggregating the {@link LeanProfile} using the provided * {@link CombinedGrouping}. * <p> * @param grouping the {@link CombinedGrouping} which determines the aggregation key while aggregating * @return the resulting {@link Tree} aggregation */ public Tree getTree(CombinedGrouping grouping) { return cachedTrees.computeIfAbsent(grouping, g -> treeAggregator.aggregate(this, g)); } // Helper Methods /** * Calculate the global aggregation of the {@link LeanProfile}. This is obtained by summing the {@link NumericInfo} * from all {@link LeanThreadNode}s in the {@link LeanProfile}. * <p> * The {@link LeanThreadNode}s, which are the root {@link Node}s in the {@link LeanProfile}, are themselves already * aggregations of all their descendant Frame {@link LeanNode}s, so we don't need to descend any further. * <p> * @return the {@link NumericInfo} containing the aggregated data */ private NumericInfo aggregateGlobal() { return source.getThreads().values().stream().collect( // Supplier, which creates a new NumericInfo which will contain the results NumericInfo::new, // Accumulator, adds the data from a LeanNode to the Accumulator (data, node) -> data.add(node.getData()), // Combiner, combines two NumericInfos (data1, data2) -> data1.add(data2)); } }