Skip to content

Commit f3211d2

Browse files
committed
Optimize filterConserve
- Avoid recursions deeper than 10 - Avoid copying tail after last dropped element
1 parent 49dcd2e commit f3211d2

File tree

1 file changed

+30
-16
lines changed

1 file changed

+30
-16
lines changed

compiler/src/dotty/tools/dotc/core/Decorators.scala

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ object Decorators {
7070
NoSymbol
7171
}
7272

73-
final val MaxFilterRecursions = 1000
73+
final val MaxFilterRecursions = 10
7474

7575
/** Implements filterConserve, zipWithConserve methods
7676
* on lists that avoid duplication of list nodes where feasible.
@@ -105,24 +105,38 @@ object Decorators {
105105
}
106106

107107
/** Like `xs filter p` but returns list `xs` itself - instead of a copy -
108-
* if `p` is true for all elements and `xs` is not longer
109-
* than `MaxFilterRecursions`.
108+
* if `p` is true for all elements.
110109
*/
111-
def filterConserve(p: T => Boolean): List[T] = {
112-
def loop(xs: List[T], nrec: Int): List[T] = xs match {
113-
case Nil => xs
110+
def filterConserve(p: T => Boolean): List[T] =
111+
112+
def addAll(buf: ListBuffer[T], from: List[T], until: List[T]): ListBuffer[T] =
113+
if from eq until then buf else addAll(buf += from.head, from.tail, until)
114+
115+
def loopWithBuffer(buf: ListBuffer[T], xs: List[T]): List[T] = xs match
114116
case x :: xs1 =>
115-
if (nrec < MaxFilterRecursions) {
116-
val ys1 = loop(xs1, nrec + 1)
117-
if (p(x))
118-
if (ys1 eq xs1) xs else x :: ys1
117+
if p(x) then buf += x
118+
loopWithBuffer(buf, xs1)
119+
case nil => buf.toList
120+
121+
def loop(keep: List[T], explore: List[T], keepCount: Int, recCount: Int): List[T] =
122+
explore match
123+
case x :: rest =>
124+
if p(x) then
125+
loop(keep, rest, keepCount + 1, recCount)
126+
else if keepCount <= 3 && recCount <= MaxFilterRecursions then
127+
val rest1 = loop(rest, rest, 0, recCount + 1)
128+
keepCount match
129+
case 0 => rest1
130+
case 1 => keep.head :: rest1
131+
case 2 => keep.head :: keep.tail.head :: rest1
132+
case 3 => val tl = keep.tail; keep.head :: tl.head :: tl.tail.head :: rest1
119133
else
120-
ys1
121-
}
122-
else xs filter p
123-
}
124-
loop(xs, 0)
125-
}
134+
loopWithBuffer(addAll(new ListBuffer[T], keep, explore), rest)
135+
case nil =>
136+
keep
137+
138+
loop(xs, xs, 0, 0)
139+
end filterConserve
126140

127141
/** Like `xs.lazyZip(ys).map(f)`, but returns list `xs` itself
128142
* - instead of a copy - if function `f` maps all elements of

0 commit comments

Comments
 (0)