Skip to content

Commit d69aa70

Browse files
committed
Merge pull request scala#4333 from Ichoran/opt-Iterator-2.11.x
Performance optimization - Iterator
2 parents 63daba3 + 301011e commit d69aa70

File tree

1 file changed

+162
-59
lines changed

1 file changed

+162
-59
lines changed

src/library/scala/collection/Iterator.scala

Lines changed: 162 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -392,8 +392,16 @@ trait Iterator[+A] extends TraversableOnce[A] {
392392
*/
393393
def flatMap[B](f: A => GenTraversableOnce[B]): Iterator[B] = new AbstractIterator[B] {
394394
private var cur: Iterator[B] = empty
395-
def hasNext: Boolean =
396-
cur.hasNext || self.hasNext && { cur = f(self.next()).toIterator; hasNext }
395+
private def nextCur() { cur = f(self.next()).toIterator }
396+
def hasNext: Boolean = {
397+
// Equivalent to cur.hasNext || self.hasNext && { nextCur(); hasNext }
398+
// but slightly shorter bytecode (better JVM inlining!)
399+
while (!cur.hasNext) {
400+
if (!self.hasNext) return false
401+
nextCur()
402+
}
403+
true
404+
}
397405
def next(): B = (if (hasNext) cur else empty).next()
398406
}
399407

@@ -405,6 +413,7 @@ trait Iterator[+A] extends TraversableOnce[A] {
405413
* @note Reuse: $consumesAndProducesIterator
406414
*/
407415
def filter(p: A => Boolean): Iterator[A] = new AbstractIterator[A] {
416+
// TODO 2.12 - Make a full-fledged FilterImpl that will reverse sense of p
408417
private var hd: A = _
409418
private var hdDefined: Boolean = false
410419

@@ -470,13 +479,27 @@ trait Iterator[+A] extends TraversableOnce[A] {
470479
* @note Reuse: $consumesAndProducesIterator
471480
*/
472481
@migration("`collect` has changed. The previous behavior can be reproduced with `toSeq`.", "2.8.0")
473-
def collect[B](pf: PartialFunction[A, B]): Iterator[B] = {
474-
val self = buffered
475-
new AbstractIterator[B] {
476-
private def skip() = while (self.hasNext && !pf.isDefinedAt(self.head)) self.next()
477-
def hasNext = { skip(); self.hasNext }
478-
def next() = { skip(); pf(self.next()) }
482+
def collect[B](pf: PartialFunction[A, B]): Iterator[B] = new AbstractIterator[B] {
483+
// Manually buffer to avoid extra layer of wrapping with buffered
484+
private[this] var hd: A = _
485+
486+
// Little state machine to keep track of where we are
487+
// Seek = 0; Found = 1; Empty = -1
488+
// Not in vals because scalac won't make them static (@inline def only works with -optimize)
489+
// BE REALLY CAREFUL TO KEEP COMMENTS AND NUMBERS IN SYNC!
490+
private[this] var status = 0/*Seek*/
491+
492+
def hasNext = {
493+
while (status == 0/*Seek*/) {
494+
if (self.hasNext) {
495+
hd = self.next()
496+
if (pf.isDefinedAt(hd)) status = 1/*Found*/
497+
}
498+
else status = -1/*Empty*/
499+
}
500+
status == 1/*Found*/
479501
}
502+
def next() = if (hasNext) { status = 0/*Seek*/; pf(hd) } else Iterator.empty.next()
480503
}
481504

482505
/** Produces a collection containing cumulative results of applying the
@@ -578,33 +601,105 @@ trait Iterator[+A] extends TraversableOnce[A] {
578601
* @note Reuse: $consumesOneAndProducesTwoIterators
579602
*/
580603
def span(p: A => Boolean): (Iterator[A], Iterator[A]) = {
581-
val self = buffered
582-
583-
// Must be a named class to avoid structural call to finish from trailing iterator
604+
/*
605+
* Giving a name to following iterator (as opposed to trailing) because
606+
* anonymous class is represented as a structural type that trailing
607+
* iterator is referring (the finish() method) and thus triggering
608+
* handling of structural calls. It's not what's intended here.
609+
*/
584610
class Leading extends AbstractIterator[A] {
585-
private val drained = new mutable.Queue[A]
586-
private var finished = false
587-
def finish(): Unit = {
588-
require(!finished)
589-
finished = true
590-
while (selfish) drained += self.next
611+
var lookahead: mutable.Queue[A] = null
612+
var hd: A = _
613+
/* Status is kept with magic numbers
614+
* 1 means next element is in hd and we're still reading into this iterator
615+
* 0 means we're still reading but haven't found a next element
616+
* -1 means we are done reading into the iterator, so we must rely on lookahead
617+
* -2 means we are done but have saved hd for the other iterator to use as its first element
618+
*/
619+
var status = 0
620+
private def store(a: A) {
621+
if (lookahead == null) lookahead = new mutable.Queue[A]
622+
lookahead += a
623+
}
624+
def hasNext = {
625+
if (status < 0) (lookahead ne null) && lookahead.nonEmpty
626+
else if (status > 0) true
627+
else {
628+
if (self.hasNext) {
629+
hd = self.next()
630+
status = if (p(hd)) 1 else -2
631+
}
632+
else status = -1
633+
status > 0
634+
}
591635
}
592-
private def selfish = self.hasNext && p(self.head)
593-
def hasNext = if (finished) drained.nonEmpty else selfish
594636
def next() = {
595-
if (finished) drained.dequeue()
596-
else if (selfish) self.next()
637+
if (hasNext) {
638+
if (status == 1) { status = 0; hd }
639+
else lookahead.dequeue()
640+
}
597641
else empty.next()
598642
}
643+
def finish(): Boolean = {
644+
if (status == -1) false
645+
else if (status == -2) {
646+
status = -1
647+
true
648+
}
649+
else {
650+
if (status == 1) store(hd)
651+
while (self.hasNext) {
652+
val a = self.next()
653+
if (p(a)) store(a)
654+
else {
655+
hd = a
656+
status = -1
657+
return true
658+
}
659+
}
660+
false
661+
}
662+
}
599663
}
664+
600665
val leading = new Leading
666+
601667
val trailing = new AbstractIterator[A] {
602-
private lazy val it = {
603-
leading.finish()
604-
self
668+
private[this] var myLeading = leading
669+
/* Status flags meanings:
670+
* -1 not yet accesssed
671+
* 0 single element waiting in leading
672+
* 1 defer to self
673+
*/
674+
private[this] var status = -1
675+
def hasNext = {
676+
if (status > 0) self.hasNext
677+
else {
678+
if (status == 0) true
679+
else if (myLeading.finish()) {
680+
status = 0
681+
true
682+
}
683+
else {
684+
status = 1
685+
myLeading = null
686+
self.hasNext
687+
}
688+
}
605689
}
606-
def hasNext = it.hasNext
607-
def next() = it.next()
690+
def next() = {
691+
if (hasNext) {
692+
if (status > 0) self.next()
693+
else {
694+
status = 1
695+
val ans = myLeading.hd
696+
myLeading = null
697+
ans
698+
}
699+
}
700+
else Iterator.empty.next()
701+
}
702+
608703
override def toString = "unknown-if-empty iterator"
609704
}
610705

@@ -618,18 +713,35 @@ trait Iterator[+A] extends TraversableOnce[A] {
618713
* @return an iterator consisting of the remaining elements
619714
* @note Reuse: $consumesAndProducesIterator
620715
*/
621-
def dropWhile(p: A => Boolean): Iterator[A] = {
622-
val self = buffered
623-
new AbstractIterator[A] {
624-
var dropped = false
625-
private def skip() =
626-
if (!dropped) {
627-
while (self.hasNext && p(self.head)) self.next()
628-
dropped = true
716+
def dropWhile(p: A => Boolean): Iterator[A] = new AbstractIterator[A] {
717+
// Magic value: -1 = hasn't dropped, 0 = found first, 1 = defer to parent iterator
718+
private[this] var status = -1
719+
// Local buffering to avoid double-wrap with .buffered
720+
private[this] var fst: A = _
721+
def hasNext: Boolean =
722+
if (status == 1) self.hasNext
723+
else if (status == 0) true
724+
else {
725+
while (self.hasNext) {
726+
val a = self.next()
727+
if (!p(a)) {
728+
fst = a
729+
status = 0
730+
return true
731+
}
629732
}
630-
def hasNext = { skip(); self.hasNext }
631-
def next() = { skip(); self.next() }
632-
}
733+
status = 1
734+
false
735+
}
736+
def next() =
737+
if (hasNext) {
738+
if (status == 1) self.next()
739+
else {
740+
status = 1
741+
fst
742+
}
743+
}
744+
else Iterator.empty.next()
633745
}
634746

635747
/** Creates an iterator formed from this iterator and another iterator
@@ -777,7 +889,7 @@ trait Iterator[+A] extends TraversableOnce[A] {
777889
* is equal (as determined by `==`) to `elem`, `false` otherwise.
778890
* @note Reuse: $consumesIterator
779891
*/
780-
def contains(elem: Any): Boolean = exists(_ == elem)
892+
def contains(elem: Any): Boolean = exists(_ == elem) // Note--this seems faster than manual inlining!
781893

782894
/** Finds the first value produced by the iterator satisfying a
783895
* predicate, if any.
@@ -789,12 +901,11 @@ trait Iterator[+A] extends TraversableOnce[A] {
789901
* @note Reuse: $consumesIterator
790902
*/
791903
def find(p: A => Boolean): Option[A] = {
792-
var res: Option[A] = None
793-
while (res.isEmpty && hasNext) {
794-
val e = next()
795-
if (p(e)) res = Some(e)
904+
while (hasNext) {
905+
val a = next()
906+
if (p(a)) return Some(a)
796907
}
797-
res
908+
None
798909
}
799910

800911
/** Returns the index of the first produced value satisfying a predicate, or -1.
@@ -807,15 +918,11 @@ trait Iterator[+A] extends TraversableOnce[A] {
807918
*/
808919
def indexWhere(p: A => Boolean): Int = {
809920
var i = 0
810-
var found = false
811-
while (!found && hasNext) {
812-
if (p(next())) {
813-
found = true
814-
} else {
815-
i += 1
816-
}
921+
while (hasNext) {
922+
if (p(next())) return i
923+
i += 1
817924
}
818-
if (found) i else -1
925+
-1
819926
}
820927

821928
/** Returns the index of the first occurrence of the specified
@@ -829,15 +936,11 @@ trait Iterator[+A] extends TraversableOnce[A] {
829936
*/
830937
def indexOf[B >: A](elem: B): Int = {
831938
var i = 0
832-
var found = false
833-
while (!found && hasNext) {
834-
if (next() == elem) {
835-
found = true
836-
} else {
837-
i += 1
838-
}
939+
while (hasNext) {
940+
if (next() == elem) return i
941+
i += 1
839942
}
840-
if (found) i else -1
943+
-1
841944
}
842945

843946
/** Creates a buffered iterator from this iterator.

0 commit comments

Comments
 (0)