Files |  Tutorials |  Articles |  Links |  Home |  Team |  Forum |  Wiki |  Impressum

Aktuelle Zeit: Do Mär 28, 2024 13:54

Foren-Übersicht » Programmierung » Allgemein
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 6 Beiträge ] 
Autor Nachricht
 Betreff des Beitrags: Kleine Jit für C++
BeitragVerfasst: Mi Mai 18, 2016 18:42 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Ich hab eine sehr interessante Lib gefunden asmjit.
Dort kann man Programmcode mit einer C++ API zusammen stöpseln und dann für 64 oder 32Bit Architektur bauen lassen.
Dabei werden alle CPU Fähigkeiten benutzt, die zu finden sind.
Der Entwickler arbeitet auch an einer Mathe und Shader lib die darüber liegt.

Wenn man SIMD in Matrix, Quaternion und Vector verpackt hat, dann leidet man darunter, dass die Compiler ziemlich schlecht optimieren, weil die einzelnen Methoden ein großes optimieren verhindern und das kopieren in und von den SIMD registern den großteil der Zeit braucht.
Man kann mit der lib das wie mit Displaylisten machen und innerhalb eines Scopes oder mit Begin/End Mechnanik die Operationen sammeln und dann ein passenden SIMD Code generieren lassen.
Daher kann man die Werte einmal am anfang kopieren und dann auf den Registern durchrechnen, statt bei jeden Methodenaufruf wieder kopieren zu müssen.
Ein weiterer Vorteil ist, dass die Jit sich um die beste SIMD Extension kümmert und man selber nicht mit intrinsics rum wursteln muss und dann zur runtime die passende dispatchen muss.

Ich erhoffe mir später eine Optimierung meiner OpenGL VM damit, da ich anhand der Zugriffe auf OpenGL Funktionen die Sprungliste zur Laufzeit immer wieder neu bauen kann.
Das ist einer der Großen Vorteil von C# und Java, die zur Laufzeit Code Anpassen, um die Ausführung zu verbessern und bei Branch intensiven Code sogar schneller als C++ werden können.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Kleine Jit für C++
BeitragVerfasst: Fr Mai 20, 2016 02:28 
Offline
DGL Member

Registriert: Fr Mai 11, 2012 13:25
Beiträge: 229
Programmiersprache: c++, c, JavaScript
Interessantes Thema! Vor allem in Zusammenhang mit Neural Networks und Deep Learning, finde ich.
Weißt Du zufällig wie das dann bei Java und C# abläuft (oder entwickelt wurde), dass der Code während der Runtime geändert wird?
Ich stelle es mir z.B. schwierig vor, erstmal rauszufinden, welche Branches weggelassen werden können, ohne dass dabei erst mal viel Zeit draufgeht und die JVM oder was auch immer dafür zuständig ist, kann ja auch nicht alle Möglichkeiten durchspielen.

_________________
"Pixel, ich bin dein Vater."
-Darf Shader


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Kleine Jit für C++
BeitragVerfasst: Fr Mai 20, 2016 08:56 
Offline
DGL Member
Benutzeravatar

Registriert: Do Sep 02, 2004 19:42
Beiträge: 4158
Programmiersprache: FreePascal, C++
@Vinz Als Denkanstoß: In der einfachsten Implementation könnte man einen Counter pro Funktion und einen pro Bytecode-VM-Instruktion haben, der mitzählt wie oft das aufgerufen wird. Wenn der Counter der Funktion in einer Sekunde um mehr als meinetwegen 5 ansteigt, schau welche Instruktionen besonders oft ausgeführt werden (daraus bekommst du die Branches die wahrscheinlich sind) und kompilier das. In komplexeren Implementationen schaut man auch noch was für Typen oft vorkommen etc.

Weitere Infos z.B. im Wikipedia-Artikel zu JITs. Eventuell findet sich auch in den Technical Reports des PyPy-Projektes das ein oder andere spannende Detail.

