Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

101

14.05.2015, 14:47

.Net besitzt einen echten JIT Compiler, der kompiliert auch zur Laufzeit. In einer üblichen .Net Executable steckt nur der CIL Code.
Du meinst möglicherweise soetwas wie Ngen, ebenfalls ein AOT Compiler für .Net. Das gibt es und wird so weit ich weiß unter anderem teilweise beim .Net Framework selbst eingesetzt. Mir wäre neu, dass das stark eingesetzt wird.
Der generierte Code ist außerdem an das System gebunden.

In meiner persönlichen erfahrung haben JIT Compiler sehr enttäuscht und erreichen in der Praxis nicht native Geschwindigkeit. Teilweise mag das sicher auch nicht alleinige Schuld der Idee des JIT seins, sondern auch die Designphilosophien der Sprachen. Das entfernen von nicht benötigten Code ist zum Beispiel eine gute Idee, die ein klein wenig Last von der Sprungvorhersage nehmen könnte, allerdings muss auch der JIT die Ausführung sehr weit voraussehen und wenn er falsch liegt sollte es richtig aufwendig werden. Theoretisch kann man auch Speicher sparen, in der Praxis aber eher nicht, weil ein guter JIT auch guten Speicher braucht. (Gänige JIT Sprachen sind auch Garbage Collected, der Speicheroverhead von GC ist sehr hoch )

Dass er exakt auf die zu verwendete CPU Architektur optimieren "kann" ist auch keine große Sache. Das selbe kann man auch prima AOT machen, der Intel C++ Compiler soll das zum Beispiel sehr gut machen(leider natürlich nur für Intel CPUs). In der Praxis optimieren viele JITs einfach nicht so gut. Optimierung braucht Zeit und die steht Just In Time einfach nicht zur Verfügung. Ich kenne zum Beispiel keinen JIT Compiler, der ein JIT Autovektorsierung, den Speed Up schlechthin, durchführen kann. Und dann bringt auch nix die Möglichkeit, neue Instruktionen für neue Architekturen erzeugen zu können, wenn der Optimierer sie nicht einisetzen kann.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Spiele Programmierer« (14.05.2015, 17:20)


Noctarius

Treue Seele

Beiträge: 120

Wohnort: Düsseldorf

Beruf: Manager of Developer Relations at Hazelcast, Inc. & Consultant for Scaleable Gameserver Systems

  • Private Nachricht senden

102

14.05.2015, 15:09

Ui du hast Recht, ich dachte dieses "Pre-JIT" (ich würde es weiterhin AOT Compilation nennen) wäre ein standard Anteil von .Net. Möchte mal wissen woher ich das habe. War das eventuell mal so oder ... hm ... Mono vielleicht? oO

edit:
Hier ist ein netter Beitrag zum Thema Optimierungen des HotSpot JIT in Java. Keine Ahnung wie weit der .NET JIT dagegen ist (http://de.slideshare.net/dougqh/jvm-mechanics-when-does-the) aber aus meiner Sicht ist der entscheidende Grundstein an diesen Optimierungen

- Sie sind automatisch eingebaut, sicher kann man eventuellen für bestimmte Fälle schnelleren Code schreiben aber ob sich das lohnt?
- Du musst die Tricks nicht alle selber kenne
- Da wurden ganze Doktor-Arbeiten drum geschrieben (wieso sollte ich es besser können als X Leute, die sich Tage / Wochen / Monate den Kopf darüber zermatern)
- Sie werden zur Laufzeit ermittelt und nicht wahrlos drauf los geschossen

Und natürlich kann man selber perfekt auf eine Platform optimieren, dann lieferst du dein Programm halt mit 20 verschiedenen Executables aus :) Main Java Program vom PC läuft aber auch auf einem RasPi, ohne Anpassung, ohne neu kompilieren.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Noctarius« (14.05.2015, 15:22)


Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

103

14.05.2015, 17:50

Das offizielle Mono zumindest nicht.

Die Präsentation ist sehr interessant, wenn die das alles zur Laufzeit machen, ist das schon faszinierend.
Allerdings kann sich wohl die Performance zufällig ändern und es kompiliert langsam.
Einen groß Teil der Optimierungen kann man jedenfalls auch AOT durchführen. Es gibt auch dort Devirtualisierung und Profile Guided Optimization. Auch wenn ich speziell im letzen Punkt dem JIT Ansatz einen Vorteil zugestehen muss.

Zum Beispiel der Intel Compiler kompiliert übrigens (auch vollkommen automatisch mit "Tricks aus Doktorarbeiten", wie alle modernen Compiler) unterschiedliche Codepfade in eine Anwendung und kann jeden Codepfad speziell auf die Pipeline des Prozessors abstimmen. Andere Compiler bieten da leider nicht ganz so gute Funktionalität an, allerdings ist es konzeptionell auch mit AOT möglich.

Zitat

wieso sollte ich es besser können als X Leute

Es geht doch nicht darum ob du es besser kannst, sondern ob es ein JIT Compiler besser kann als ein AOT Compiler kompiliert, obwohl er viel weniger Zeit zur Verfügung hat die Optimierungen einzusetzen und Java konzeptionell nicht auf Performance ausgelegt ist.
Und wenn es wirklich darauf ankommt, dann muss man einach selber ran. Und das nicht weil man so viel schlauer ist, als als alle Compilerentwickler zusammen, sondern weil der Compiler einfach immernoch eine Maschine ist. Er versteht nicht, was der Programmierer wirklich machen will, er kennt nur eine kleine feste Menge an Regeln die er anzuwenden versucht. Der Programmierer kann den Fall genau nach seiner Intention optimieren und die spezielle Situation mit Logik angehen anstatt mit Regeln.

