Skip to content

Commit

Permalink
a whole pile of micro-optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
lihaoyi-databricks committed Sep 9, 2019
1 parent bf9d955 commit 2108b89
Show file tree
Hide file tree
Showing 13 changed files with 222 additions and 187 deletions.
8 changes: 4 additions & 4 deletions sjsonnet/src-jvm/sjsonnet/SjsonnetMain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import scala.util.Try

object SjsonnetMain {
def createParseCache() = collection.mutable.Map[String, fastparse.Parsed[(Expr, Map[String, Int])]]()
def resolveImport(searchRoots0: Seq[Path], allowedInputs: Option[Set[os.Path]] = None)(wds: Seq[Path], str: String) = {
(wds ++ searchRoots0)
def resolveImport(searchRoots0: Seq[Path], allowedInputs: Option[Set[os.Path]] = None)(wd: Path, str: String) = {
(wd +: searchRoots0)
.flatMap(base => os.FilePath(str) match {
case r: os.RelPath =>
if (r.ups > base.segmentCount()) None
Expand Down Expand Up @@ -45,7 +45,7 @@ object SjsonnetMain {
stderr: PrintStream,
wd: os.Path,
allowedInputs: Option[Set[os.Path]] = None,
importer: Option[(Seq[Path], String) => Option[os.Path]] = None): Int = {
importer: Option[(Path, String) => Option[os.Path]] = None): Int = {

val result = for{
t <- Cli.groupArgs(args.toList, Cli.genericSignature(wd), Cli.Config()).left.map{
Expand Down Expand Up @@ -90,7 +90,7 @@ object SjsonnetMain {
stderr: PrintStream,
wd: os.Path,
allowedInputs: Option[Set[os.Path]] = None,
importer: Option[(Seq[Path], String) => Option[os.Path]] = None): Either[String, String] = {
importer: Option[(Path, String) => Option[os.Path]] = None): Either[String, String] = {
val path = os.Path(file, wd)
val interp = new Interpreter(
parseCache,
Expand Down
94 changes: 45 additions & 49 deletions sjsonnet/src/sjsonnet/Evaluator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import ujson.Value
class Evaluator(parseCache: collection.mutable.Map[String, fastparse.Parsed[(Expr, Map[String, Int])]],
extVars: Map[String, ujson.Value],
wd: Path,
importer: (Seq[Path], String) => Option[(Path, String)]) extends EvalScope(extVars, wd){
importer: (Path, String) => Option[(Path, String)]) extends EvalScope(extVars, wd){
implicit def evalScope: EvalScope = this

override def materialize(v: Val): Value = {
Expand Down Expand Up @@ -109,7 +109,7 @@ class Evaluator(parseCache: collection.mutable.Map[String, fastparse.Parsed[(Exp
visitExpr(value) match {
case Val.Str(s) => s
case r =>
try Materializer(r).toString()
try Materializer.stringify(r)
catch Util.tryCatchWrap(offset)
},
offset
Expand Down Expand Up @@ -242,7 +242,7 @@ class Evaluator(parseCache: collection.mutable.Map[String, fastparse.Parsed[(Exp

def resolveImport(value: String, offset: Int)
(implicit scope: ValScope, fileScope: FileScope): (Path, String) = {
importer(Seq(wd, fileScope.currentFile.parent()), value)
importer(fileScope.currentFile.parent(), value)
.getOrElse(
Util.fail(
"Couldn't import file: " + pprint.Util.literalize(value),
Expand Down Expand Up @@ -303,10 +303,10 @@ class Evaluator(parseCache: collection.mutable.Map[String, fastparse.Parsed[(Exp
case (Val.Str(l), Expr.BinaryOp.`<=`, Val.Str(r)) => Val.bool(l <= r)
case (Val.Str(l), Expr.BinaryOp.`>=`, Val.Str(r)) => Val.bool(l >= r)
case (Val.Str(l), Expr.BinaryOp.`+`, r) =>
try Val.Str(l + Materializer.apply(r).transform(new Renderer()).toString)
try Val.Str(l + Materializer.stringify(r))
catch Util.tryCatchWrap(offset)
case (l, Expr.BinaryOp.`+`, Val.Str(r)) =>
try Val.Str(Materializer.apply(l).transform(new Renderer()).toString + r)
try Val.Str(Materializer.stringify(l) + r)
catch Util.tryCatchWrap(offset)
case (Val.Num(l), Expr.BinaryOp.`-`, Val.Num(r)) => Val.Num(l - r)
case (Val.Num(l), Expr.BinaryOp.`<<`, Val.Num(r)) => Val.Num(l.toLong << r.toLong)
Expand Down Expand Up @@ -422,68 +422,64 @@ class Evaluator(parseCache: collection.mutable.Map[String, fastparse.Parsed[(Exp
(self, sup) => makeNewScope(self, sup)
).toArray

lazy val newSelf: Val.Obj = new Val.Obj(
value.flatMap {
lazy val newSelf: Val.Obj = {
val builder = Map.newBuilder[String, Val.Obj.Member]
value.foreach {
case Member.Field(offset, fieldName, plus, None, sep, rhs) =>
visitFieldName(fieldName, offset).map(_ -> Val.Obj.Member(plus, sep, (self: Val.Obj, sup: Option[Val.Obj], _, _) => {
assertions(self)
visitExpr(rhs)(makeNewScope(Some(self), sup), implicitly)
}))
})).foreach(builder.+=)
case Member.Field(offset, fieldName, false, Some(argSpec), sep, rhs) =>
visitFieldName(fieldName, offset).map(_ -> Val.Obj.Member(false, sep, (self: Val.Obj, sup: Option[Val.Obj], _, _) => {
assertions(self)
visitMethod(rhs, argSpec, offset)(makeNewScope(Some(self), sup), implicitly)
}))
})).foreach(builder.+=)
case _: Member.BindStmt => // do nothing
case _: Member.AssertStmt => // do nothing
}

case _: Member.BindStmt => None
case _: Member.AssertStmt => None
}.toMap,
self => assertions(self),
None
)
new Val.Obj(builder.result(), self => assertions(self), None)
}
newSelf

case ObjBody.ObjComp(preLocals, key, value, postLocals, first, rest) =>
lazy val compScope: ValScope = scope.extend(
newSuper = None
)

lazy val newSelf: Val.Obj = new Val.Obj(
visitComp(first :: rest.toList, Seq(compScope))
.flatMap { s =>

lazy val newScope: ValScope = s.extend(
newBindings,
newDollar = scope.dollar0.orElse(Some(newSelf)),
newSelf = Some(newSelf),
newSuper = None
)

lazy val newBindings = visitBindings(
(preLocals.iterator ++ postLocals).collect{ case Member.BindStmt(b) => b},
(self, sup) => newScope
).toArray

visitExpr(key)(s, implicitly) match {
case Val.Str(k) =>
Some(k -> Val.Obj.Member(false, Visibility.Normal, (self: Val.Obj, sup: Option[Val.Obj], _, _) =>
visitExpr(value)(
s.extend(
newBindings,
newDollar = Some(s.dollar0.getOrElse(self)),
newSelf = Some(self),
),
implicitly
)
))
case Val.Null => None
}
lazy val newSelf: Val.Obj = {
val builder = Map.newBuilder[String, Val.Obj.Member]
for(s <- visitComp(first :: rest.toList, Seq(compScope))){
lazy val newScope: ValScope = s.extend(
newBindings,
newDollar = scope.dollar0.orElse(Some(newSelf)),
newSelf = Some(newSelf),
newSuper = None
)

lazy val newBindings = visitBindings(
(preLocals.iterator ++ postLocals).collect{ case Member.BindStmt(b) => b},
(self, sup) => newScope
).toArray

visitExpr(key)(s, implicitly) match {
case Val.Str(k) =>
builder += (k -> Val.Obj.Member(false, Visibility.Normal, (self: Val.Obj, sup: Option[Val.Obj], _, _) =>
visitExpr(value)(
s.extend(
newBindings,
newDollar = Some(s.dollar0.getOrElse(self)),
newSelf = Some(self),
),
implicitly
)
))
case Val.Null => // do nothing
}
.toMap,
_ => (),
None
)
}
new Val.Obj(builder.result(), _ => (), None)
}

newSelf
}
Expand Down
9 changes: 8 additions & 1 deletion sjsonnet/src/sjsonnet/Expr.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package sjsonnet

import scala.collection.{BitSet, mutable}

sealed trait Offsetted{
def offset: Int
}
Expand Down Expand Up @@ -46,7 +48,12 @@ object Expr{


case class Parened(offset: Int, value: Expr) extends Expr
case class Params(args: Seq[(String, Option[Expr], Int)])
case class Params(args: IndexedSeq[(String, Option[Expr], Int)]){
val argIndices: Map[String, Int] = args.map{case (k, d, i) => (k, i)}.toMap
val noDefaultIndices: BitSet = mutable.BitSet.empty ++ args.collect{case (_, None, i) => i}
val defaults: IndexedSeq[(Int, Expr)] = args.collect{case (_, Some(x), i) => (i, x)}
val allIndices: Set[Int] = args.map{case (_, _, i) => i}.toSet
}
case class Args(args: Seq[(Option[String], Expr)])

case class UnaryOp(offset: Int, op: UnaryOp.Op, value: Expr) extends Expr
Expand Down
2 changes: 1 addition & 1 deletion sjsonnet/src/sjsonnet/Interpreter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Interpreter(parseCache: collection.mutable.Map[String, fastparse.Parsed[(E
extVars: Map[String, ujson.Value],
tlaVars: Map[String, ujson.Value],
wd: Path,
importer: (Seq[Path], String) => Option[(Path, String)]) {
importer: (Path, String) => Option[(Path, String)]) {

val evaluator = new Evaluator(
parseCache,
Expand Down
64 changes: 31 additions & 33 deletions sjsonnet/src/sjsonnet/Materializer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ import java.util.IdentityHashMap
import sjsonnet.Expr.{FieldName, Member, ObjBody}
import sjsonnet.Expr.Member.Visibility
import upickle.core.Visitor

import scala.collection.mutable
object Materializer {
def apply(v: Val)(implicit evaluator: EvalScope): ujson.Value = apply0(v, ujson.Value)
def stringify(v: Val)(implicit evaluator: EvalScope): String = {
apply0(v, new sjsonnet.Renderer()).toString
}

def apply0[T](v: Val, visitor: Visitor[T, T])
(implicit evaluator: EvalScope): T = try {
Expand All @@ -28,31 +33,27 @@ object Materializer {
case obj: Val.Obj =>
obj.triggerAllAsserts(obj)

val keys = obj.getVisibleKeys().toSeq.sortBy(_._1)
val objVisitor = visitor.visitObject(keys.size, -1)
val keys = obj.getVisibleKeys().toArray.sortBy(_._1)
val objVisitor = visitor.visitObject(keys.length , -1)

for((k, hidden) <- keys if !hidden){
objVisitor.visitKey(-1)
objVisitor.visitKeyValue(k)
objVisitor.visitValue(
apply0(
obj.value(k, -1)(
new FileScope(null, Map()),
implicitly
for(t <- keys) {
val (k, hidden) = t
if (!hidden){
objVisitor.visitKeyValue(objVisitor.visitKey(-1).visitString(k, -1))
objVisitor.visitValue(
apply0(
obj.value(k, -1)(evaluator.emptyMaterializeFileScope, implicitly),
visitor
),
visitor
),
-1
)
-1
)
}
}
objVisitor.visitEnd(-1)

case f: Val.Func =>
apply0(
f.apply(Nil, "(memory)", -1)(
new FileScope(null, Map()),
implicitly
),
f.apply(Nil, "(memory)", -1)(evaluator.emptyMaterializeFileScope, implicitly),
visitor
)
}
Expand All @@ -67,19 +68,16 @@ object Materializer {
case ujson.Null => Val.Null
case ujson.Num(n) => Val.Num(n)
case ujson.Str(s) => Val.Str(s)
case ujson.Arr(xs) => Val.Arr(xs.map(x => Lazy(reverse(x))).toSeq)
case ujson.Obj(xs) => new Val.Obj(
xs.map(x =>
(
x._1,
Val.Obj.Member(false, Visibility.Normal,
(_: Val.Obj, _: Option[Val.Obj], _, _) => reverse(x._2)
)
case ujson.Arr(xs) => Val.Arr(xs.map(x => Lazy(reverse(x))).toArray[Lazy])
case ujson.Obj(xs) =>
val builder = Map.newBuilder[String, Val.Obj.Member]
for(x <- xs){
val v = Val.Obj.Member(false, Visibility.Normal,
(_: Val.Obj, _: Option[Val.Obj], _, _) => reverse(x._2)
)
).toMap,
_ => (),
None
)
builder += (x._1 -> v)
}
new Val.Obj(builder.result(), _ => (), None)
}

def toExpr(v: ujson.Value): Expr = v match{
Expand All @@ -88,12 +86,12 @@ object Materializer {
case ujson.Null => Expr.Null(0)
case ujson.Num(n) => Expr.Num(0, n)
case ujson.Str(s) => Expr.Str(0, s)
case ujson.Arr(xs) => Expr.Arr(0, xs.map(toExpr).toSeq)
case ujson.Arr(xs) => Expr.Arr(0, xs.map(toExpr).toArray[Expr])
case ujson.Obj(kvs) =>
Expr.Obj(0,
ObjBody.MemberList(
for((k, v) <- kvs.toSeq)
yield Member.Field(0, FieldName.Fixed(k), false, None, Visibility.Normal, toExpr(v))
for((k, v) <- kvs.toArray)
yield Member.Field(0, FieldName.Fixed(k), false, None, Visibility.Normal, toExpr(v))
)
)
}
Expand Down
7 changes: 5 additions & 2 deletions sjsonnet/src/sjsonnet/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ object Parser{
if (x.sliding(2).exists{case Seq(l, r) => l._1.isDefined && r._1.isEmpty case _ => false}) {
println("FAIL")
Fail.opaque("no positional params after named params")
} else Pass(Expr.Args(x))
} else Pass(Expr.Args(x.toArray[(Option[String], Expr)]))
}

def params[_: P]: P[Expr.Params] = P( (id ~ ("=" ~ expr).?).rep(sep = ",") ~ ",".? ).flatMapX{ x =>
Expand All @@ -319,7 +319,10 @@ object Parser{
if (seen(k)) overlap = k
else seen.add(k)
}
if (overlap == null) Pass(Expr.Params(x.map{case (k, v) => (k, v, indexFor(k))}))
if (overlap == null) {
val paramData = x.map{case (k, v) => (k, v, indexFor(k))}.toArray
Pass(Expr.Params(paramData))
}
else Fail.opaque("no duplicate parameter: " + overlap)

}
Expand Down
Loading

0 comments on commit 2108b89

Please sign in to comment.