@@ -71,18 +71,6 @@ extends collection.AbstractSeq[Int]
7171
7272 def isInclusive = false
7373
74- @ inline final override def foreach [@ specialized(Unit ) U ](f : Int => U ) {
75- if (length > 0 ) {
76- val last = this .last
77- var i = start
78- while (i != last) {
79- f(i)
80- i += step
81- }
82- f(i)
83- }
84- }
85-
8674 override def length : Int = numRangeElements
8775 override lazy val last : Int =
8876 if (length == 0 ) Nil .last
@@ -95,6 +83,83 @@ extends collection.AbstractSeq[Int]
9583 if (idx < 0 || idx >= length) throw new IndexOutOfBoundsException (idx.toString)
9684 locationAfterN(idx)
9785 }
86+
87+ /** @note Making foreach run as fast as a while loop is a challenge.
88+ * The key elements which I can observe making a difference are:
89+ *
90+ * - the inner loop should be as small as possible
91+ * - the inner loop should be monomorphic
92+ * - the inner loop should perform no boxing and no avoidable tests
93+ *
94+ * This is achieved by:
95+ *
96+ * - keeping initialization logic out of the inner loop
97+ * - dispatching to custom variations based on initial conditions
98+ * - tricking the compiler into always calling Function1#apply$mcVI$sp
99+ *
100+ * The last one is important and less than obvious. Even when foreach
101+ * was specialized on Unit, only Int => Unit arguments benefited from it.
102+ * Other function types would be accepted, but in the absence of full
103+ * specialization the integer argument was boxed on every call. For example:
104+ *
105+ class A {
106+ final def f(x: Int): Int = x + 1
107+ // Calls Range.foreach, which calls Function1.apply
108+ def g1 = 1 until 100 foreach { x => f(x) }
109+ // Calls Range.foreach$mVc$sp, which calls Function1.apply$mcVI$sp
110+ def g2 = 1 until 100 foreach { x => f(x) ; () }
111+ }
112+ *
113+ * However! Since the result of the closure is always discarded, we
114+ * simply cast it to Int => Unit, thereby executing the fast version.
115+ * The seemingly looming ClassCastException can never arrive.
116+ */
117+ @ inline final override def foreach [U ](f : Int => U ) {
118+ if (step < 0 ) {
119+ if (isInclusive) foreachDownIn(f.asInstanceOf [Int => Unit ])
120+ else foreachDownEx(f.asInstanceOf [Int => Unit ])
121+ }
122+ else {
123+ if (isInclusive) foreachUpIn(f.asInstanceOf [Int => Unit ])
124+ else foreachUpEx(f.asInstanceOf [Int => Unit ])
125+ }
126+ }
127+
128+ /** !!! These methods must be public or they will not be inlined.
129+ * But they are certainly not intended to be part of the API.
130+ * This collision between inlining requirements and access semantics
131+ * is highly unfortunate and must be resolved.
132+ *
133+ * Proposed band-aid: an @internal annotation.
134+ */
135+ @ inline final def foreachDownIn (f : Int => Unit ) {
136+ var i = start
137+ while (i >= end) {
138+ f(i)
139+ i += step
140+ }
141+ }
142+ @ inline final def foreachUpIn (f : Int => Unit ) {
143+ var i = start
144+ while (i <= end) {
145+ f(i)
146+ i += step
147+ }
148+ }
149+ @ inline final def foreachDownEx (f : Int => Unit ) {
150+ var i = start
151+ while (i > end) {
152+ f(i)
153+ i += step
154+ }
155+ }
156+ @ inline final def foreachUpEx (f : Int => Unit ) {
157+ var i = start
158+ while (i < end) {
159+ f(i)
160+ i += step
161+ }
162+ }
98163
99164 /** Creates a new range containing the first `n` elements of this range.
100165 *
0 commit comments