viele Grüße,
Horazont

_________________
If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung.
current projects: ManiacLab; aioxmpp
zombofant networkmy photostream
„Writing code is like writing poetry“ - source unknown


„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Kleine Jit für C++
BeitragVerfasst: Fr Mai 20, 2016 13:22 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Ich hab gestern mal die Lib als Experimental Code in mein Radon Framework gepackt und dabei noch was aus deren CMake Config gelernt.
Das integrieren ist wirklich einfach und kompiliert durch.
Ich will mal testweise eine Matrix*Vector und Vector+Vector operation auf JIT Basis bauen, also eine Matrix und Vector Klasse und in den Multiplikations und Additions operator, der Bytecode in eine Buffer schreibt.
Der Buffer kann dann durch die Jit und ich erhoffe mir dann ein SIMD Optimierten Code, der die ganzen Aufrufe zusammen gepackt wiederspiegelt.

Das Problem Wieso SIMD in den meisten Bibliotheken so mies performt, liegt daran, dass oft Vector, Matrix, Quaternion und co als Klassen gekapselt und dann in den Operatoren die SIMD intrinsics oder ASM Code steckt. VS und GCC hatten in meinen letzten Tests die inline makierten Methoden nicht inlined und wenn ich Macros oder Templates genommen hab hat er es zusammen gepuzzelt aber die register kopiern intrinsics nicht weg optimiert.
Die meiste Zeit geht für das kopieren von Speicher in die FPU/APU Register und dann in die SIMD Register und den weg zurück drauf.
Jeder Operator kopiert also alles hin, macht die eigentliche Operation und kopiert dann wieder zurück und das für jede Operation.

Wenn man also die Daten einmal in die Register schiebt, dann alle Operationen macht und einmal wieder raus zieht hat man die Performance, die man eigentlich erwartet und das Kann man mit der JIT und eine Art CommandBuffer erreichen.

Vinz hat geschrieben:
Weißt Du zufällig wie das dann bei Java und C# abläuft (oder entwickelt wurde), dass der Code während der Runtime geändert wird?
Ich stelle es mir z.B. schwierig vor, erstmal rauszufinden, welche Branches weggelassen werden können, ohne dass dabei erst mal viel Zeit draufgeht und die JVM oder was auch immer dafür zuständig ist, kann ja auch nicht alle Möglichkeiten durchspielen.

Der modernste Ansatz ist Hardware basiertes Profiling, welches bei AMD mit Instruction based sampling(IBS) und bei Intel mit event based sampling(EBS) passiert.
Dabei haben die CPUs ein kleinen Speicherbereich(bei IBS 4k), wo die alle Counter zwischen 2 sample Aufrufen hin schreiben.
Bei JVM wird bei verfügbarkeit der Sampling Code in Funktionsscope injiziert, also vor der ersten Operation in der Funktion und beim Ret.
Dann kommt die Analyse, welche guckt ob branch missprediction counter > 0 sind, versucht den Branch um zu kehren oder bei moderneren CPUs gibt es eine Instruction für welcher Fall erwartet wird(hat VC++, GCC, CLANG auch über Preprocessor raus gelegt).
Dabei merkt sich die JVM den Vorigen Schritt und vergleich beim nächsten mal ob es besser geworden ist.
So passieren zig optimierungen über viele Iterationen.

Im schlimmsten Fall, also wenn die Hardware so um die Steinzeit gebaut wurde, dann nutzt die JVM Atomic Counter und platziert die gezielt an stellen wo sie Bottlenecks vermutet und macht Altmodisches instruction sampling mit sehr niedrigen sampling raten.
Die meisten CPU Profiler benutzen instruction sampling mit sehr hohen sampling raten, z.B. 1ms und jedes Sample holt sich den Callstack und kann so über längere Zeit ein gutes Abbild geben, in welchen Funktionen wie lange die CPU war.
Da bei Java aber dein Programm schnell laufen soll und JIT optimierung das Sahnehäufchen auf dem Kuchen sein soll, wird die Samplingrate wesentlich niedriger angesetzt, z.B. bei jeden GC Update. Ausserdem müssen nicht alle Threads angehalten werden, wie beim Profiller, sondern jeder mal irgendwann.

