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

perf: Unfold variables in parallel where it is possible (fixes LNG-109 ) #656

Merged
merged 5 commits into from
Feb 1, 2023
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
30 changes: 28 additions & 2 deletions model/inline/src/main/scala/aqua/model/inline/Inline.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ object SeqMode extends MergeMode
object ParMode extends MergeMode

/**
*
* @param flattenValues values that need to be resolved before `predo`.
* ListMap for keeping order of values (mostly for debugging purposes)
* @param predo operations tree
Expand All @@ -23,7 +22,34 @@ private[inline] case class Inline(
flattenValues: ListMap[String, ValueRaw] = ListMap.empty,
predo: Chain[OpModel.Tree] = Chain.empty,
mergeMode: MergeMode = ParMode
)
) {

def desugar: Inline = {
val desugaredPredo =
predo.toList match {
case Nil => Chain.empty
case x :: Nil =>
Chain.one(x)
case l =>
mergeMode match
case SeqMode =>
Chain.one(SeqModel.wrap(l: _*))
case ParMode => Chain.one(ParModel.wrap(l: _*))
}

Inline(
flattenValues,
desugaredPredo
)
}

def mergeWith(inline: Inline, mode: MergeMode): Inline = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it go to Monoid[Inline]?

Copy link
Contributor

@gurinderu gurinderu Jan 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like Monoid[Inline@@MergeMode] ?

val left = desugar
val right = inline.desugar

Inline(left.flattenValues ++ right.flattenValues, left.predo ++ right.predo, mode)
}
}

// TODO may not be needed there
private[inline] object Inline {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ object MakeStructRawInliner extends RawInliner[MakeStructRaw] {
name <- Mangler[S].findAndForbidName(raw.structType.name + "_obj")
foldedFields <- raw.fields.nonEmptyTraverse(unfold(_))
varModel = VarModel(name, raw.baseType)
valsInline = foldedFields.toSortedMap.values.map(_._2).fold(Inline.empty)(_ |+| _)
valsInline = foldedFields.toSortedMap.values.map(_._2).fold(Inline.empty)(_ |+| _).desugar
fields = foldedFields.map(_._1)
objCreation <- createObj(fields, varModel)
} yield {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ object RawValueInliner extends Logging {
}
}

private def toModel[S: Mangler: Exports: Arrows](
private[inline] def toModel[S: Mangler: Exports: Arrows](
unfoldF: State[S, (ValueModel, Inline)]
): State[S, (ValueModel, Option[OpModel.Tree])] =
for {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ object ApplyIntoCopyRawInliner extends Logging {
name <- Mangler[S].findAndForbidName(value.name + "_obj_copy")
foldedFields <- intoCopy.fields.nonEmptyTraverse(unfold(_))
varModel = VarModel(name, value.baseType)
valsInline = foldedFields.toSortedMap.values.map(_._2).fold(Inline.empty)(_ |+| _)
valsInline = foldedFields.toSortedMap.values.map(_._2).fold(Inline.empty)(_ |+| _).desugar
fields = foldedFields.map(_._1)
objCopy <- copyObj(value, fields, varModel)
} yield {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import aqua.model.{
XorModel
}
import aqua.model.inline.Inline
import aqua.model.inline.SeqMode
import aqua.model.inline.{ParMode, SeqMode}
import aqua.model.inline.RawValueInliner.unfold
import aqua.model.inline.state.{Arrows, Exports, Mangler}
import aqua.raw.value.{
Expand All @@ -42,6 +42,7 @@ import aqua.types.{ArrayType, CanonStreamType, ScalarType, StreamType, Type}
import cats.Eval
import cats.data.{Chain, IndexedStateT, State}
import cats.syntax.monoid.*
import cats.syntax.traverse.*
import cats.instances.list.*
import scribe.Logging

Expand Down Expand Up @@ -140,40 +141,78 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi
mergeMode = SeqMode
)
}
}

// Helper for `optimizeProperties`
private case class PropertyRawWithModel(raw: PropertyRaw, model: Option[PropertyModel])

// Unfold properties that we can process in parallel
private def optimizeProperties[S: Mangler: Exports: Arrows](
properties: Chain[PropertyRaw]
): State[S, (Chain[PropertyRawWithModel], Inline)] = {
properties.map {
case iir @ IntoIndexRaw(vr, t) =>
unfold(vr, propertiesAllowed = false).flatMap {
case (vm@VarModel(_, _, _), inline) if vm.properties.nonEmpty =>
removeProperties(vm).map { case (vf, inlf) =>
PropertyRawWithModel(iir, Option(IntoIndexModel(vf.name, t))) -> Inline(
inline.flattenValues ++ inlf.flattenValues,
inline.predo ++ inlf.predo,
mergeMode = SeqMode
)
}
case (VarModel(name, _, _), inline) =>
State.pure(PropertyRawWithModel(iir, Option(IntoIndexModel(name, t))) -> inline)
case (LiteralModel(literal, _), inline) =>
State.pure(PropertyRawWithModel(iir, Option(IntoIndexModel(literal, t))) -> inline)
}

case p => State.pure(PropertyRawWithModel(p, None) -> Inline.empty)
}.sequence.map { (propsWithInline: Chain[(PropertyRawWithModel, Inline)]) =>
val fullInline = propsWithInline.map(_._2).foldLeft(Inline.empty)(_ |+| _)
val props = propsWithInline.map(_._1)
(props, fullInline)
}
}

private def unfoldProperties[S: Mangler: Exports: Arrows](
prevInline: Inline,
vm: VarModel,
properties: Chain[PropertyRaw],
propertiesAllowed: Boolean
): State[S, (VarModel, Inline)] = {
properties
.foldLeft[State[S, (VarModel, Inline)]](
State.pure((vm, prevInline))
) { case (state, property) =>
state.flatMap { case (vm, leftInline) =>
unfoldProperty(vm, property).flatMap {
case (v, i) if !propertiesAllowed && v.properties.nonEmpty =>
removeProperties(v).map { case (vf, inlf) =>
vf -> Inline(
leftInline.flattenValues ++ i.flattenValues ++ inlf.flattenValues,
leftInline.predo ++ i.predo ++ inlf.predo,
mergeMode = SeqMode
)
}
case (v, i) =>
State.pure(
v -> Inline(
leftInline.flattenValues ++ i.flattenValues,
leftInline.predo ++ i.predo,
mergeMode = SeqMode
)
)
optimizeProperties(properties).flatMap { case (optimizedProps, optimizationInline) =>
optimizedProps
.foldLeft[State[S, (VarModel, Inline)]](
State.pure((vm, prevInline.mergeWith(optimizationInline, SeqMode)))
) { case (state, property) =>
state.flatMap { case (vm, leftInline) =>
property match {
case PropertyRawWithModel(_, Some(model)) =>
State.pure(vm.copy(properties = vm.properties :+ model) -> leftInline)
case PropertyRawWithModel(raw, _) =>
unfoldProperty(vm, raw).flatMap {
case (v, i) if !propertiesAllowed && v.properties.nonEmpty =>
removeProperties(v).map { case (vf, inlf) =>
vf -> Inline(
leftInline.flattenValues ++ i.flattenValues ++ inlf.flattenValues,
leftInline.predo ++ i.predo ++ inlf.predo,
mergeMode = SeqMode
)
}
case (v, i) =>
State.pure(
v -> Inline(
leftInline.flattenValues ++ i.flattenValues,
leftInline.predo ++ i.predo,
mergeMode = SeqMode
)
)
}
}
}
}
}
}
}

private def unfoldRawWithProperties[S: Mangler: Exports: Arrows](
Expand All @@ -184,7 +223,7 @@ object ApplyPropertiesRawInliner extends RawInliner[ApplyPropertyRaw] with Loggi
((raw, properties.headOption) match {
case (vr @ VarRaw(_, st @ StreamType(_)), Some(IntoIndexRaw(idx, _))) =>
unfold(vr).flatMap {
case (vm @ VarModel(nameVM, _, _), inl) =>
case (VarModel(nameVM, _, _), inl) =>
val gateRaw = ApplyGateRaw(nameVM, st, idx)
unfold(gateRaw).flatMap {
case (gateResVal: VarModel, gateResInline) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package aqua.model.inline

import aqua.model.*
import aqua.model.inline.raw.ApplyIntoCopyRawInliner
import aqua.model.inline.state.InliningState
import aqua.raw.ops.*
import aqua.raw.value.*
import aqua.types.*
import cats.data.{Chain, NonEmptyList, NonEmptyMap}
import cats.syntax.show.*
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class CopyInlinerSpec extends AnyFlatSpec with Matchers {

"copy inliner" should "unfold values in parallel" in {

val structType = StructType(
"struct_type",
NonEmptyMap.of("field1" -> ScalarType.u32, "field2" -> ScalarType.string)
)

val arrType = ArrayType(ScalarType.string)
val length = FunctorRaw("length", ScalarType.u32)
val lengthValue = VarRaw("l", arrType).withProperty(length)

val getField = CallArrowRaw(None, "get_field", Nil, ArrowType(NilType, UnlabeledConsType(ScalarType.string, NilType)), Option(LiteralRaw("\"serv\"", ScalarType.string)))

val copyRaw =
IntoCopyRaw(structType, NonEmptyMap.of("field1" -> lengthValue, "field2" -> getField))
val varName = "some_struct"
val varRaw = VarRaw(varName, structType).withProperty(copyRaw)

val (model, tree) =
RawValueInliner.valueToModel[InliningState](varRaw, false).run(InliningState()).value._2

val result = VarModel(varName + "_obj_copy", structType)
model shouldBe result

val lengthModel = FunctorModel("length", ScalarType.u32)

tree.get.equalsOrShowDiff(
SeqModel.wrap(
ParModel.wrap(
SeqModel.wrap(
FlattenModel(VarModel("l", arrType), "l_to_functor").leaf,
FlattenModel(VarModel("l_to_functor", arrType, Chain.one(lengthModel)), "l_length").leaf,
),
CallServiceModel(
"serv",
"get_field",
Nil,
VarModel("get_field", ScalarType.string)
).leaf
),
CallServiceModel(
"json",
"puts",
VarModel(varName, structType) :: LiteralModel.fromRaw(
LiteralRaw.quote("field1")
) :: VarModel("l_length", ScalarType.u32) :: LiteralModel.fromRaw(
LiteralRaw.quote("field2")
) :: VarModel("get_field", ScalarType.string) :: Nil,
result
).leaf
)
) shouldBe true

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package aqua.model.inline

import aqua.model.*
import aqua.model.inline.raw.ApplyIntoCopyRawInliner
import aqua.model.inline.state.InliningState
import aqua.raw.ops.*
import aqua.raw.value.*
import aqua.types.*
import cats.data.{Chain, NonEmptyList, NonEmptyMap}
import cats.syntax.show.*
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class MakeStructInlinerSpec extends AnyFlatSpec with Matchers {

"make struct inliner" should "unfold values in parallel" in {

val structType = StructType(
"struct_type",
NonEmptyMap.of("field1" -> ScalarType.u32, "field2" -> ScalarType.string)
)

val arrType = ArrayType(ScalarType.string)
val length = FunctorRaw("length", ScalarType.u32)
val lengthValue = VarRaw("l", arrType).withProperty(length)

val getField = CallArrowRaw(
None,
"get_field",
Nil,
ArrowType(NilType, UnlabeledConsType(ScalarType.string, NilType)),
Option(LiteralRaw("\"serv\"", ScalarType.string))
)

val makeStruct =
MakeStructRaw(NonEmptyMap.of("field1" -> lengthValue, "field2" -> getField), structType)
val varName = structType.name

val (model, tree) =
RawValueInliner.valueToModel[InliningState](makeStruct, false).run(InliningState()).value._2

val result = VarModel(varName + "_obj", structType)
model shouldBe result

val lengthModel = FunctorModel("length", ScalarType.u32)

tree.get.equalsOrShowDiff(
SeqModel.wrap(
ParModel.wrap(
SeqModel.wrap(
FlattenModel(VarModel("l", arrType), "l_to_functor").leaf,
FlattenModel(VarModel("l_to_functor", arrType, Chain.one(lengthModel)), "l_length").leaf
),
CallServiceModel(
"serv",
"get_field",
Nil,
VarModel("get_field", ScalarType.string)
).leaf
),
CallServiceModel(
"json",
"obj",
LiteralModel.fromRaw(
LiteralRaw.quote("field1")
) :: VarModel("l_length", ScalarType.u32) :: LiteralModel.fromRaw(
LiteralRaw.quote("field2")
) :: VarModel("get_field", ScalarType.string) :: Nil,
result
).leaf
)
) shouldBe true

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import aqua.raw.value.{ApplyPropertyRaw, FunctorRaw, IntoIndexRaw, LiteralRaw, V
import aqua.types.*
import cats.data.NonEmptyMap
import cats.data.Chain
import cats.syntax.show.*
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

Expand Down Expand Up @@ -207,7 +208,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
resTree.isEmpty should be(false)

resTree.get.equalsOrShowDiff(
SeqModel.wrap(
ParModel.wrap(
SeqModel.wrap(
FlattenModel(
VarModel(
Expand Down Expand Up @@ -284,7 +285,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
resTree.isEmpty should be(false)

resTree.get.equalsOrShowDiff(
SeqModel.wrap(
ParModel.wrap(
FlattenModel(
VarModel(
"ys",
Expand Down Expand Up @@ -325,7 +326,6 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
)
)
)
println(resTree)
}

"raw value inliner" should "desugarize stream with length" in {
Expand Down Expand Up @@ -367,7 +367,7 @@ class RawValueInlinerSpec extends AnyFlatSpec with Matchers {
resTree.isEmpty should be(false)

resTree.get.equalsOrShowDiff(
SeqModel.wrap(
ParModel.wrap(
FlattenModel(
VarModel(
"ys",
Expand Down