@@ -71,18 +71,6 @@ extends collection.AbstractSeq[Int]
71
71
72
72
def isInclusive = false
73
73
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
-
86
74
override def length : Int = numRangeElements
87
75
override lazy val last : Int =
88
76
if (length == 0 ) Nil .last
@@ -95,6 +83,83 @@ extends collection.AbstractSeq[Int]
95
83
if (idx < 0 || idx >= length) throw new IndexOutOfBoundsException (idx.toString)
96
84
locationAfterN(idx)
97
85
}
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
+ }
98
163
99
164
/** Creates a new range containing the first `n` elements of this range.
100
165
*
0 commit comments