Skip to content

Commit 0fa4d8f

Browse files
authored
Try to fix performance regression introduced by a0033bf (#1111)
For whatever reason, before we inlined `BODY` into the else branch ``` val x = if (...) { shift(p) { k => () } } else { ... }; BODY ``` but didn't anymore after a0033bf. This PR fixes this by always introducing joinpoints for ifs and matches. Then, when a continuation is used exactly ones in a follow-up pass, we will inline it. This way, more things become direct-style, avoiding pushes. It comes at the cost of potentially not seeing through the joinpoint (as in the case of `anf.effekt.md`) and of having more known jumps. However, our cost model says that known jumps are generally cheaper than pushes, so it should be worth it.
1 parent 88f3249 commit 0fa4d8f

File tree

2 files changed

+75
-62
lines changed

2 files changed

+75
-62
lines changed

effekt/jvm/src/test/scala/effekt/core/VMTests.scala

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -300,12 +300,12 @@ class VMTests extends munit.FunSuite {
300300
)),
301301

302302
examplesDir / "benchmarks" / "are_we_fast_yet" / "queens.effekt" -> Some(Summary(
303-
staticDispatches = 2898,
303+
staticDispatches = 2022,
304304
dynamicDispatches = 0,
305305
patternMatches = 0,
306-
branches = 3887,
307-
pushedFrames = 455,
308-
poppedFrames = 439,
306+
branches = 3347,
307+
pushedFrames = 1106,
308+
poppedFrames = 1090,
309309
allocations = 0,
310310
closures = 0,
311311
variableReads = 0,
@@ -320,8 +320,8 @@ class VMTests extends munit.FunSuite {
320320
dynamicDispatches = 0,
321321
patternMatches = 0,
322322
branches = 26736,
323-
pushedFrames = 5000,
324-
poppedFrames = 5000,
323+
pushedFrames = 671,
324+
poppedFrames = 671,
325325
allocations = 0,
326326
closures = 0,
327327
variableReads = 34546,
@@ -332,7 +332,7 @@ class VMTests extends munit.FunSuite {
332332
)),
333333

334334
examplesDir / "benchmarks" / "are_we_fast_yet" / "towers.effekt" -> Some(Summary(
335-
staticDispatches = 24606,
335+
staticDispatches = 16401,
336336
dynamicDispatches = 0,
337337
patternMatches = 16396,
338338
branches = 16287,
@@ -548,8 +548,8 @@ class VMTests extends munit.FunSuite {
548548
dynamicDispatches = 0,
549549
patternMatches = 0,
550550
branches = 210,
551-
pushedFrames = 68,
552-
poppedFrames = 67,
551+
pushedFrames = 1,
552+
poppedFrames = 1,
553553
allocations = 0,
554554
closures = 0,
555555
variableReads = 222,
@@ -592,7 +592,7 @@ class VMTests extends munit.FunSuite {
592592
)),
593593

594594
examplesDir / "benchmarks" / "effect_handlers_bench" / "tree_explore.effekt" -> Some(Summary(
595-
staticDispatches = 3317,
595+
staticDispatches = 2697,
596596
dynamicDispatches = 0,
597597
patternMatches = 2380,
598598
branches = 3167,
@@ -610,11 +610,11 @@ class VMTests extends munit.FunSuite {
610610
examplesDir / "benchmarks" / "effect_handlers_bench" / "triples.effekt" -> Some(Summary(
611611
staticDispatches = 231,
612612
dynamicDispatches = 0,
613-
patternMatches = 4,
613+
patternMatches = 0,
614614
branches = 701,
615-
pushedFrames = 702,
616-
poppedFrames = 880,
617-
allocations = 4,
615+
pushedFrames = 582,
616+
poppedFrames = 876,
617+
allocations = 0,
618618
closures = 0,
619619
variableReads = 0,
620620
variableWrites = 0,
@@ -674,12 +674,12 @@ class VMTests extends munit.FunSuite {
674674
)),
675675

676676
examplesDir / "casestudies" / "lexer.effekt.md" -> Some(Summary(
677-
staticDispatches = 343,
677+
staticDispatches = 302,
678678
dynamicDispatches = 18,
679679
patternMatches = 205,
680680
branches = 405,
681-
pushedFrames = 195,
682-
poppedFrames = 195,
681+
pushedFrames = 187,
682+
poppedFrames = 187,
683683
allocations = 109,
684684
closures = 27,
685685
variableReads = 164,
@@ -690,12 +690,12 @@ class VMTests extends munit.FunSuite {
690690
)),
691691

692692
examplesDir / "casestudies" / "parser.effekt.md" -> Some(Summary(
693-
staticDispatches = 12887,
693+
staticDispatches = 11375,
694694
dynamicDispatches = 783,
695695
patternMatches = 9427,
696-
branches = 14696,
697-
pushedFrames = 7583,
698-
poppedFrames = 7559,
696+
branches = 14892,
697+
pushedFrames = 7172,
698+
poppedFrames = 7160,
699699
allocations = 3848,
700700
closures = 521,
701701
variableReads = 6742,
@@ -706,12 +706,12 @@ class VMTests extends munit.FunSuite {
706706
)),
707707

708708
examplesDir / "casestudies" / "anf.effekt.md" -> Some(Summary(
709-
staticDispatches = 6925,
709+
staticDispatches = 6140,
710710
dynamicDispatches = 443,
711711
patternMatches = 5098,
712-
branches = 8018,
713-
pushedFrames = 4298,
714-
poppedFrames = 4285,
712+
branches = 8110,
713+
pushedFrames = 4086,
714+
poppedFrames = 4074,
715715
allocations = 2143,
716716
closures = 358,
717717
variableReads = 4080,
@@ -722,12 +722,12 @@ class VMTests extends munit.FunSuite {
722722
)),
723723

724724
examplesDir / "casestudies" / "inference.effekt.md" -> Some(Summary(
725-
staticDispatches = 1482058,
725+
staticDispatches = 1482048,
726726
dynamicDispatches = 3224955,
727727
patternMatches = 1497935,
728728
branches = 304502,
729-
pushedFrames = 2943014,
730-
poppedFrames = 2077323,
729+
pushedFrames = 2643292,
730+
poppedFrames = 2066027,
731731
allocations = 4654126,
732732
closures = 867068,
733733
variableReads = 2955965,
@@ -738,13 +738,13 @@ class VMTests extends munit.FunSuite {
738738
)),
739739

740740
examplesDir / "pos" / "raytracer.effekt" -> Some(Summary(
741-
staticDispatches = 91784,
741+
staticDispatches = 85740,
742742
dynamicDispatches = 0,
743-
patternMatches = 771652,
743+
patternMatches = 763548,
744744
branches = 65951,
745-
pushedFrames = 51235,
746-
poppedFrames = 51235,
747-
allocations = 78909,
745+
pushedFrames = 43131,
746+
poppedFrames = 43131,
747+
allocations = 70805,
748748
closures = 0,
749749
variableReads = 77886,
750750
variableWrites = 26904,
@@ -808,8 +808,8 @@ class VMTests extends munit.FunSuite {
808808
dynamicDispatches = 52,
809809
patternMatches = 37,
810810
branches = 0,
811-
pushedFrames = 34,
812-
poppedFrames = 34,
811+
pushedFrames = 17,
812+
poppedFrames = 17,
813813
allocations = 37,
814814
closures = 37,
815815
variableReads = 10,

effekt/shared/src/main/scala/effekt/core/optimizer/Normalizer.scala

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,17 @@ object Normalizer { normal =>
235235

236236
case Stmt.Val(id, tpe, binding, body) =>
237237

238-
// def barendregt(stmt: Stmt): Stmt = new Renamer().apply(stmt)
238+
def joinpoint(id: Id, tpe: ValueType, body: Stmt)(f: BlockVar => Context ?=> Stmt)(using C: Context): Stmt = body match {
239+
// do not eta-expand variables
240+
case Stmt.App(k: BlockVar, Nil, ValueVar(x, tpe) :: Nil, Nil) if x == id || tpe == Type.TUnit => f(k)
241+
case _ =>
242+
val k = Id("k")
243+
C.usage.put(k, Usage.Many)
244+
val kDef = Block.BlockLit(Nil, Nil, ValueParam(id, tpe) :: Nil, Nil, body)
245+
Stmt.Def(k, kDef, f(Block.BlockVar(k, kDef.tpe, kDef.capt))(using C.bind(k, kDef)))
246+
}
239247

240-
def normalizeVal(id: Id, tpe: ValueType, binding: Stmt, body: Stmt): Stmt = normalize(binding) match {
248+
def normalizeVal(id: Id, tpe: ValueType, binding: Stmt, body: Stmt)(using C: Context): Stmt = normalize(binding) match {
241249

242250
// [[ val x = ABORT; body ]] = ABORT
243251
case abort if abort.tpe == Type.TBottom =>
@@ -252,34 +260,36 @@ object Normalizer { normal =>
252260
BlockLit(tparams, cparams, vparams, BlockParam(k, BlockType.Interface(Type.ResumeSymbol, List(tpeB, answer)), captures) :: Nil,
253261
normalize(body2)))
254262

255-
// [[ val x = sc match { case id(ps) => body2 }; body ]] = sc match { case id(ps) => val x = body2; body }
256263
case Stmt.Match(sc, List((id2, BlockLit(tparams2, cparams2, vparams2, bparams2, body2))), None) =>
257264
Stmt.Match(sc, List((id2, BlockLit(tparams2, cparams2, vparams2, bparams2,
258265
normalizeVal(id, tpe, body2, body)))), None)
259266

260-
// These rewrites do not seem to contribute a lot given their complexity...
261-
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
262-
263-
// [[ val x = if (cond) { thn } else { els }; body ]] = if (cond) { [[ val x = thn; body ]] } else { [[ val x = els; body ]] }
264-
// case normalized @ Stmt.If(cond, thn, els) if body.size <= 2 =>
265-
// // since we duplicate the body, we need to freshen the names
266-
// val normalizedThn = barendregt(normalizeVal(id, tpe, thn, body))
267-
// val normalizedEls = barendregt(normalizeVal(id, tpe, els, body))
268-
//
269-
// Stmt.If(cond, normalizedThn, normalizedEls)
270-
//
271-
// case Stmt.Match(sc, clauses, default)
272-
// // necessary since otherwise we loose Nothing-boxing
273-
// // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
274-
// if body.size <= 2 && (clauses.size + default.size) >= 1 =>
275-
// val normalizedClauses = clauses map {
276-
// case (id2, BlockLit(tparams2, cparams2, vparams2, bparams2, body2)) =>
277-
// (id2, BlockLit(tparams2, cparams2, vparams2, bparams2, barendregt(normalizeVal(id, tpe, body2, body))): BlockLit)
278-
// }
279-
// val normalizedDefault = default map { stmt => barendregt(normalizeVal(id, tpe, stmt, body)) }
280-
// Stmt.Match(sc, normalizedClauses, normalizedDefault)
281-
282-
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
267+
// Introduce joinpoints that are potentially later inlined or garbage collected
268+
// [[ val x = if (cond) { thn } else { els }; body ]] =
269+
// def k(x) = [[ body ]]
270+
// if (cond) { [[ val x1 = thn; k(x1) ]] } else { [[ val x2 = els; k(x2) ]] }
271+
case Stmt.If(cond, thn, els) =>
272+
joinpoint(id, tpe, normalize(body)) { k =>
273+
val x1 = Id(id.name)
274+
val x2 = Id(id.name)
275+
Stmt.If(cond,
276+
normalizeVal(x1, tpe, thn, Stmt.App(k, Nil, List(ValueVar(x1, tpe)), Nil)),
277+
normalizeVal(x2, tpe, els, Stmt.App(k, Nil, List(ValueVar(x2, tpe)), Nil)))
278+
}
279+
280+
281+
case Stmt.Match(sc, clauses, default) =>
282+
joinpoint(id, tpe, normalize(body)) { k =>
283+
Stmt.Match(sc, clauses.map {
284+
case (tag, BlockLit(tparams, cparams, vparams, bparams, body)) =>
285+
val x = Id(id.name)
286+
(tag, BlockLit(tparams, cparams, vparams, bparams,
287+
normalizeVal(x, tpe, body, Stmt.App(k, Nil, List(ValueVar(x, tpe)), Nil))))
288+
}, default.map { stmt =>
289+
val x = Id(id.name)
290+
normalizeVal(x, tpe, stmt, Stmt.App(k, Nil, List(ValueVar(x, tpe)), Nil))
291+
})
292+
}
283293

284294
// [[ val x = return e; s ]] = let x = [[ e ]]; [[ s ]]
285295
case Stmt.Return(expr2) =>
@@ -312,9 +322,12 @@ object Normalizer { normal =>
312322
case Stmt.Put(ref2, capt2, value2, body2) =>
313323
Stmt.Put(ref2, capt2, value2, normalizeVal(id, tpe, body2, body))
314324

315-
// [[ val x = stmt; return x ]] = [[ stmt ]]
316325
case other => normalize(body) match {
326+
// [[ val x = stmt; return x ]] = [[ stmt ]]
317327
case Stmt.Return(x: ValueVar) if x.id == id => other
328+
// [[ val x: Unit = stmt; return () ]] = [[ stmt ]]
329+
case Stmt.Return(x) if x.tpe == Type.TUnit && other.tpe == Type.TUnit => other
330+
// [[ val x = stmt; body ]] = val x = [[ stmt ]]; [[ body ]]
318331
case normalizedBody => Stmt.Val(id, tpe, other, normalizedBody)
319332
}
320333
}

0 commit comments

Comments
 (0)