Scala execution times in Eclipse

2019-06-28 07:40发布

There's something fishy going on when I run Scala programs from Eclipse. I run an App object and it takes 7.8 s to run (actual execution time timed with System.nanoTime in the object). When I run the same .class file from the command line it takes 2.5 s.

I notice above the Console window it says

<terminated> Run(1)[Scala Application] C:\Program Files\Java\jre6\bin\javaw.exe

I wonder if this has any bearing on the times. Also, here are my eclipse.ini settings, which I set according to the recommendations in the Scala IDE pages:

-vm
C:\Program Files\Java\jre6\bin
-startup
plugins/org.eclipse.equinox.launcher_1.1.0.v20100507.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.1.R36x_v20100810
-product
org.eclipse.epp.package.java.product
--launcher.defaultAction
openFile
--launcher.XXMaxPermSize
256M
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
256m
--launcher.defaultAction
openFile
-vmargs
-Dosgi.requiredJavaVersion=1.5
-Xms256m
-Xmx2048m
-XX:PermSize=64m
-Xss1M
-server
-XX:+DoEscapeAnalysis
-XX:+UseConcMarkSweepGC
-XX:+UseCompressedOops

I am 99% sure this same code was taking 0.7 s to run in Eclipse a couple of days ago. I have tried pasting the code into a new project and running from there, but it doesn't change the running time. The project uses the global Scala Compiler settings and those are all set to default.

UPDATE : I have just tried swapping in the 2.9.0.1 Scala Library so that it's using the exact same version as the command line, and it made no difference to run time.

I also tried running the file from the command line with all the -vmargs options above, and it makes no difference.

Any ideas what is going on, or any settings I need to check? Thanks.


UPDATE 2 : I've half-answered the question myself now - I was running the code in different ways; but here's the problem:

Version 1, extending App :

object P005_V2 extends App { 
  def isDivis(x:Int, i:Int):Boolean = {
    if(i > 20) true
    else if(x % i != 0) false
    else isDivis(x, i+1)
  }
  def find(n:Int):Int = if (isDivis(n, 2)) n else find (n+2)

  val t = System.nanoTime;
  println (find (2))
  println(System.nanoTime - t)
}

Takes 0.7 s in Eclipse, 2.3 s from command prompt

Version 2, instantiating from another object

object P005_V2 { 
  def isDivis(x:Int, i:Int):Boolean = {
    if(i > 20) true
    else if(x % i != 0) false
    else isDivis(x, i+1)
  }
  def find(n:Int):Int = if (isDivis(n, 2)) n else find (n+2)

  val t = System.nanoTime;
  println (find (2))
  println(System.nanoTime - t)
}

object Run extends App {
  P005_V2
}

Takes 7.6 s in Eclipse, 2.5 s from command line

So, I understand that App was designed so that you can do things in the constructor and they will be optimized, unlike the deprecated Application. What seems to be happening here is that because I'm calling println(find(2)) from the constructor, the version which doesn't extend Appwill not be optimized and will take longer.

But the question remains, why the huge speed differences between executing via Eclipse and the command line?

Indeed both the command line versions are running slow, because a Java version at the command line takes 0.7 s and the Scala version should be as fast (as it can be, as demonstrated by version 1 running in Eclipse).

1条回答
beautiful°
2楼-- · 2019-06-28 08:28

After some experimentation I have 95% of the answer so I will give it myself:

I am running Windows 7 64-bit.

The command line is using the 32-bit JDK environment, as specified by JAVA_HOME variable.

Eclipse is using the 64-bit JRE environment, specified via Window | Preferences | Java | Installed JREs and the project's System Library.

Changing each of these to use the other version produces similar running times.

java from the command line will use the 64-bit JVM when available, regardless of what is specified in JAVA_HOME, unlike scala, which can lead to confusion in Scala/Java comparisons.

The difference in running times between Versions 1 and 2 above is because, as noted, Version 2 is running the costly calculation in the object's constructor (rather than a delayedInt method when App is used in Version 1). Version 2 is very wrong, since you shouldn't do costly calculations and I/O in a constructor. If you call the find method directly from the Run object then the speed anomaly goes away.

Conclusion:

Run times will depend on whether you are running the 32-bit or 64-bit JVM, and this is specified within Eclipse regardless of your environment settings. If using the 64-bit version you need to be especially careful not to do heavy processing within a constructor.

Running times (seconds):

                   Java                          Scala                         Scala - within constructor
                   JRE 64-bit     JDK 32-bit     JRE 64-bit     JDK 32-bit     JRE 64-bit     JDK 32-bit
Windows 7 64-bit   0.7            2.4            0.7            2.5            7.6            2.5
Windows XP 32-bit  n/a            13.4           n/a            14             n/a            13.1
(slower machine)
查看更多
登录 后发表回答