예제 #1
0
// SumFunc implements Func and sums the values of all argument
// traces into a single trace.
//
// MISSING_DATA_SENTINEL values are not included in the sum. Note that if all
// the values at an index are MISSING_DATA_SENTINEL then the sum will be
// MISSING_DATA_SENTINEL.
func (SumFunc) Eval(ctx *Context, node *Node) ([]*types.PerfTrace, error) {
	if len(node.Args) != 1 {
		return nil, fmt.Errorf("Sum() takes a single argument.")
	}
	if node.Args[0].Typ != NodeFunc {
		return nil, fmt.Errorf("Sum() takes a function argument.")
	}
	traces, err := node.Args[0].Eval(ctx)
	if err != nil {
		return nil, fmt.Errorf("Sum() argument failed to evaluate: %s", err)
	}

	if len(traces) == 0 {
		return traces, nil
	}

	ret := types.NewPerfTraceN(len(traces[0].Values))
	ret.Params()["id"] = tiling.AsFormulaID(ctx.formula)
	for i, _ := range ret.Values {
		sum := 0.0
		count := 0
		for _, tr := range traces {
			if v := tr.Values[i]; v != config.MISSING_DATA_SENTINEL {
				sum += v
				count += 1
			}
		}
		if count > 0 {
			ret.Values[i] = sum
		}
	}
	return []*types.PerfTrace{ret}, nil
}
예제 #2
0
// addCalculatedTraces adds the traces returned from evaluating the given
// formula over the given tile to the QueryResponse.
func addCalculatedTraces(qr *QueryResponse, tile *tiling.Tile, formula string) error {
	ctx := parser.NewContext(tile)
	traces, err := ctx.Eval(formula)
	if err != nil {
		return fmt.Errorf("Failed to evaluate formula %q: %s", formula, err)
	}
	hasFormula := false
	for _, tr := range traces {
		if tiling.IsFormulaID(tr.Params()["id"]) {
			hasFormula = true
		}
		tg := traceGuiFromTrace(tr, tr.Params()["id"], tile)
		qr.Traces = append(qr.Traces, tg)
	}
	if !hasFormula {
		// If we haven't added the formula trace to the response yet, add it in now.
		f := types.NewPerfTraceN(len(tile.Commits))
		tg := traceGuiFromTrace(f, tiling.AsFormulaID(formula), tile)
		qr.Traces = append(qr.Traces, tg)
	}
	return nil
}
예제 #3
0
// geoFunc implements Func and merges the values of all argument
// traces into a single trace with a geometric mean.
//
// MISSING_DATA_SENTINEL and negative values are not included in the mean.
// Note that if all the values at an index are MISSING_DATA_SENTINEL or
// negative then the mean will be MISSING_DATA_SENTINEL.
func (GeoFunc) Eval(ctx *Context, node *Node) ([]*types.PerfTrace, error) {
	if len(node.Args) != 1 {
		return nil, fmt.Errorf("geo() takes a single argument.")
	}
	if node.Args[0].Typ != NodeFunc {
		return nil, fmt.Errorf("geo() takes a function argument.")
	}
	traces, err := node.Args[0].Eval(ctx)
	if err != nil {
		return nil, fmt.Errorf("geo() argument failed to evaluate: %s", err)
	}

	if len(traces) == 0 {
		return traces, nil
	}

	ret := types.NewPerfTraceN(len(traces[0].Values))
	ret.Params()["id"] = tiling.AsFormulaID(ctx.formula)
	for i, _ := range ret.Values {
		// We're accumulating a product, but in log-space to avoid large N overflow.
		sumLog := 0.0
		count := 0
		for _, tr := range traces {
			if v := tr.Values[i]; v >= 0 && v != config.MISSING_DATA_SENTINEL {
				sumLog += math.Log(v)
				count += 1
			}
		}
		if count > 0 {
			// The geometric mean is the N-th root of the product of N terms.
			// In log-space, the root becomes a division, then we translate back to normal space.
			ret.Values[i] = math.Exp(sumLog / float64(count))
		}
	}
	return []*types.PerfTrace{ret}, nil
}