/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.engine.calcnode.stats; import org.fudgemsg.FudgeMsg; import org.fudgemsg.FudgeMsgFactory; import org.fudgemsg.MutableFudgeMsg; import com.opengamma.util.ArgumentChecker; /** * Records statistics about invocations of a function. * <p> * This is run centrally to aggregate statistics. The statistics recorded include the time taken and the data volume. Old data is decayed to be less relevant. * <p> * This class is mutable and thread-safe via synchronization. */ public class FunctionInvocationStatistics { /** * A decay to prioritize the latest data. */ private static final double DATA_DECAY = 0.1; /** * The number of samples in the snapshot. */ private static final int SNAPSHOT_SAMPLES = 100; /** * The function identifier. */ private final String _functionId; /** * A cost estimate for the time the function takes. */ private double _invocationCost = 1.0; /** * A cost estimate for the data input size. */ private double _dataInputCost = 1.0; /** * A cost estimate for the data output size. */ private double _dataOutputCost = 1.0; /** * The {@link System#nanoTime} instant when the data was last updated. */ private long _lastUpdated; private int _cost = SNAPSHOT_SAMPLES - 1; private double _invocations; private double _invocationTime; private double _dataInput; private double _dataOutput; /** * Creates an instance for a specific function. * * @param functionId the function id, not null */ public FunctionInvocationStatistics(final String functionId) { ArgumentChecker.notNull(functionId, "functionId"); _functionId = functionId; } /** * Creates an instance from a document. * * @param doc the document, not null */ public FunctionInvocationStatistics(final FunctionCostsDocument doc) { ArgumentChecker.notNull(doc, "doc"); _functionId = doc.getFunctionId(); setCosts(doc.getInvocationCost(), doc.getDataInputCost(), doc.getDataOutputCost()); } //------------------------------------------------------------------------- /** * Updates the statistics record with details of the costs. * <p> * This may be called directly, but is more typically called via {@link #recordInvocation}. * * @param invocationCost the invocation cost * @param dataInputCost the data input cost * @param dataOutputCost the data output cost */ synchronized void setCosts(final double invocationCost, final double dataInputCost, final double dataOutputCost) { _invocationCost = invocationCost; _dataInputCost = dataInputCost; _dataOutputCost = dataOutputCost; _lastUpdated = System.nanoTime(); } /** * Updates the statistics with details of one or more invocations. * <p> * The data passed in is used to update the long-running cost values. * * @param invocationCount the number of invocations the data is for * @param invocationNanos the execution time, in nanoseconds, of the invocation(s) * @param dataInputBytes the mean data input, bytes per input node, or {@code NaN} if unavailable * @param dataOutputBytes the mean data output, bytes per output node, or {@code NaN} if unavailable */ synchronized void recordInvocation( final int invocationCount, final double invocationNanos, final double dataInputBytes, final double dataOutputBytes) { _invocations += invocationCount; _invocationTime += invocationNanos; _dataInput += Double.isNaN(dataInputBytes) ? ((_invocations > 0) ? (_dataInput / _invocations) : 0) : dataInputBytes; _dataOutput += Double.isNaN(dataOutputBytes) ? ((_invocations > 0) ? (_dataOutput / _invocations) : 0) : dataOutputBytes; _cost += invocationCount; if (_cost >= SNAPSHOT_SAMPLES) { _cost = 0; setCosts(_invocationTime / _invocations, _dataInput / _invocations, _dataOutput / _invocations); _invocations *= 1 - DATA_DECAY; _invocationTime *= 1 - DATA_DECAY; _dataInput *= 1 - DATA_DECAY; _dataOutput *= 1 - DATA_DECAY; } } //------------------------------------------------------------------------- /** * Gets the function identifier. * * @return the function identifier, not null */ public String getFunctionId() { return _functionId; } /** * Gets the invocation cost, a mean "standard" time to execute in nanoseconds. * * @return invocation cost in nanoseconds */ public synchronized double getInvocationCost() { return _invocationCost; } /** * Gets the data input cost, a mean bytes per input value. * * @return data input cost in bytes per value */ public synchronized double getDataInputCost() { return _dataInputCost; } /** * Gets the data output cost, a mean bytes per output value. * * @return data output cost in bytes per value */ public synchronized double getDataOutputCost() { return _dataOutputCost; } /** * Gets the {@link System#nanoTime} timestamp of the last time the costs changed. * * @return the value of {@link System#nanoTime} of the last sample update */ public synchronized long getLastUpdateNanos() { return _lastUpdated; } //------------------------------------------------------------------------- /** * Populates the document with a snapshot of the values from this class. * * @param document the document to populate, not null */ public synchronized void populateDocument(final FunctionCostsDocument document) { document.setInvocationCost(_invocationCost); document.setDataInputCost(_dataInputCost); document.setDataOutputCost(_dataOutputCost); } //------------------------------------------------------------------------- /** * Returns a string suitable for debugging. * * @return a string, not null */ @Override public String toString() { return getFunctionId() + " = " + getInvocationCost() + "ns, " + getDataInputCost() + " bytes/input, " + getDataOutputCost() + " bytes/output, at " + getLastUpdateNanos(); } // For debug purposes only, remove when PLAT-882 is complete /* package */synchronized FudgeMsg toFudgeMsg(final FudgeMsgFactory factory) { ///CSIGNORE final MutableFudgeMsg message = factory.newMessage(); message.add("invocationCost", _invocationCost); message.add("dataInputCost", _dataInputCost); message.add("dataOutputCost", _dataOutputCost); message.add("invocationCount", _invocations); message.add("invocationTime", _invocationTime); message.add("dataInputSize", _dataInput); message.add("dataOutputSize", _dataOutput); return message; } }