Google ihr C++ Profiler z.B. überlädt die new, malloc, delete, free Funktionen und tut dort noch im Profiling Mode Callstack und die CPU Infos abholen.

Bei C# wurde JIT sehr lange nur bei der Startupphase verwendet um einmal den Vorkompilierten Code zu finalisieren, also nicht wie bei Java wo man noch Quellcode oder schon VM Bytecode hat.
Also C# hat schon generischen Code gebaut, dann startet der und baut dann für das System optimierten CPU Bytecode. Die Daten werden dann im System tief vergraben gecached, dass beim nächsten Start die wieder verwendet werden können. Das ist auch der Grund wieso z.B. VS2012 und aufwärts beim ersten start lange braucht und bei jeden weiteren sehr fix da ist, weil der Cache greift. Das gilt für VC#, ka. wie es in der Linux Welt mit dem C# Compiler ist.
In einem MSDN Blog Artikel hatte ich gelsen, dass in VC#2015 dann auch einige JIT Mechanismen drin sind die zur Laufzeit optimieren.
Also hatte sich so gelesen, als ob vorher nicht wirklich was zur Laufzeit optimiert wurde.
Visual Studio hat "Profile Guided Optimization" für C++ und C#, da startet man das Program mehrere male und durchläuft so sämtliche funktionalität.
Dabei generiert dann der Profiler ein Log und kompiliert und linkt die Applikation komplett neu, auf basis der gesammelten Verhaltensdaten.
So stellt er dann Branches um, zieht Funktionen zusammen(bei zu hoher instruction latency), zerlegt sie(bei instruction cache probleme), optimiert für caches und so weiter.
Das ist am Ende das gleiche wie bei JVM mit der Echtzeitoptimierung aber halt Offline.
Der große Nachteil ist, wenn man das nicht vollständig oder unter falschen Umständen gemacht hat, dann kommen falsche Daten raus und es wird Falsch optimiert.
Dies kann bei JVM natürlich nicht passieren, weil er sich von iteration zu iteration verbessen will und fehlgeschlagene Optimierungen wieder rückgängig macht.

IBS ist z.B. der Grund wieso ich immer ein AMD System zum Entwickeln hab.
Denn erstmal ist der Profiler von AMD OpenSource und kostenfrei und IBS kann man bei den schnellsten AMD CPUs noch teilweise in Echtzeit Profilen.
Die Daten sind korrekt, weil die CPU sich selber drum kümmert und weiß, wenn sie ein L1/2/3 Cache-Miss hat, die Branch pre prediction schief lief, wieviel Cycles für Befehlsausführung und Datenbeschaffung drauf gehen oder wielange man auf Ergebnisse gewartet hat, weil nicht genug in der Instruction Pipeline war.
Die Counter zählen alle hoch und wenn man es ausliest, werden die resettet.
So legt man selber fest wo man profilen will und muss nicht alles einzeln irgendwo raus ziehen.
Intel setzt auf ein Event basiertes profiling, welches wie vermuten lässt man auf bestimmte Ereignisse konfigurieren kann und dann werden Counter entsprechend getriggered.
Das ganze ist kompakter und vermutlich noch schneller aber bietet wesentlich weniger Informationen gleichzeitig.
Damit hab ich noch nicht praktisch gearbeitet, weil VTune unglaublich teuer ist und ich noch kein OpenSource Profiler auf EBS Basis gesehen hab.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Kleine Jit für C++
BeitragVerfasst: Fr Mai 20, 2016 18:40 
Offline
DGL Member
Benutzeravatar

Registriert: Mo Nov 08, 2010 18:41
Beiträge: 769
Programmiersprache: Gestern
Telerik hat dazu einen wunderbaren Artikel hier. Es ist also nicht ganz so viel Magic wie man denkt.

