- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Super slow for-comprehension when emulating basic for-loop (about 200k iterations/second).
Fri, 2010-08-27, 13:57
The following simple code should execute in under a second. However it takes about 1 minute!
If everything is moved into the main method, then the program terminates in under a second, as expected.
object Main {
var sum = 0
for(i <- 0 until 10000000) {
if (i % 100000 == 0) println(i)
sum += i
}
def main(args: Array[String]) {
println("Finished: " + sum + ".")
}
}
This may cause unexpected and very serious performance issues in Scala applications.
If everything is moved into the main method, then the program terminates in under a second, as expected.
object Main {
var sum = 0
for(i <- 0 until 10000000) {
if (i % 100000 == 0) println(i)
sum += i
}
def main(args: Array[String]) {
println("Finished: " + sum + ".")
}
}
This may cause unexpected and very serious performance issues in Scala applications.
Fri, 2010-08-27, 14:07
#2
Re: Super slow for-comprehension when emulating basic for-loop
The issue is already known and written about in many blogs (see Avoid the Application trait).
Also, what JVM are you using? I've seen JIT logs on my static initializers at times, but this is rare. It's best to avoid any kind of expensive startup operation in a top-level object's constructor block as it gets placed in a static initializer. It's best to follow the same rules there as you had for java static {} blocks (i.e. AVOID!).
- Josh
On Fri, Aug 27, 2010 at 8:57 AM, Lex <lexn82@gmail.com> wrote:
Also, what JVM are you using? I've seen JIT logs on my static initializers at times, but this is rare. It's best to avoid any kind of expensive startup operation in a top-level object's constructor block as it gets placed in a static initializer. It's best to follow the same rules there as you had for java static {} blocks (i.e. AVOID!).
- Josh
On Fri, Aug 27, 2010 at 8:57 AM, Lex <lexn82@gmail.com> wrote:
The following simple code should execute in under a second. However it takes about 1 minute!
If everything is moved into the main method, then the program terminates in under a second, as expected.
object Main {
var sum = 0
for(i <- 0 until 10000000) {
if (i % 100000 == 0) println(i)
sum += i
}
def main(args: Array[String]) {
println("Finished: " + sum + ".")
}
}
This may cause unexpected and very serious performance issues in Scala applications.
Fri, 2010-08-27, 14:17
#3
Re: Super slow for-comprehension when emulating basic for-loop
My guess: It's executed as a static initializer block i.e. during loading the class = epic slow.
On Fri, Aug 27, 2010 at 2:57 PM, Lex <lexn82@gmail.com> wrote:
--
Viktor Klang,
Code Connoisseur
Work: www.akkasource.com
Code: github.com/viktorklang
Follow: twitter.com/viktorklang
Read: klangism.tumblr.com
On Fri, Aug 27, 2010 at 2:57 PM, Lex <lexn82@gmail.com> wrote:
The following simple code should execute in under a second. However it takes about 1 minute!
If everything is moved into the main method, then the program terminates in under a second, as expected.
object Main {
var sum = 0
for(i <- 0 until 10000000) {
if (i % 100000 == 0) println(i)
sum += i
}
def main(args: Array[String]) {
println("Finished: " + sum + ".")
}
}
This may cause unexpected and very serious performance issues in Scala applications.
--
Viktor Klang,
Code Connoisseur
Work: www.akkasource.com
Code: github.com/viktorklang
Follow: twitter.com/viktorklang
Read: klangism.tumblr.com
Fri, 2010-08-27, 14:17
#4
Re: Super slow for-comprehension when emulating basic for-loop
On Fri, Aug 27, 2010 at 2:01 PM, Ismael Juma wrote:
> Yes, this is a known issue with the JVM. Code that is executed as part
> of the class initialisation does not get the usual optimisations.
Also see:
http://permalink.gmane.org/gmane.comp.java.openjdk.hotspot.compiler.deve...
Ismael
Fri, 2010-08-27, 14:37
#5
Re: Super slow for-comprehension when emulating basic for-loop
I'm using the latest Sun Java (version 1.6 update 21). The issue can be replicated always.
This is a very simple code, and the performance is not just slow, its unusable. 2x penalty would be ok, 10-20x would be acceptable, in this case the performance is 300 times slower compared to the for-loop in the main method.
I understand that static initializes are slow, but the bottom line is that this behavior of the basic for-loop is not acceptable, especially for a language that can be used for scripting.
A user of the language should not have to worry about things like java static initializers to write such basic code.
On Fri, Aug 27, 2010 at 9:03 AM, Ismael Juma <mlists@juma.me.uk> wrote:
This is a very simple code, and the performance is not just slow, its unusable. 2x penalty would be ok, 10-20x would be acceptable, in this case the performance is 300 times slower compared to the for-loop in the main method.
I understand that static initializes are slow, but the bottom line is that this behavior of the basic for-loop is not acceptable, especially for a language that can be used for scripting.
A user of the language should not have to worry about things like java static initializers to write such basic code.
On Fri, Aug 27, 2010 at 9:03 AM, Ismael Juma <mlists@juma.me.uk> wrote:
On Fri, Aug 27, 2010 at 2:01 PM, Ismael Juma <mlists@juma.me.uk> wrote:
> Yes, this is a known issue with the JVM. Code that is executed as part
> of the class initialisation does not get the usual optimisations.
Also see:
http://permalink.gmane.org/gmane.comp.java.openjdk.hotspot.compiler.devel/3553
Ismael
Fri, 2010-08-27, 14:47
#6
Re: Super slow for-comprehension when emulating basic for-loop
On Fri, Aug 27, 2010 at 6:35 AM, Lex <lexn82@gmail.com> wrote:
I'm using the latest Sun Java (version 1.6 update 21). The issue can be replicated always.
This is a very simple code, and the performance is not just slow, its unusable. 2x penalty would be ok, 10-20x would be acceptable, in this case the performance is 300 times slower compared to the for-loop in the main method.
I understand that static initializes are slow, but the bottom line is that this behavior of the basic for-loop is not acceptable, especially for a language that can be used for scripting.
A user of the language should not have to worry about things like java static initializers to write such basic code.
Perhaps you should be taking this issue to Oracle as they maintain the JVM. There's nothing that the Scala team can do to change the execution characteristics of the JVM.
On Fri, Aug 27, 2010 at 9:03 AM, Ismael Juma <mlists@juma.me.uk> wrote:
On Fri, Aug 27, 2010 at 2:01 PM, Ismael Juma <mlists@juma.me.uk> wrote:
> Yes, this is a known issue with the JVM. Code that is executed as part
> of the class initialisation does not get the usual optimisations.
Also see:
http://permalink.gmane.org/gmane.comp.java.openjdk.hotspot.compiler.devel/3553
Ismael
--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Blog: http://goodstuff.im
Surf the harmonics
Fri, 2010-08-27, 14:57
#7
Re: Super slow for-comprehension when emulating basic for-loop
While loops are affected by this problem but not nearly to the same extent as for loops. While loops are about 20 times slower in static initializer block, compare that to 300 times slower with for-loops. That is a different of a program performing poorly and a program hanging with no response at all.
Java for loops are compiled as while loops, so Java does not have this problem to the same extent. So JVM is not at fault here.
On Fri, Aug 27, 2010 at 9:44 AM, David Pollak <feeder.of.the.bears@gmail.com> wrote:
Java for loops are compiled as while loops, so Java does not have this problem to the same extent. So JVM is not at fault here.
On Fri, Aug 27, 2010 at 9:44 AM, David Pollak <feeder.of.the.bears@gmail.com> wrote:
On Fri, Aug 27, 2010 at 6:35 AM, Lex <lexn82@gmail.com> wrote:
I'm using the latest Sun Java (version 1.6 update 21). The issue can be replicated always.
This is a very simple code, and the performance is not just slow, its unusable. 2x penalty would be ok, 10-20x would be acceptable, in this case the performance is 300 times slower compared to the for-loop in the main method.
I understand that static initializes are slow, but the bottom line is that this behavior of the basic for-loop is not acceptable, especially for a language that can be used for scripting.
A user of the language should not have to worry about things like java static initializers to write such basic code.
Perhaps you should be taking this issue to Oracle as they maintain the JVM. There's nothing that the Scala team can do to change the execution characteristics of the JVM.
On Fri, Aug 27, 2010 at 9:03 AM, Ismael Juma <mlists@juma.me.uk> wrote:
On Fri, Aug 27, 2010 at 2:01 PM, Ismael Juma <mlists@juma.me.uk> wrote:
> Yes, this is a known issue with the JVM. Code that is executed as part
> of the class initialisation does not get the usual optimisations.
Also see:
http://permalink.gmane.org/gmane.comp.java.openjdk.hotspot.compiler.devel/3553
Ismael
--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Blog: http://goodstuff.im
Surf the harmonics
Fri, 2010-08-27, 15:07
#8
Re: Super slow for-comprehension when emulating basic for-loop
What? Java != JVM here. You can accomplish the same faults in java by writting FPish code. Wait until JDK 7 and closures and we'll see what the differences is.
In general, if you're doing a lot of initialization in static blocks, it's considered a Bad Thing(TM) so you should try to avoid it. Just like you do in Java. I don't care if you're writing a scripting application, you should avoid this, and you can do so rather easily (usually).
In C, it's usually best practice to try to line up structures with memory blocks for optimal performance. On the JVM it is best to avoid anything non-trivial in static initializer blocks. Know your platform.
- Josh
On Fri, Aug 27, 2010 at 9:56 AM, Lex <lexn82@gmail.com> wrote:
In general, if you're doing a lot of initialization in static blocks, it's considered a Bad Thing(TM) so you should try to avoid it. Just like you do in Java. I don't care if you're writing a scripting application, you should avoid this, and you can do so rather easily (usually).
In C, it's usually best practice to try to line up structures with memory blocks for optimal performance. On the JVM it is best to avoid anything non-trivial in static initializer blocks. Know your platform.
- Josh
On Fri, Aug 27, 2010 at 9:56 AM, Lex <lexn82@gmail.com> wrote:
While loops are affected by this problem but not nearly to the same extent as for loops. While loops are about 20 times slower in static initializer block, compare that to 300 times slower with for-loops. That is a different of a program performing poorly and a program hanging with no response at all.
Java for loops are compiled as while loops, so Java does not have this problem to the same extent. So JVM is not at fault here.
On Fri, Aug 27, 2010 at 9:44 AM, David Pollak <feeder.of.the.bears@gmail.com> wrote:
On Fri, Aug 27, 2010 at 6:35 AM, Lex <lexn82@gmail.com> wrote:
I'm using the latest Sun Java (version 1.6 update 21). The issue can be replicated always.
This is a very simple code, and the performance is not just slow, its unusable. 2x penalty would be ok, 10-20x would be acceptable, in this case the performance is 300 times slower compared to the for-loop in the main method.
I understand that static initializes are slow, but the bottom line is that this behavior of the basic for-loop is not acceptable, especially for a language that can be used for scripting.
A user of the language should not have to worry about things like java static initializers to write such basic code.
Perhaps you should be taking this issue to Oracle as they maintain the JVM. There's nothing that the Scala team can do to change the execution characteristics of the JVM.
On Fri, Aug 27, 2010 at 9:03 AM, Ismael Juma <mlists@juma.me.uk> wrote:
On Fri, Aug 27, 2010 at 2:01 PM, Ismael Juma <mlists@juma.me.uk> wrote:
> Yes, this is a known issue with the JVM. Code that is executed as part
> of the class initialisation does not get the usual optimisations.
Also see:
http://permalink.gmane.org/gmane.comp.java.openjdk.hotspot.compiler.devel/3553
Ismael
--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Blog: http://goodstuff.im
Surf the harmonics
Fri, 2010-08-27, 15:17
#9
Re: Super slow for-comprehension when emulating basic for-loop
On Friday August 27 2010, Lex wrote:
> While loops are affected by this problem but not nearly to the same
> extent as for loops. While loops are about 20 times slower in static
> initializer block, compare that to 300 times slower with for-loops.
> That is a different of a program performing poorly and a program
> hanging with no response at all.
> Java for loops are compiled as while loops, so Java does not have
> this problem to the same extent. So JVM is not at fault here.
Exactly what outcome are you pursuing?
Randall Schulz
Fri, 2010-08-27, 15:17
#10
Re: Super slow for-comprehension when emulating basic for-loop
On Fri, Aug 27, 2010 at 10:01 AM, Randall R Schulz <rschulz@sonic.net> wrote:
Independent of the static initializer stuff, it would be nice if for loops were rewritten as while loops in special cases where that was possible.
for (x <- indexedSeqCollection) { stuff }
becomes
{
val range = indexedSeqCollection.indices
var i = range.start
while (i < collectionWithWhileable.end) {
x = collectionWithWhileable(i)
stuff
i += range.step
}
}
instead of
collectionWithWhileable.foreach(x => stuff)
(Okay, I left off the appropriate handling of the case where step is negative, and you probably want to special-case +- 1 also, but you get the idea.)
--Rex
Exactly what outcome are you pursuing?
Independent of the static initializer stuff, it would be nice if for loops were rewritten as while loops in special cases where that was possible.
for (x <- indexedSeqCollection) { stuff }
becomes
{
val range = indexedSeqCollection.indices
var i = range.start
while (i < collectionWithWhileable.end) {
x = collectionWithWhileable(i)
stuff
i += range.step
}
}
instead of
collectionWithWhileable.foreach(x => stuff)
(Okay, I left off the appropriate handling of the case where step is negative, and you probably want to special-case +- 1 also, but you get the idea.)
--Rex
Fri, 2010-08-27, 15:17
#11
Re: Super slow for-comprehension when emulating basic for-loop
Am 27.08.2010 16:01, schrieb Randall R Schulz:
> On Friday August 27 2010, Lex wrote:
>> While loops are affected by this problem but not nearly to the same
>> extent as for loops. While loops are about 20 times slower in static
>> initializer block, compare that to 300 times slower with for-loops.
>> That is a different of a program performing poorly and a program
>> hanging with no response at all.
>> Java for loops are compiled as while loops, so Java does not have
>> this problem to the same extent. So JVM is not at fault here.
> Exactly what outcome are you pursuing?
>
>
> Randall Schulz
>
someone could make a compiler plugin that compiles stuff differently to
work around the problem ;)
Fri, 2010-08-27, 15:27
#12
Re: Super slow for-comprehension when emulating basic for-loop
On Fri, Aug 27, 2010 at 2:56 PM, Lex wrote:
> So JVM is not at fault here.
It helps to understand what is going on before making statements like
this. We said that the reason that the code runs much slower is the
lack of optimisation during static initialisation. Of course, this
means that low-level code suffers less from this issue than high-level
code. The latter does more that benefits from said JVM optimisations
that are not being done.
Best,
Ismael
Fri, 2010-08-27, 15:37
#13
Re: Super slow for-comprehension when emulating basic for-loop
I knew the reason before I posted this. But that is not the point. The point is having simple code failing so badly. The point is having for loops (the most basic construct in a language) that behave not just bad, but unexpectedly bad. The point is having to write while loops in the language that is supposed to reduce LOC count.
I understand that for-comprehensions cannot be optimized to while loops in general. However the cases of for(i <- a until b), for(item <- collection), are both most common and ubiquitous. The deserve to be optimized to proper while loops.
Simple things should be simple... Given how ubiquitous for loops are i do not understand why the simple forms cannot be compiled as efficient while loops. for(i <- a until b), for(item <- collection), are the most common cases.
On Fri, Aug 27, 2010 at 10:07 AM, Ismael Juma <mlists@juma.me.uk> wrote:
I understand that for-comprehensions cannot be optimized to while loops in general. However the cases of for(i <- a until b), for(item <- collection), are both most common and ubiquitous. The deserve to be optimized to proper while loops.
Simple things should be simple... Given how ubiquitous for loops are i do not understand why the simple forms cannot be compiled as efficient while loops. for(i <- a until b), for(item <- collection), are the most common cases.
On Fri, Aug 27, 2010 at 10:07 AM, Ismael Juma <mlists@juma.me.uk> wrote:
On Fri, Aug 27, 2010 at 2:56 PM, Lex <lexn82@gmail.com> wrote:
> So JVM is not at fault here.
It helps to understand what is going on before making statements like
this. We said that the reason that the code runs much slower is the
lack of optimisation during static initialisation. Of course, this
means that low-level code suffers less from this issue than high-level
code. The latter does more that benefits from said JVM optimisations
that are not being done.
Best,
Ismael
Fri, 2010-08-27, 15:37
#14
Re: Super slow for-comprehension when emulating basic for-loop
On Fri, Aug 27, 2010 at 9:13 AM, Rex Kerr <ichoran@gmail.com> wrote:
Don't be so sure as to which is faster. Last time I benchmarked something similar, the for comprehension was faster than the while loop I was trying to replace it with.
On Fri, Aug 27, 2010 at 10:01 AM, Randall R Schulz <rschulz@sonic.net> wrote:Exactly what outcome are you pursuing?
Independent of the static initializer stuff, it would be nice if for loops were rewritten as while loops in special cases where that was possible.
for (x <- indexedSeqCollection) { stuff }
becomes
{
val range = indexedSeqCollection.indices
var i = range.start
while (i < collectionWithWhileable.end) {
x = collectionWithWhileable(i)
stuff
i += range.step
}
}
instead of
collectionWithWhileable.foreach(x => stuff)
Don't be so sure as to which is faster. Last time I benchmarked something similar, the for comprehension was faster than the while loop I was trying to replace it with.
Fri, 2010-08-27, 15:47
#15
Re: Super slow for-comprehension when emulating basic for-loop
Rex Kerr wrote:
> Independent of the static initializer stuff, it would be nice if for
> loops were rewritten as while loops in special cases where that was
> possible.
I think this comes up every few weeks on the scala mailing lists. There
are pros and cons and it seems the compiler people think the cons weigh
more.
Fri, 2010-08-27, 15:57
#16
Re: Super slow for-comprehension when emulating basic for-loop
In the normal case, where the JVM can inline and optimise with JIT, you will see well performing code. When you design your code such that you leverage a JVM issue for a huge slowdown, well... you'll see the slowdown.
Remember that Scala, and Java, is not a startup-performant language. It does best once you give it time to optimise. Hopefully the future will see "stored" JIT profiles to remove some of this warm-up time, but I don't think it's reasonable to expect a static initialiser to behave well in Scala or Java.
The simple solution of deferring your initialization until the program is running is not only better for the design of your program (singletons are iffy creatures), it will reduce the running time of the code.
God forbid you had some kind of locking in this object. Then you'd run into another JVM issue relating to deadlocks and static initializers, and of course Scala would be at fault.
- Josh
On Fri, Aug 27, 2010 at 10:15 AM, Lex <lexn82@gmail.com> wrote:
Remember that Scala, and Java, is not a startup-performant language. It does best once you give it time to optimise. Hopefully the future will see "stored" JIT profiles to remove some of this warm-up time, but I don't think it's reasonable to expect a static initialiser to behave well in Scala or Java.
The simple solution of deferring your initialization until the program is running is not only better for the design of your program (singletons are iffy creatures), it will reduce the running time of the code.
God forbid you had some kind of locking in this object. Then you'd run into another JVM issue relating to deadlocks and static initializers, and of course Scala would be at fault.
- Josh
On Fri, Aug 27, 2010 at 10:15 AM, Lex <lexn82@gmail.com> wrote:
I knew the reason before I posted this. But that is not the point. The point is having simple code failing so badly. The point is having for loops (the most basic construct in a language) that behave not just bad, but unexpectedly bad. The point is having to write while loops in the language that is supposed to reduce LOC count.
I understand that for-comprehensions cannot be optimized to while loops in general. However the cases of for(i <- a until b), for(item <- collection), are both most common and ubiquitous. The deserve to be optimized to proper while loops.
Simple things should be simple... Given how ubiquitous for loops are i do not understand why the simple forms cannot be compiled as efficient while loops. for(i <- a until b), for(item <- collection), are the most common cases.
On Fri, Aug 27, 2010 at 10:07 AM, Ismael Juma <mlists@juma.me.uk> wrote:
On Fri, Aug 27, 2010 at 2:56 PM, Lex <lexn82@gmail.com> wrote:
> So JVM is not at fault here.
It helps to understand what is going on before making statements like
this. We said that the reason that the code runs much slower is the
lack of optimisation during static initialisation. Of course, this
means that low-level code suffers less from this issue than high-level
code. The latter does more that benefits from said JVM optimisations
that are not being done.
Best,
Ismael
Fri, 2010-08-27, 15:57
#17
Re: Super slow for-comprehension when emulating basic for-loop
On Friday August 27 2010, Lex wrote:
> But that is not the point.
> The point is having simple code failing so badly.
Optimizing simple cases is pointless and foolish.
The effort has to go to optimizing common cases.
And nothing is actually failing in your artificial snippet.
> ...
Randall Schulz
Fri, 2010-08-27, 16:07
#18
Re: Super slow for-comprehension when emulating basic for-loop
On Fri, Aug 27, 2010 at 9:30 AM, Nils Kilden-Pedersen <nilskp@gmail.com> wrote:
I just tried this again and, at least on an IndexedSeq, the while loop is infinitely slower, probably due to the apply(i) call being expensive.
On Fri, Aug 27, 2010 at 9:13 AM, Rex Kerr <ichoran@gmail.com> wrote:On Fri, Aug 27, 2010 at 10:01 AM, Randall R Schulz <rschulz@sonic.net> wrote:Exactly what outcome are you pursuing?
Independent of the static initializer stuff, it would be nice if for loops were rewritten as while loops in special cases where that was possible.
for (x <- indexedSeqCollection) { stuff }
becomes
{
val range = indexedSeqCollection.indices
var i = range.start
while (i < collectionWithWhileable.end) {
x = collectionWithWhileable(i)
stuff
i += range.step
}
}
instead of
collectionWithWhileable.foreach(x => stuff)
Don't be so sure as to which is faster. Last time I benchmarked something similar, the for comprehension was faster than the while loop I was trying to replace it with.
I just tried this again and, at least on an IndexedSeq, the while loop is infinitely slower, probably due to the apply(i) call being expensive.
Fri, 2010-08-27, 16:07
#19
Re: Super slow for-comprehension when emulating basic for-loop
In the normal case, where the JVM can inline and optimise with JIT, you will see well performing code. When you design your code such that you leverage a JVM issue for a huge slowdown, well... you'll see the slowdown.The problem I have with this particular case is that it violates the "Principle of least astonishment". Having slow static intializers and slow for loops is one thing. But the combination of two is just a killer. Worse still, the basic for loops is what most people would use for one-off array initialization.
On Fri, Aug 27, 2010 at 10:32 AM, Josh Suereth <joshua.suereth@gmail.com> wrote:
In the normal case, where the JVM can inline and optimise with JIT, you will see well performing code. When you design your code such that you leverage a JVM issue for a huge slowdown, well... you'll see the slowdown.
Remember that Scala, and Java, is not a startup-performant language. It does best once you give it time to optimise. Hopefully the future will see "stored" JIT profiles to remove some of this warm-up time, but I don't think it's reasonable to expect a static initialiser to behave well in Scala or Java.
The simple solution of deferring your initialization until the program is running is not only better for the design of your program (singletons are iffy creatures), it will reduce the running time of the code.
God forbid you had some kind of locking in this object. Then you'd run into another JVM issue relating to deadlocks and static initializers, and of course Scala would be at fault.
- Josh
On Fri, Aug 27, 2010 at 10:15 AM, Lex <lexn82@gmail.com> wrote:
I knew the reason before I posted this. But that is not the point. The point is having simple code failing so badly. The point is having for loops (the most basic construct in a language) that behave not just bad, but unexpectedly bad. The point is having to write while loops in the language that is supposed to reduce LOC count.
I understand that for-comprehensions cannot be optimized to while loops in general. However the cases of for(i <- a until b), for(item <- collection), are both most common and ubiquitous. The deserve to be optimized to proper while loops.
Simple things should be simple... Given how ubiquitous for loops are i do not understand why the simple forms cannot be compiled as efficient while loops. for(i <- a until b), for(item <- collection), are the most common cases.
On Fri, Aug 27, 2010 at 10:07 AM, Ismael Juma <mlists@juma.me.uk> wrote:
On Fri, Aug 27, 2010 at 2:56 PM, Lex <lexn82@gmail.com> wrote:
> So JVM is not at fault here.
It helps to understand what is going on before making statements like
this. We said that the reason that the code runs much slower is the
lack of optimisation during static initialisation. Of course, this
means that low-level code suffers less from this issue than high-level
code. The latter does more that benefits from said JVM optimisations
that are not being done.
Best,
Ismael
Fri, 2010-08-27, 16:17
#20
Re: Super slow for-comprehension when emulating basic for-loop
On Friday August 27 2010, Nils Kilden-Pedersen wrote:
> ...
> >
> > Don't be so sure as to which is faster. Last time I benchmarked
> > something similar, the for comprehension was faster than the while
> > loop I was trying to replace it with.
>
> I just tried this again and, at least on an IndexedSeq, the while
> loop is infinitely slower, probably due to the apply(i) call being
> expensive.
If it's infinitely slower, you should not yet have the results...
RRS
Fri, 2010-08-27, 16:27
#21
Re: Super slow for-comprehension when emulating basic for-loop
On Fri, Aug 27, 2010 at 9:49 AM, Randall R Schulz <rschulz@sonic.net> wrote:
I kid you not, it's still running.
On Friday August 27 2010, Nils Kilden-Pedersen wrote:
> ...
> >
> > Don't be so sure as to which is faster. Last time I benchmarked
> > something similar, the for comprehension was faster than the while
> > loop I was trying to replace it with.
>
> I just tried this again and, at least on an IndexedSeq, the while
> loop is infinitely slower, probably due to the apply(i) call being
> expensive.
If it's infinitely slower, you should not yet have the results...
I kid you not, it's still running.
RRS
Fri, 2010-08-27, 16:37
#22
Re: Super slow for-comprehension when emulating basic for-loop
On Fri, Aug 27, 2010 at 10:47 AM, Nils Kilden-Pedersen <nilskp@gmail.com> wrote:
I'd forgotten how nonperformant the indexing of some IndexedSeqs was. With Vector[Int], it's about 2.8x slower to use something like ".sum" instead of using the while loop to compute it. It's 30% slower to use for than while.
With ArrayBuffer[Int], both sum and for are 6-7x slower than while.
With Array[Int], for is 16x slower and sum is 28x slower than while.
With Range, I find that for is 4x slower (unlike Nils--I'm uncertain of the difference; maybe his code doesn't have time to get inlined?).
--Rex
On Fri, Aug 27, 2010 at 9:30 AM, Nils Kilden-Pedersen <nilskp@gmail.com> wrote:
On Fri, Aug 27, 2010 at 9:13 AM, Rex Kerr <ichoran@gmail.com> wrote:On Fri, Aug 27, 2010 at 10:01 AM, Randall R Schulz <rschulz@sonic.net> wrote:Exactly what outcome are you pursuing?
Independent of the static initializer stuff, it would be nice if for loops were rewritten as while loops in special cases where that was possible.
for (x <- indexedSeqCollection) { stuff }
becomes
{
val range = indexedSeqCollection.indices
var i = range.start
while (i < collectionWithWhileable.end) {
x = collectionWithWhileable(i)
stuff
i += range.step
}
}
instead of
collectionWithWhileable.foreach(x => stuff)
Don't be so sure as to which is faster. Last time I benchmarked something similar, the for comprehension was faster than the while loop I was trying to replace it with.
I just tried this again and, at least on an IndexedSeq, the while loop is infinitely slower, probably due to the apply(i) call being expensive.
I'd forgotten how nonperformant the indexing of some IndexedSeqs was. With Vector[Int], it's about 2.8x slower to use something like ".sum" instead of using the while loop to compute it. It's 30% slower to use for than while.
With ArrayBuffer[Int], both sum and for are 6-7x slower than while.
With Array[Int], for is 16x slower and sum is 28x slower than while.
With Range, I find that for is 4x slower (unlike Nils--I'm uncertain of the difference; maybe his code doesn't have time to get inlined?).
--Rex
Fri, 2010-08-27, 16:47
#23
Re: Super slow for-comprehension when emulating basic for-loop
On Friday August 27 2010, Lex wrote:
> > In the normal case, where the JVM can inline and optimise with JIT,
> > you will see well performing code. When you design your code such
> > that you leverage a JVM issue for a huge slowdown, well... you'll
> > see the slowdown.
>
> The problem I have with this particular case is that it violates the
> "Principle of least astonishment". ...
>
> ...
Violation of expectations is highly subjective.
For example, I expect users of email forums to trim the quoted material
in their replies. That expectation is frequently violated.
Randall Schulz
Fri, 2010-08-27, 16:57
#24
Re: Super slow for-comprehension when emulating basic for-loop
I somewhat agree with your sentiments, just not your conclusions in this case. I do not think it is common in production code to embed lots of for-expresisons and initialization in static initializers. One could consider that an anti-pattern. I'm not sure Scala should work around JVM issues here because A) We'd like the JVM to fix itself and B) users should avoid static-initializers for other reasons.
Now, in terms of optimising for-expressions where possible. I am all for this with integer range loops and the like. If that is all you desire out of this, then we agree.
If you are arguing that objects should not use static initializers for their construction, I'd ask you to think through the problem a bit more.
On Fri, Aug 27, 2010 at 11:04 AM, Lex <lexn82@gmail.com> wrote:
Now, in terms of optimising for-expressions where possible. I am all for this with integer range loops and the like. If that is all you desire out of this, then we agree.
If you are arguing that objects should not use static initializers for their construction, I'd ask you to think through the problem a bit more.
On Fri, Aug 27, 2010 at 11:04 AM, Lex <lexn82@gmail.com> wrote:
In the normal case, where the JVM can inline and optimise with JIT, you will see well performing code. When you design your code such that you leverage a JVM issue for a huge slowdown, well... you'll see the slowdown.The problem I have with this particular case is that it violates the "Principle of least astonishment". Having slow static intializers and slow for loops is one thing. But the combination of two is just a killer. Worse still, the basic for loops is what most people would use for one-off array initialization.
On Fri, Aug 27, 2010 at 10:32 AM, Josh Suereth <joshua.suereth@gmail.com> wrote:In the normal case, where the JVM can inline and optimise with JIT, you will see well performing code. When you design your code such that you leverage a JVM issue for a huge slowdown, well... you'll see the slowdown.
Remember that Scala, and Java, is not a startup-performant language. It does best once you give it time to optimise. Hopefully the future will see "stored" JIT profiles to remove some of this warm-up time, but I don't think it's reasonable to expect a static initialiser to behave well in Scala or Java.
The simple solution of deferring your initialization until the program is running is not only better for the design of your program (singletons are iffy creatures), it will reduce the running time of the code.
God forbid you had some kind of locking in this object. Then you'd run into another JVM issue relating to deadlocks and static initializers, and of course Scala would be at fault.
- Josh
On Fri, Aug 27, 2010 at 10:15 AM, Lex <lexn82@gmail.com> wrote:
I knew the reason before I posted this. But that is not the point. The point is having simple code failing so badly. The point is having for loops (the most basic construct in a language) that behave not just bad, but unexpectedly bad. The point is having to write while loops in the language that is supposed to reduce LOC count.
I understand that for-comprehensions cannot be optimized to while loops in general. However the cases of for(i <- a until b), for(item <- collection), are both most common and ubiquitous. The deserve to be optimized to proper while loops.
Simple things should be simple... Given how ubiquitous for loops are i do not understand why the simple forms cannot be compiled as efficient while loops. for(i <- a until b), for(item <- collection), are the most common cases.
On Fri, Aug 27, 2010 at 10:07 AM, Ismael Juma <mlists@juma.me.uk> wrote:
On Fri, Aug 27, 2010 at 2:56 PM, Lex <lexn82@gmail.com> wrote:
> So JVM is not at fault here.
It helps to understand what is going on before making statements like
this. We said that the reason that the code runs much slower is the
lack of optimisation during static initialisation. Of course, this
means that low-level code suffers less from this issue than high-level
code. The latter does more that benefits from said JVM optimisations
that are not being done.
Best,
Ismael
Fri, 2010-08-27, 17:07
#25
Re: Super slow for-comprehension when emulating basic for-loop
Now, in terms of optimising for-expressions where possible. I am all for this with integer range loops and the like. If that is all you desire out of this, then we agree.It is possible to use lazy vals to initialize singleton objects on demand. While that would remedy the worst case, it would also slow down the overall performance. So no, I am not suggesting to avoid static initializer for singleton construction. One can always use a package object with lazy vals when absolutely necessary.
If you are arguing that objects should not use static initializers for their construction, I'd ask you to think through the problem a bit more.
On Fri, Aug 27, 2010 at 1:57 PM, Lex wrote:
> The following simple code should execute in under a second. However it takes
> about 1 minute!
Yes, this is a known issue with the JVM. Code that is executed as part
of the class initialisation does not get the usual optimisations. See:
http://www.scala-blogs.org/2008/07/application-trait-considered-harmful....
Best,
Ismael