Numerik in Scala (Performance)

0x7F800000

Top Contributor
- Wrapper-Objekte werden nur dann generiert, wenn sie auch notwendig sind. Wenn ich z.B. ein [c]Int[/c] zu den Elementen eines [c]Array[Int][/c] addiere, wird nichts gewrappt.
- moderne JVMs optimieren Wrapper-Zugriffe mittlerweile weg
- implizite Umwandlungen werden schon zur Compilezeit aufgelöst, ein Aufruf einer [c]Numeric[/c]-Methode ist genauso schnell wie etwa der Aufruf einer [c]java.lang.Math[/c]-Methode
Echt? Ok, dann wäre ich endlos dankbar zu erfahren, was ich denn hier falsch mache:
Code:
object InfixWrapperTest{
  
  trait AdditiveMonoid[A]{
    def zero: A
    def add(x: A, y: A): A	
  
    class Ops(x: A){
	  def +(y: A) = add(x, y)
	}
  }
  
  implicit object DoubleIsAdditive extends AdditiveMonoid[Double]{
    def zero = 0d
	def add(x: Double, y: Double) = x + y
  }
  
  implicit def wrapToOps[T](x: T)(implicit monoid: AdditiveMonoid[T]): AdditiveMonoid[T]#Ops = new monoid.Ops(x)
  
  def measuringTime[A](f: => A) = {
    val t = System.currentTimeMillis
	val result = f
	(result, System.currentTimeMillis - t)
  }
  
  def sumGeneric[X](a: Array[X])(implicit monoid: AdditiveMonoid[X]) = {
    var result = monoid.zero
	var i = 0
	while (i < a.length){ 
	  result += a(i)
	  i += 1
	}
	result
  }
  
  def sumDirect(a: Array[Double]) = {
    var result = 0d
	var i = 0
	while (i < a.length){ 
	  result += a(i)
	  i += 1
	}
	result
  }
  
  def main(args: Array[String]){
    
	val arr = new Array[Double](1000000)
	for (i <- 0 until arr.size) arr(i) = math.sin(i*i + i +137)
	
	val warmup = 200
	val runs = 1000
	
	var genericTime = 0L
	var directTime = 0L
	
	println("visueller Check der Korrektheit der Resultate:")
	println(measuringTime{ sumDirect(arr) })
	println(measuringTime{ sumGeneric(arr) })
	
	println("Messung der Laufzeit")
	for (i <- 1 to (warmup + runs)){
	  val g = measuringTime{ sumGeneric(arr) }._2
	  val d = measuringTime{ sumDirect(arr) }._2
	  if( i > warmup){
	    genericTime += g
		directTime += d
	  }
	  println(i + "/" + (warmup + runs))
	}
	println("generisch: " + (genericTime.toDouble / runs) + "ms")
	println("direkt: " + (directTime.toDouble / runs) + "ms")
  }
}
Ergebnis:
Code:
generisch: 82.383ms
direkt: 5.298ms
Das ist alles unter scala 2.9.1 für jvm 1.6.irgendwas kompiliert, mit dem original scalac-compiler. Wo kommt der Faktor 16 her, wenn das alles so toll wegoptimiert wird? Wenn man fehlendes fma miteinrechnet, und das ganze mit C++ vergleicht, dann ist das Faktor 32. Wenn man das also so schreiben würde, dann bräuchte man für Scala einen mini-cluster mit 32 Rechnern, um gegen denselben C++ - Code auf einem Studi-Laptop anzukommen.

Edit (Illuvatar): Abgetrennt aus diesem Post
 
Zuletzt bearbeitet von einem Moderator:

escalate

Mitglied
Habe mich noch nicht so wirklich mit Optimierungen in Scala beschäftigt, aber ich habe es mal ausprobiert (Scala 2.9.0.1, sun-jdk 1.6.0_26):

Orginal:

generisch: 8.95ms
direkt: 1.734ms