_________________
Meine Homepage


Nach oben
 Profil  
Mit Zitat antworten  
 Betreff des Beitrags: Re: Kleine Jit für C++
BeitragVerfasst: Di Mai 24, 2016 18:39 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Es gibt leider kaum Doku, von daher ist es viel Code lesen und verstehen aber ich hab nun mal eine Vektor multiplikation hin bekommen.
Code:
  1. typedef void(*FuncType)(float*, float*);
  2.  
  3. class DebugLogger: public asmjit::StringLogger
  4. {
  5. public:
  6.     virtual void logString(uint32_t style, const char* buf, size_t len = asmjit::kInvalidIndex) noexcept override
  7.     {
  8.         RF_IO::LogInfo(buf);
  9.     }
  10. };
  11.  
  12. void Jitter()
  13. {
  14.     using namespace asmjit;
  15.    
  16.     JitRuntime runtime;
  17.     X86Assembler assembly(&runtime);
  18.     X86Compiler c(&assembly);
  19.     DebugLogger logger;
  20.     assembly.setLogger(&logger);
  21.    
  22.     c.addFunc(FuncBuilder2<void, float*, float*>(kCallConvHost));
  23.    
  24.     X86GpVar opA = c.newIntPtr("opA");
  25.     X86GpVar opB = c.newIntPtr("opB");
  26.  
  27.     c.setArg(0, opA);
  28.     c.setArg(1, opB);
  29.  
  30.     X86XmmVar a = c.newXmm("a");
  31.     X86XmmVar b = c.newXmm("b");
  32.  
  33.     c.movdqa(a, x86::ptr(opA, 0, 4));
  34.     c.movdqa(b, x86::ptr(opB, 0, 4));
  35.  
  36.     c.mulps(a, b);
  37.  
  38.     c.movdqa(x86::ptr(opA,0,4), a);
  39.  
  40.     c.endFunc();
  41.     auto result = c.finalize();
  42.  
  43.     FuncType func = asmjit_cast<FuncType>(assembly.make());
  44.  
  45. // code ausführen
  46.     RF_Pattern::Singleton<RF_Thread::ThreadPool>::GetInstance().WaitTillDoneWithInactiveQueue();// auf generierten code in console warten
  47.  
  48.     __declspec(align(16)) Vec128 v1,v2;
  49.     v1 = Vec128::fromSF(11, 5, 3, 2);
  50.     v2 = Vec128::fromSF(1, 2, 3, 4);
  51.     func(v1.sf, v2.sf);
  52.     return;
  53. }


Code:
  1. L0:                                     ;                 [..  ] 
  2. movdqa xmm1, dword ptr [rdx]            ; movdqa b, [opB] [.R.w] 
  3. movdqa xmm0, dword ptr [rcx]            ; movdqa a, [opA] [r.w ] 
  4. mulps xmm0, xmm1                        ; mulps a, b      [. xR] 
  5. movdqa dword ptr [rcx], xmm0            ; movdqa [opA], a [R R ] 
  6. ret 
  7. L1:                                     ;                 [    ]


Man kann übrigens den Compiler noch raus werfen.
Der kümmert sich um ein bisschen high logik, Register und ptr code verwaltung und auch noch per Feature den code so umstellt, dass die cpu weniger warten muss.
Wenn man die raus wirft, fliegt ne menge Code raus und das ganze wird noch viel kleiner als binary aber bisher muss ich sagen, dass ich das lieber drin hab.

_________________
"Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren"
Benjamin Franklin

Projekte: https://github.com/tak2004


Nach oben
 Profil  
Mit Zitat antworten  
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 6 Beiträge ] 
Foren-Übersicht » Programmierung » Allgemein


Wer ist online?

Mitglieder in diesem Forum: Google [Bot] und 31 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
cron
  Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de
[ Time : 0.047s | 18 Queries | GZIP : On ]