Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: improve expr.toString performance #1706

Merged
merged 1 commit into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,10 @@ sealed trait DataExpr extends TimeSeriesExpr with Product {
ResultSet(this, data.getOrElse(this, Nil), context.state)
}

override def toString: String = {
if (offset.isZero) exprString else s"$exprString,$offset,:offset"
override def append(builder: java.lang.StringBuilder): Unit = {
builder.append(exprString)
if (!offset.isZero)
builder.append(',').append(offset).append(",:offset")
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ object EventExpr {
*/
case class Raw(query: Query) extends EventExpr {

override def toString: String = query.toString
override def append(builder: java.lang.StringBuilder): Unit = {
Interpreter.append(builder, query)
}
}

/**
Expand All @@ -49,6 +51,8 @@ object EventExpr {

require(columns.nonEmpty, "set of columns cannot be empty")

override def toString: String = Interpreter.toString(query, columns, ":table")
override def append(builder: java.lang.StringBuilder): Unit = {
Interpreter.append(builder, query, columns, ":table")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,18 @@
*/
package com.netflix.atlas.core.model

trait Expr extends Product {
import com.netflix.atlas.core.stacklang.StackItem

trait Expr extends Product with StackItem {

/** Use builder for encoding as a string. Sub-classes should override the append method. */
override def toString: String = {
// 256 is a rough size that isn't too large to waste a lot, but also limit the
// need for growing the buffer with typical expressions
val builder = new java.lang.StringBuilder(256)
append(builder)
builder.toString
}

/**
* Returns a string that can be executed with the stack interpreter to create this expression.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ object FilterExpr {
case class Stat(expr: TimeSeriesExpr, stat: String, str: Option[String] = None)
extends FilterExpr {

override def toString: String = str.getOrElse(s"$expr,$stat,:stat")
override def append(builder: java.lang.StringBuilder): Unit = {
str match {
case Some(s) => builder.append(s)
case None => Interpreter.append(builder, expr, stat, ":stat")
}
}

def dataExprs: List[DataExpr] = expr.dataExprs

Expand All @@ -64,7 +69,9 @@ object FilterExpr {

def name: String

override def toString: String = s":stat-$name"
override def append(builder: java.lang.StringBuilder): Unit = {
Interpreter.append(builder, s":stat-$name")
}

def dataExprs: List[DataExpr] = Nil

Expand Down Expand Up @@ -113,7 +120,9 @@ object FilterExpr {

if (!expr1.isGrouped) require(!expr2.isGrouped, "filter grouping must match expr grouping")

override def toString: String = Interpreter.toString(expr1, expr2, ":filter")
override def append(builder: java.lang.StringBuilder): Unit = {
Interpreter.append(builder, expr1, expr2, ":filter")
}

def dataExprs: List[DataExpr] = expr1.dataExprs ::: expr2.dataExprs

Expand Down Expand Up @@ -183,7 +192,9 @@ object FilterExpr {

require(k > 0, s"k must be positive ($k <= 0)")

override def toString: String = Interpreter.toString(expr, stat, k, s":$opName")
override def append(builder: java.lang.StringBuilder): Unit = {
Interpreter.append(builder, expr, stat, k, s":$opName")
}

def dataExprs: List[DataExpr] = expr.dataExprs

Expand Down
120 changes: 88 additions & 32 deletions atlas-core/src/main/scala/com/netflix/atlas/core/model/MathExpr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import com.netflix.atlas.core.util.Math
import com.netflix.atlas.core.util.Strings
import com.netflix.spectator.api.histogram.PercentileBuckets

import scala.collection.immutable.ArraySeq

trait MathExpr extends TimeSeriesExpr

object MathExpr {
Expand All @@ -49,7 +51,9 @@ object MathExpr {

def dataExprs: List[DataExpr] = expr.dataExprs

override def toString: String = Interpreter.toString(expr, original, replacement, ":as")
override def append(builder: java.lang.StringBuilder): Unit = {
Interpreter.append(builder, expr, original, replacement, ":as")
}

def isGrouped: Boolean = expr.isGrouped

Expand Down Expand Up @@ -83,7 +87,9 @@ object MathExpr {

def dataExprs: List[DataExpr] = Nil

override def toString: String = Interpreter.toString(v, ":const")
override def append(builder: java.lang.StringBuilder): Unit = {
Interpreter.append(builder, v, ":const")
}

def isGrouped: Boolean = false

Expand All @@ -107,7 +113,9 @@ object MathExpr {

def dataExprs: List[DataExpr] = Nil

override def toString: String = ":random"
override def append(builder: java.lang.StringBuilder): Unit = {
Interpreter.append(builder, ":random")
}

def isGrouped: Boolean = false

Expand Down Expand Up @@ -135,7 +143,9 @@ object MathExpr {

def dataExprs: List[DataExpr] = Nil

override def toString: String = Interpreter.toString(seed, ":srandom")
override def append(builder: java.lang.StringBuilder): Unit = {
Interpreter.append(builder, seed, ":srandom")
}

def isGrouped: Boolean = false

Expand Down Expand Up @@ -196,7 +206,9 @@ object MathExpr {

def dataExprs: List[DataExpr] = Nil

override def toString: String = s"$mode,:time"
override def append(builder: java.lang.StringBuilder): Unit = {
Interpreter.append(builder, mode, ":time")
}

def isGrouped: Boolean = false

Expand All @@ -215,7 +227,9 @@ object MathExpr {

def dataExprs: List[DataExpr] = Nil

override def toString: String = Interpreter.toString(s, e, ":time-span")
override def append(builder: java.lang.StringBuilder): Unit = {
Interpreter.append(builder, s, e, ":time-span")
}

def isGrouped: Boolean = false

Expand Down Expand Up @@ -286,7 +300,9 @@ object MathExpr {

def dataExprs: List[DataExpr] = expr.dataExprs

override def toString: String = Interpreter.toString(expr, min, s":$name")
override def append(builder: java.lang.StringBuilder): Unit = {
Interpreter.append(builder, expr, min, s":$name")
}

def isGrouped: Boolean = expr.isGrouped

Expand Down Expand Up @@ -316,6 +332,10 @@ object MathExpr {

override def toString: String = Interpreter.toString(expr, max, s":$name")

override def append(builder: java.lang.StringBuilder): Unit = {
Interpreter.append(builder, expr, max, s":$name")
}

def isGrouped: Boolean = expr.isGrouped

def groupByKey(tags: Map[String, String]): Option[String] = expr.groupByKey(tags)
Expand Down Expand Up @@ -344,7 +364,16 @@ object MathExpr {

def dataExprs: List[DataExpr] = expr.dataExprs

override def toString: String = Interpreter.toString(expr, s":$name")
override def toString: String = {
// Needs to be overridden here or it will get the copy from the based Function type
val builder = new java.lang.StringBuilder()
append(builder)
builder.toString
}

override def append(builder: java.lang.StringBuilder): Unit = {
Interpreter.append(builder, expr, s":$name")
}

def isGrouped: Boolean = expr.isGrouped

Expand Down Expand Up @@ -456,7 +485,16 @@ object MathExpr {

def dataExprs: List[DataExpr] = expr1.dataExprs ::: expr2.dataExprs

override def toString: String = Interpreter.toString(expr1, expr2, s":$name")
override def toString: String = {
// Needs to be overridden here or it will get the copy from the based Function type
val builder = new java.lang.StringBuilder()
append(builder)
builder.toString
}

override def append(builder: java.lang.StringBuilder): Unit = {
Interpreter.append(builder, expr1, expr2, s":$name")
}

def isGrouped: Boolean = expr1.isGrouped || expr2.isGrouped

Expand Down Expand Up @@ -680,7 +718,9 @@ object MathExpr {

def dataExprs: List[DataExpr] = expr.dataExprs

override def toString: String = Interpreter.toString(expr, s":$name")
override def append(builder: java.lang.StringBuilder): Unit = {
Interpreter.append(builder, expr, s":$name")
}

def isGrouped: Boolean = false

Expand Down Expand Up @@ -766,7 +806,9 @@ object MathExpr {
}
}

override def toString: String = Interpreter.toString(expr, keys, ":by")
override def append(builder: java.lang.StringBuilder): Unit = {
Interpreter.append(builder, expr, keys, ":by")
}

def dataExprs: List[DataExpr] = expr.dataExprs

Expand Down Expand Up @@ -828,16 +870,20 @@ object MathExpr {
private val evalGroupKeys = expr.keys.filter(_ != TagKey.percentile)
private val pcts = percentiles.distinct.sortWith(_ < _).toArray

override def toString: String = {
val baseExpr =
if (evalGroupKeys.isEmpty)
expr.af.query.toString
else
Interpreter.toString(expr.af.query, evalGroupKeys, ":by")
if (expr.offset.isZero)
s"$baseExpr,(,${pcts.mkString(",")},),:percentiles"
else
s"$baseExpr,(,${pcts.mkString(",")},),:percentiles,${expr.offset},:offset"
override def append(builder: java.lang.StringBuilder): Unit = {
// Base expr
Interpreter.append(builder, expr.af.query)
if (evalGroupKeys.nonEmpty)
Interpreter.append(builder, expr.af.query, evalGroupKeys, ":by")

// Percentiles
builder.append(",(,")
Interpreter.append(builder, ArraySeq.unsafeWrapArray(pcts)*)
builder.append(",),:percentiles")

// Offset
if (!expr.offset.isZero)
builder.append(',').append(expr.offset).append(",:offset")
}

override def dataExprs: List[DataExpr] = List(expr)
Expand Down Expand Up @@ -995,9 +1041,11 @@ object MathExpr {

def dataExprs: List[DataExpr] = evalExpr.dataExprs

override def toString: String = toString(displayExpr)
override def append(builder: java.lang.StringBuilder): Unit = {
append(builder, displayExpr)
}

private def toString(expr: Expr): String = {
private def append(builder: java.lang.StringBuilder, expr: Expr): Unit = {
val op =
if (displayParams.isEmpty)
s":$name"
Expand All @@ -1009,33 +1057,41 @@ object MathExpr {
// aggregate function. Modifications to the aggregate need to be represented
// after the operation as part of the expression string. There are two
// categories: offsets applied to the data function and group by.
val buffer = new java.lang.StringBuilder
buffer.append(s"$q,$op")
getOffset(evalExpr).foreach(d => buffer.append(s",$d,:offset"))
builder.append(s"$q,$op")
getOffset(evalExpr).foreach(d => builder.append(s",$d,:offset"))

val grouping = evalExpr.finalGrouping
if (grouping.nonEmpty) {
buffer.append(grouping.mkString(",(,", ",", ",),:by"))
builder.append(grouping.mkString(",(,", ",", ",),:by"))
}

buffer.toString
case t: TimeSeriesExpr if groupingMatches =>
// The passed in expression maybe the result of a rewrite to the display expression
// that was not applied to the eval expression. If it changes the grouping, then it
// would alter the toString behavior. So the grouping match check is based on the
// original display expression.
val evalOffset = getOffset(evalExpr)
evalOffset.fold(s"$t,$op") { d =>
val str = evalOffset.fold(s"$t,$op") { d =>
val displayOffset = getOffset(t).getOrElse(Duration.ZERO)
if (d != displayOffset) s"${t.withOffset(d)},$op" else s"$t,$op"
}
builder.append(str)
case _ =>
Interpreter.append(builder, expr, op)
val grouping = evalExpr.finalGrouping
val by = if (grouping.nonEmpty) grouping.mkString(",(,", ",", ",),:by") else ""
s"$expr,$op$by"
if (grouping.nonEmpty) {
builder.append(",(,")
Interpreter.append(builder, grouping*)
builder.append(",),:by")
}
}
}

private def toString(expr: Expr): String = {
val builder = new java.lang.StringBuilder()
append(builder, expr)
builder.toString
}

private def groupingMatches: Boolean = {
displayExpr match {
case t: TimeSeriesExpr => t.finalGrouping == evalExpr.finalGrouping
Expand Down
Loading
Loading