einzige Änderung @specialized
Java:
25: def sumGeneric[@specialized(Double) X](a: Array[X])(implicit monoid: AdditiveMonoid[X]) = {

generisch: 1.776ms
direkt: 1.635ms

Das sieht doch schon wesentlich freundlicher aus. Das Wrapper-Objekt sieht man aber im Bytecode noch.
 

0x7F800000

Top Contributor
Orginal:

generisch: 8.95ms
direkt: 1.734ms

einzige Änderung @specialized
Java:
25: def sumGeneric[@specialized(Double) X](a: Array[X])(implicit monoid: AdditiveMonoid[X]) = {

generisch: 1.776ms
direkt: 1.635ms

Das sieht doch schon wesentlich freundlicher aus.
Huh? Was geht los... :autsch: Moment mal^^
 

0x7F800000

Top Contributor
[Liebe Mods! könnte bitte jemand den Thread absägen und irgendwo in Scala-Unterforum reinschmeißen?]

Also:
Ohne @specialized: generische Version ist um faktor 16 langsamer
Mit @specialized: um faktor 10 langsamer (ehrlich gesagt wundert es mich ziemlich, warum es überhaupt schneller wird?)

Dass die generische Version bei dir plötzlich genauso schnell läuft, wie die spezielle, ist für mich ehrlich gesagt ein ziemlicher Shock: ich habe mich eigentlich schon damit abgefunden, dass Scala dieses blöde infix-rumgewrappe nicht vernünftig inlinen kann :shock:

Was mich jetzt brennend interessiert ist, was du denn damit angestellt hast, bzw was ich denn schon wieder verpasst habe? ???:L

Hab hier scalac compiler version 2.9.0.1 und jdk1.7.0-b147, habe versucht das ganze mit verschiedenen Optionen wie -optimise zu kompilieren oder mit -server zu starten: bringt alles nichts, Faktor 10 bleibt. Wie kommt es zu solch einem gigantischen Unterschied? Wenn dieser Unterschied weg wäre, dann wäre alles nochmal wesentlich geiler... Es würde schlagartig meine Einstufung von Numeric von gruselig auf genial ändern.

PS: ehrlich gesagt sieht das ganze zu gut aus, um wahr zu sein: als ob man beim herumexperimentieren dieselbe methode zweimal gemessen hätte... :bahnhof:
 
Zuletzt bearbeitet:
J

JavaCoda

Gast
implicit def wrapToOps[T](x: T)(implicit monoid: AdditiveMonoid[T]): AdditiveMonoid[T]#Ops = new monoid.Ops(x)
<ironie>
Wow, Scala ist wahrlich ein Meilenstein für die Verständlichkeit und Nachvollziehbarkeit der Fachkonzepte aus dem Code
</ironie>
Mal ehrlich: Man muss angesichts solcher Spielereien und dem abwechselnden Zuckerschock, den man durch die Syntax kriegt, und den Wutanfällen, die einem die miserable IDE-Unterstützung bescheren, schon harter Fanboy sein, oder den ganzen Tag mit rosaroten Kontaktlinsen durch die Gegend taumeln, um zu glauben, dass so ein Bastelkram je den Status "nette aber unnütze Spielerei" verlassen und "Enterprise-ready" sein werden.
 

0x7F800000

Top Contributor
<ironie>
Wow, Scala ist wahrlich ein Meilenstein für die Verständlichkeit und Nachvollziehbarkeit der Fachkonzepte aus dem Code
</ironie>
Das ist ein kurzer und bestens nachvollziehbarer Einzeiler, vorausgesetzt man kennt die Syntax und entwirft gelegentlich irgendwelche Scala-Bibliotheken. Dies ist definitiv die Library-Designer Sicht. Der Benutzer sieht am Ende nur das hier:
Code:
val z = x + y
Bei deinen Enterprise-Anwendungen müssen die Benutzer ja auch nicht im Backend rumwühlen, sondern kriegen eine bunte Benutzeroberfläche...

Mal ehrlich: Man muss angesichts solcher Spielereien und dem abwechselnden Zuckerschock, den man durch die Syntax kriegt, und den Wutanfällen, die einem die miserable IDE-Unterstützung bescheren, schon harter Fanboy sein, oder den ganzen Tag mit rosaroten Kontaktlinsen durch die Gegend taumeln, um zu glauben, dass so ein Bastelkram je den Status "nette aber unnütze Spielerei" verlassen und "Enterprise-ready" sein werden.
Mit "Enterprise-ready" kenn ich mich nicht aus, interessiert mich nicht sonderlich. Ich will einfach ein Werkzeug haben, mit dem man vernünftig rechnen kann. Java ist um mehrere Größenordnungen besser als die meisten verstümmelten C-Parodien (sogenannte "mathematische Programmiersprachen"), aber man kann damit leider auch nicht vernünftig rechnen. Wenn Du nächstes mal auf irgendeinen Experimentalphysiker o.ä. triffst, dann kannst Du gerne versuchen ihm zu erläutern, wie "enterprise-ready" der ganze spitze Klammersalat und völlig.unleserliche(Formeln) sind, ich versuche es in der Zwischenzeit mit Scala, wenn niemand was dagegen hat ;)
 
Zuletzt bearbeitet:

escalate

Mitglied
Was mich jetzt brennend interessiert ist, was du denn damit angestellt hast, bzw was ich denn schon wieder verpasst habe? ???:L
Gute Frage, eigentlich gar nichts. Habe es einfach ohne irgendwelche Optimierungen ausgeführt.
Mit Scala 2.9.1 und JDK 1.7.0 bekomme ich die gleichen Ergebnisse. Ich habe es auch mal mit meiner Timing-Funktion getestet, wieder gleiche Zeiten.

Der Bytecode hier wird für die Spezialisierung auf double ausgeworfen:
Code:
public double sumGeneric$mDc$sp(double[], InfixWrapperTest$AdditiveMonoid);
  Code:
   0:   aload_2
   1:   invokeinterface #66,  1; //InterfaceMethod InfixWrapperTest$AdditiveMonoid.zero:()Ljava/lang/Object;
   6:   invokestatic    #203; //Method scala/runtime/BoxesRunTime.unboxToDouble:(Ljava/lang/Object;)D
   9:   dstore_3
   10:  iconst_0
   11:  istore  5
   13:  iload   5
   15:  aload_1
   16:  arraylength
   17:  if_icmpge       52
   20:  aload_0
   21:  dload_3
   22:  invokestatic    #181; //Method scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double;
   25:  aload_2
   26:  invokevirtual   #77; //Method wrapToOps:(Ljava/lang/Object;LInfixWrapperTest$AdditiveMonoid;)LInfixWrapperTest$AdditiveMonoid$Ops;
   29:  aload_1
   30:  iload   5
   32:  daload
   33:  invokestatic    #181; //Method scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double;
   36:  invokevirtual   #85; //Method InfixWrapperTest$AdditiveMonoid$Ops.$plus:(Ljava/lang/Object;)Ljava/lang/Object;
   39:  invokestatic    #203; //Method scala/runtime/BoxesRunTime.unboxToDouble:(Ljava/lang/Object;)D
   42:  dstore_3
   43:  iload   5
   45:  iconst_1
   46:  iadd
   47:  istore  5
   49:  goto    13
   52:  dload_3
   53:  dreturn

Die nicht-generische Variante:

Code:
public double sumDirect(double[]);
  Code:
   0:   dconst_0
   1:   dstore_2
   2:   iconst_0
   3:   istore  4
   5:   iload   4
   7:   aload_1
   8:   arraylength
   9:   if_icmpge       28
   12:  dload_2
   13:  aload_1
   14:  iload   4
   16:  daload
   17:  dadd
   18:  dstore_2
   19:  iload   4
   21:  iconst_1
   22:  iadd
   23:  istore  4
   25:  goto    5
   28:  dload_2
   29:  dreturn

Den Unterschied sieht man, der Wrapper ist schon noch da. Nächste Frage ist dann, was die JVM daraus macht ;)
 