Ich stimme LetsGo zu, dass es auf die Anwendung ankommt.

LukasBanana

Alter Hase

  • »LukasBanana« ist der Autor dieses Themas

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

104

14.05.2015, 18:37

So langsam aber sicher komme ich mit dem CFG bzw. Code Generator voran :D

Folgendes XieXie Programm kompiliert und rennt bereits :thumbsup: :

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// Temp XieXie Test

import Console

// "Calculation" interface (or rather abstract class)
class Calc {
    
    String name()
    
    int apply(int lhs, int rhs)
    
}

// Test main class
class Test {
    
    // Folds all elements from 'list' and start with 'neutral'
    static int fold(int[] list, int neutral, Calc calc) {
        
        Console.write("method: ")
        Console.writeLine(calc.name())
        
        int s := neutral
        foreach x : list {
            s := calc.apply(s, x)
        }
        
        Console.write("elements: { ")
        
        for int i ; i < list.size() ; i++ {
            Console.write(list[i])
            if i < list.size() - 1 {
                Console.write(", ")
            } else {
                Console.writeLine(" }")
            }
        }
        
        Console.write("result = ")
        Console.writeLine(s)
        
        return s
    }
    
    // Prints the underline for the specified string
    static void underline(String s, int extent := 0) {
        for int i := -extent ; i < s.size() ; i++ {
            Console.write("-")
        }
        Console.writeLine("")
    }
    
    // Prints an headline for the specified string
    static void headline(String s) {
        Test.underline(s, 4)
        Console.write("| ")
        Console.write(s)
        Console.writeLine(" |")
        Test.underline(s, 4)
    }
    
    // Main entry point
    static void main() {
        
        Test.headline("folding test")
        
        var list := { 3, -12, 42, 8, 39 }
        
        Test.fold(
            list,
            0,
            new Calc() {
                String name() { return "sum" }
                int apply(int lhs, int rhs) {
                    return lhs + rhs
                }
            }
        )
        
        Test.fold(
            list,
            1,
            new Calc() {
                String name() { return "multiplication" }
                int apply(int lhs, int rhs) {
                    return lhs * rhs
                }
            }
        )
        
    }
    
}


Moment gibt es in der Sprache noch keine Function-Objects, aber dafür kann man mit abstrakten und anonymen Klassen das ganze emulieren.
Hier an Hand des Beipiels einer (relativ) 'generischen' "fold" Methode.

Ausgabe des Programm ist:

Quellcode

1
2
3
4
5
6
7
8
9
----------------
| folding test |
----------------
method: sum
elements: { 3, -12, 42, 8, 39 }
result = 80
method: multiplication
elements: { 3, -12, 42, 8, 39 }
result = -471744

105

14.05.2015, 19:47

Man, das ist wirklich ausgesprochen schön :thumbsup:
Ich überlege auch irgendwie mal so etwas zu machen, also eine vm + compiler... mal sehn ^^

LukasBanana

Alter Hase

  • »LukasBanana« ist der Autor dieses Themas

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

106

14.05.2015, 21:42

Ich überlege auch irgendwie mal so etwas zu machen, also eine vm + compiler... mal sehn ^^

Eine kleine VM zu schreiben ist eigentlich gar nicht schwer, nur etwas Zeitaufwändig. Deshalb hatte ich auch damit angefangen.
Im Grunde reicht es ja aus, wenn der Kern einer VM aus einer Schleife und einer sehr großen Switch-Case Anweisung, für die ganzen Instruktionen, besteht.

Falls du wirklich auch eine eigene Sprache entwickeln willst: nimm dir nicht zu viel vor ^^
Jedes kleine Feature kann im Compiler viel Arbeit bedeuten.

LukasBanana

Alter Hase

  • »LukasBanana« ist der Autor dieses Themas

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

107

08.06.2015, 21:41

Mal was zum aktuellen Stand: Ich komme im Moment leider nur sehr langsam voran.
Wenigstens halte ich mich stringent daran, nur bisherige Features umzusetzen, statt immer noch mit neuen Features anzufangen :D
Zur Zeit bin ich vor allem Fehler in der Code Generierung am fixen.
Trotz Abstrahierung und Zwischendarstellungen, scheint mir die Code Generierung nach wie vor der schwierigste Teil des Compilers zu sein.
Ständig finde ich wieder Fehler, in denen mein Compiler Variablen zu früh überschreibt :rolleyes:
Aber die Code Generierung ist nun mal die Schnittstelle zwischen High- und Low Level schlecht hin :-)

Hier mal ein kleines Beispiel, das ich eben zum Laufen bekommen habe.
Natürlich kein wirklich sinnvoller Anwendungsfall, aber es geht darum,
dass der Compiler auch bei komplexeren Ausdrücken, das richtige erzeugt:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// XieXie Nesting Test
// 08/06/2015

import Console

class Printer { void print() }

class Test {

    static void main() {
        
        /*
        Iterate over list, which is returned by "getArray",
        from an anonymous class, which is derived from "Object"
        */
        foreach x :
            new Object() {
                int[] getArray() {
                    return { 42, 3, -12 }
                }
            }.getArray()
        {
            Console.writeLine(x)
        }
        
        // Array access and anonymous class
        {
            null,
            new Printer() {
                void print() {
                    Console.writeLine("\(__CLASS__).\(__PROC__)")
                }
            },
            null
        }[1].print()
        
        // Call procedure with array, immediately after object instantiation
        Console.writeLine(
            new Object() {
                int sum(int[] list) {
                    int s
                    foreach x : list {
                        s += x
                    }
                    return s
                }
            }.sum({ 1, 2, 3 })
        )
        
    }
    
}

Werbeanzeige