0x7F800000

Top Contributor
Gute Frage, eigentlich gar nichts. Habe es einfach ohne irgendwelche Optimierungen ausgeführt.
Mit Scala 2.9.1 und JDK 1.7.0 bekomme ich die gleichen Ergebnisse.
Also der stinknormale compiler direkt von scala-lang.org und stinknormale jdk1.7.0 von der Oracle seite? (nicht irgendeine leicht divergente OpenJDK Version oder so?) :reflect: Vom Betriebssystem und der schnelligkeit des Rechners kann es nicht abhängen? Kann das irgendwie sein, dass scala beim launchen die falsche jvm-version benutzt oder dass irgendwelche für die Optimierung verantwortlichen Systemvariablen falsch gesetzt sind? ???:L

Der decompilierte bytecode sieht bei mir jeweils 1:1 exakt genauso aus, Zeichen für Zeichen. Was könnte denn noch die JVM dran hindern, vernünftig zu funktionieren? An zu schwacher Hardware kann's ja wohl nicht liegen, so Aufwendig kann das Bisschen Inlining ja nicht sein? :bahnhof:
 
Zuletzt bearbeitet von einem Moderator:

escalate

Mitglied
Also der stinknormale compiler direkt von scala-lang.org und stinknormale jdk1.7.0 von der Oracle seite?
Ja.

(nicht irgendeine leicht divergente OpenJDK Version oder so?)
Ich habe mit denen hier getestet, immer das Gleiche...
Code:
Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02, mixed mode)
Code:
OpenJDK Runtime Environment (IcedTea6 1.10.3) (Gentoo build 1.6.0_22-b22)
OpenJDK 64-Bit Server VM (build 20.0-b11, mixed mode)
Code:
Java(TM) SE Runtime Environment (build 1.7.0-b147)
Java HotSpot(TM) 64-Bit Server VM (build 21.0-b17, mixed mode)

Vom Betriebssystem und der schnelligkeit des Rechners kann es nicht abhängen?
Das wäre ja seltsam ;) 64bit?

Kann das irgendwie sein, dass scala beim launchen die falsche jvm-version benutzt oder dass irgendwelche für die Optimierung verantwortlichen Systemvariablen falsch gesetzt sind? ???:L

Die Version von Java habe ich mir so geben lassen:
Code:
scala -J-version
 

0x7F800000

Top Contributor
Ich habe mit denen hier getestet, immer das Gleiche...
Code:
Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02, mixed mode)
Code:
OpenJDK Runtime Environment (IcedTea6 1.10.3) (Gentoo build 1.6.0_22-b22)
OpenJDK 64-Bit Server VM (build 20.0-b11, mixed mode)
Code:
Java(TM) SE Runtime Environment (build 1.7.0-b147)
Java HotSpot(TM) 64-Bit Server VM (build 21.0-b17, mixed mode)
Ja, immer das gleiche: 64-Bit Server VM...

Bei mir ist's 32-bit Client VM (kleiner laptop :autsch:)
Code:
java version "1.7.0"
Java(TM) SE Runtime Environment (build 1.7.0-b147)
Java HotSpot(TM) Client VM (build 21.0-b17, mixed mode, sharing)
Aber launchen in -server (-Jserver? -J-server? wohin mit dem minus?) mode bringt auch nicht die erwünschte "agressive Optimierung" :noe: (außerdem bezieht sich dieses "mixed" doch eh darauf, dass es wie Client startet und wie Server optimiert, oder wie war das nochmal?)
 
Zuletzt bearbeitet:

0x7F800000

Top Contributor
The 32-bit JRE (any version) doesn't even have the Server VM.
Das wird's wohl sein? -.-
Da muss ich wohl der JVM für die erleuchtenden Fehlermeldungen danken: "-server" wird nämlich völlig stillschweigend akzeptiert... :autsch:
 
Zuletzt bearbeitet:

mvitz

Top Contributor
Falls es euch hilft, hier mal meine Werte:

Code:
generisch: 76.163ms
direkt: 5.038ms

Versionen:
java -version
Code:
java version "1.6.0_26"
Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
Java HotSpot(TM) Client VM (build 20.1-b02, mixed mode, sharing)

scala -J-version
Code:
Welcome to Scala version 2.9.1.final (Java HotSpot(TM) Client VM, Java 1.6.0_26)
 

0x7F800000

Top Contributor
Danke mvitz!

Das bekräftigt meine Vermutung, dass Scala-Code nur unter 64bit-Server-JVM ordentlich läuft, diesen Effekt kannte ich von Java bisher nicht, das ist eine außerordentlich wichtige Feststellung, danke euch beiden! :)
 
Zuletzt bearbeitet:

mvitz

Top Contributor
So, damit es noch vollständig ist:

Windows7 32bit Server VM -->
Code:
generisch: 21.328ms
direkt: 4.21ms

Server VM ist im JDK enthalten, und wurde per
Code:
SET JAVA_OPTS="-server"
gesetzt.

scala -J-version -->
Code:
Welcome to Scala version 2.9.1.final (Java HotSpot(TM) Server VM, Java 1.6.0_26)
 

0x7F800000

Top Contributor
Hm, das Manöver mit JAVA_OPTS="-server" hat nicht geholfen:
Code:
Fehler: Hauptklasse server konnte nicht gefunden oder geladen werden
Läuft so nicht...

Aber OK! Ist nicht so wichtig, der Plan ist klar: 64-bit Server JVM installieren, freuen! :D

Leute, ich bin euch außerordentlich dankbar, wäre mir das nicht aufgefallen, hätte ich in dem nächsten halben Jahr eine MENGE Code gegen die Wand gefahren, aber jetzt muss ich lediglich einige wenige Design-Entscheidungen korrigieren.

Erstmal keine weiteren Fragen :)
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
K Unterschied funktionial <-> OO anhand von Scala <-> Java JVM Sprachen: Kotlin, Scala, Groovy, Jython... 5
M Experten für Scala-Play- Programmierung gesucht!! JVM Sprachen: Kotlin, Scala, Groovy, Jython... 3
M Scala-Programm mit Netbeans compilieren JVM Sprachen: Kotlin, Scala, Groovy, Jython... 1
M Suche Scala Entwickler (Umsteiger [JAVA]) für Zusammenarbeit an privatem Projekt JVM Sprachen: Kotlin, Scala, Groovy, Jython... 7
R Frage zu Scala Code JVM Sprachen: Kotlin, Scala, Groovy, Jython... 2
Landei Scala Scala 2.10 RC JVM Sprachen: Kotlin, Scala, Groovy, Jython... 3
schlingel Scala Schulung - Gratis vom Scala-Schöpfer JVM Sprachen: Kotlin, Scala, Groovy, Jython... 2
Landei Scala Scala-Kritik JVM Sprachen: Kotlin, Scala, Groovy, Jython... 151
Spin Scala Eclipse IDE JVM Sprachen: Kotlin, Scala, Groovy, Jython... 7
Spin Funktionen vs Methods in Scala JVM Sprachen: Kotlin, Scala, Groovy, Jython... 9
Landei Scala Freies eBook "Scala for the impatient" JVM Sprachen: Kotlin, Scala, Groovy, Jython... 2
Spin Arithmetik in Scala JVM Sprachen: Kotlin, Scala, Groovy, Jython... 32
Spin Scala MenuListener JVM Sprachen: Kotlin, Scala, Groovy, Jython... 5
Spin Scala in Eclipse will nicht. JVM Sprachen: Kotlin, Scala, Groovy, Jython... 15
Landei Scala Deutsches Scala-Tutorial JVM Sprachen: Kotlin, Scala, Groovy, Jython... 3
B Scala oder Clojure JVM Sprachen: Kotlin, Scala, Groovy, Jython... 6
Landei Scala "Programming in Scala" - erste Ausgabe kostenlos JVM Sprachen: Kotlin, Scala, Groovy, Jython... 1
W Scala *.Scala to *.jar JVM Sprachen: Kotlin, Scala, Groovy, Jython... 6
H Scala und Aspekte JVM Sprachen: Kotlin, Scala, Groovy, Jython... 4
S Scala Klasse.class in Scala? JVM Sprachen: Kotlin, Scala, Groovy, Jython... 4
B Scala Scala und Netbeans GUI Editor JVM Sprachen: Kotlin, Scala, Groovy, Jython... 15
S Scala: Parser und Lexical JVM Sprachen: Kotlin, Scala, Groovy, Jython... 2
D Wie manche ich das in Scala JVM Sprachen: Kotlin, Scala, Groovy, Jython... 12
S Scala: Static - Konstruktor?? JVM Sprachen: Kotlin, Scala, Groovy, Jython... 5
G Scala IDE JVM Sprachen: Kotlin, Scala, Groovy, Jython... 18
A Scala und J2ME JVM Sprachen: Kotlin, Scala, Groovy, Jython... 2
S Scala Fragen zu Scala JVM Sprachen: Kotlin, Scala, Groovy, Jython... 21
D (Mathe-) Vektoren in Scala JVM Sprachen: Kotlin, Scala, Groovy, Jython... 4
Landei Scala im Kommen :-) JVM Sprachen: Kotlin, Scala, Groovy, Jython... 4

Ähnliche Java Themen

Neue Themen


Oben