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

Aktuelle Zeit: Do Mär 28, 2024 09:55

Foren-Übersicht » Sonstiges » Projekte
Unbeantwortete Themen | Aktive Themen



Ein neues Thema erstellen Auf das Thema antworten  [ 64 Beiträge ]  Gehe zu Seite Vorherige  1, 2, 3, 4, 5  Nächste
Autor Nachricht
 Betreff des Beitrags: Re: Radon Framework
BeitragVerfasst: Mi Okt 21, 2015 02:08 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
OpenGLVM Builder
Ich hab den Builder vorran getrieben.
Der aktuelle Stand erlaubt das parsen der specs, dann wird der Baum auf der Platte gespeichert, damit man nicht nochmal parsen muss(dauert unter c# und .net ziemlich lange).
Man kann States erzeugen und Funktionen zuweisen, die Parameter von Funktionen setzen, nach Funktionen suchen und externe Variablen kann man auch schon erzeugen.
Bild
Was nun fehlt ist das Laden/Speichern von Intermediate Code,
das verschieben von Funktionen innerhalb von States und zwischen States und
das löschen von externen Variablen.
Dann kann man damit tatsächlich schon mal arbeiten.

Was dann noch fehlt sind das ganze validieren von Variablen und Parametern und ich versuche irgendwie die Parameterwerte von jeder Funktion mit im Codebaum an zu zeigen aber das hab ich bis dato ned hin bekommen(blödes wpf data model konzept).


Dateianhänge:
oglvmbuilder0.7.jpg [74.72 KiB]
Noch nie heruntergeladen

_________________
"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: Radon Framework
BeitragVerfasst: Fr Nov 06, 2015 01:09 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Ich hab die Woche eine eigene HashList Klasse entwickelt und hab nun angefangen die mit meiner aktuell verwendeten Klasse zu benchmarken.
Die aktuelle Variante nutzt google sparse und dense hash tables, weil ich immer davon ausgegangen bin, dass die ziemlich schnell sind.
Ich hatte zumindestens einige Artikel gelesen, wo man die empfohlen hat.
Mir ist es wichtig, dass mein Code im Debug sowie Release mode flott laufen und das scheint bei google nicht der Fall zu sein.

Mein Benchmark reserviert Platz für 5000 Elemente und füllt jeweils 5000 in die Implementierung.
Dann läuft ein Test mit 10.000.000 zugriffen auf ein existierenden Key, dann läuft ein Test mit 5.000.000 erfolgreichen und 5.000.000 nicht erfolgreichen abwechselnd.

Alle Werte sind in Cycles und keiner von den ist multithreaded.
Im Debug Mode
FetchSuccessfulTiming
706072 = HashList
228834923 = google sparse map
9852593 = google dense map
FetchMixedTiming
1556480 = HashList
225335541 = google sparse map
10879108 = google dense map
Im Release Mode
FetchSuccessfulTiming
319534 = HashList
1039383 = google sparse map
132363 = google dense map
FetchMixedTiming
532817 = HashList
1759199 = google sparse map
408124 = google dense map

Ich dachte ich hab irgendwo noch ein Bug in mein Code als ich einige Minuten auf die Ergebnisse vom google Hash Implementierung für Debug gewartet hab.
Leider sind beide wirklich unglaublich langsam in Debug und meine Vermutung ist der ganze Template kram, der da durch läuft.
Im Release ist die Sparse, welche ich eigentlich immer nutze immer noch um einiges langsamer und nur die dense, die ich sehr selten nutze ist noch schneller.

Meine Implementierung(Source, Header) trennt die Daten von den Keys, also ich hab 2 Listen, eine mit Keys und eine mit Pointern.
Wenn ich ein Key suche, dann greife ich nur auf die Keyliste und wenn ich ein Value haben will, dann greife ich erst auf die Keyliste zu und wenn ich ein treffer hab nehme ich den selben Index in der Pointerliste.
Also kümmert sich meine Hashliste nicht um Daten, sondern um Hashes und die sind cache effizient nebeneinander, weswegen ich schon in der unoptimierten Variante so schnell bin.

Ich hab das ganze mit UnitTest in Begleitung geschrieben und die code coverage liegt bei knapp 87%.

Als nächstes will ich mit der Optimierung anfangen.
Die erste optimierung ist das einführen von Buckets, denn aktuell ist jedes Element einem Slot zugewiesen und wenn der belegt ist sucht er ein anderen.
In der Regel benutzt man buckets, also hat man eine Kollision, dann guckt man in das Bucket nach weiteren Plätzen und wird erst ein anderen Slot wählen, wenn der Bucket voll ist.
Dies nutzt man in der Regel, um 99% der Elemente aus den Spiel zu nehmen und dann mit dem letzten paar Sequenziell ab zu frühstücken.
Das hat den Vorteil, dass die Sequenziellen Tests Cachefreundlich sind und der Speicher darauf getrimmt ist vorraus zu laden.
Meine Buckets werden mindestens eine Cacheline groß, denn weniger ist ineffizient, weil der Sprung auf den nächsten Slot garantiert ein Cachemiss ist und der kostet um einiges mehr als alle Elemente in der Cacheline zu prüfen.
Ich erwarte goolge dense hash bereits mit dieser Optimierung zu schlagen.

Die 2. optimierung wird SIMD, spätestens hier erwarte ich, dass ich noch weitere Cachelines zu einem Bucket hinzufügen werde.
Denn auf meinem System hab ich 64Byte Cachelines und 16Byte verarbeitet SSE2 bereits, Intel hat auch schon bis zu 64Byte Vektoren.
Also im aller besten Fall könnte ich 16 Keys in einer Operation prüfen.

Eine 3. Optimierung wäre die Cacheline Verdrahtung zu beachten, z.B. hat mein i5 ein "8-Way Set Associative" Verhalten also die 9. Cachelines kollidiert.
Mein Level 1 Data Cache ist 64Byte und das mal 8 sind 512 Bytes, also sollte man ein offset von MemoryBlock % 512 addieren und diese Addresse als Startwert für die KeyListe verwenden. Nun könnte man 512Bytes lesen, ohne cache miss und während man bei Line 8 ist, kann die CPU schon aus Level 2 die ersten Cache Lines wieder mit neuen Daten beschreiben. Das wäre aber nur Interessant, wenn die Bucketgröße über 512 geht.
Ab ein unbekannten Punkt ist es Effizienter auf ein anderen Slot zu springen, statt die Buckets zu vergrößern.
Das will ich dann profilen, um möglichst gute Richtwerte zu finden.

Die Werte verdrahte ich nicht hart, denn ich hab Code, um die Caches der CPU ab zu fragen, aktuell benutzt ich z.B. schon die Page Größe des OS um mir Speicher zu holen.
Bei Windows sind das 4096 Bytes, also 1024 Keys in meinem Fall.

_________________
"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: Radon Framework
BeitragVerfasst: So Nov 08, 2015 00:50 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Visual Studio 2015 Support
Ich hab gestern eine Visual Studio 2015 Pro Lizenz bekommen und gestern Abend sowie Heute mal mein Code angepasst.
Die neue IDE ist wesentlich pedantischer als die VS2013, so hab ich einige Fehler gefunden, die nicht mal GCC und XCode(clang) gefunden haben.
3 Große Bugs hab ich nun gefixt.

variadic function mit Referenz als ersten Parameter ist laut Standard nicht erlaubt, weil das Verhalten unbestimmt wäre.
Bisher haben das alle Compiler hin genommen und nicht mal gewarnt, VS2015 hat mir das als C++11 Standard Fehler um die Ohren gehauen.

Falsche forward decleration von class statt struct wurde bisher von allen Compilern stillschweigend konvertiert und nicht mal gewarnt.
VS2015 hat es nicht akzeptiert und mir ein indirekten Fehler gegeben. Indirekt, weil er beim linken sagte, dass er eine Funktion, wo die dekleration verwendet wurde nicht implementiert wurde.

noexcept modifier ist ein praktisches C++11 Feature, welches dem Compiler sagt, bau kein code für Exception Handling, ausser ich sag da kann was passieren.
Daran ist auch ein Verhalten gekoppelt, wie reagiert werden soll, wenn was passiert.
Jedenfalls hatte ich es falsch verwendet und bis auf VS2015 hat mir mal wieder kein Compiler davon was erzählt -_-
Ich habe eine Exceptionfreie Lib, das werfen Exceptions ist sogar explizit deaktivert und hab dann an den einzelnen Funktionen, wo keine Exceptions geworfen werden es entsprechend makiert.
C++11 legt als default fest, dass alle User Funktionen, Default Constructor/Destructor/Move Operator und so weiter noexcept(true) werden müssen und VS2015 hält sich nun dran und entsprechend hat er mir alles um die Ohren gehauen, wo ich selber noexcept makiert hab aber nie das Default verhalten fest gelegt hab.
Das muss man auch noch machen aber wieder wollte keiner der Compiler mir das mal sagen, dass die selber es dann setzen.

HashList VS2013 VS VS2015
Im Debug Mode
FetchSuccessfulTiming
706072 wird zu 672179 = HashList
228834923 wird zu 176788410 = google sparse map
9852593 wird zu 10436057 = google dense map
FetchMixedTiming
1556480 wird zu 1380427 = HashList
225335541 wird zu 173030559 = google sparse map
10879108 wird zu 11796702 = google dense map

Im Release Mode
FetchSuccessfulTiming
319534 wird zu 366582 = HashList
1039383 wird zu 811903 = google sparse map
132363 wird zu 130179 = google dense map
FetchMixedTiming
532817 wird zu 572482 = HashList
1759199 wird zu 1833678 = google sparse map
408124 wird zu 408431 = google dense map

Code Generation und normaler Code statt Templates
Ich habe meine Quaternion Klasse als deprecated makiert, weil ich nun eine QuatF32 und QuatF64 Klasse habe, welche von CMake generiert werden.
Mein generierter Code liegt nun in build/code_templates und Funktioniert wie gehabt.
Quaternion Template hat kein Sinn gemacht, weil ledeglich Float32 und Float64 Implementierungen sinn machen, man aber alles übergeben könnte.
Ich hatte auch ein Math Template, welches ich deprecated makiert hab und gegen Float32 und Float64 ersetzt hab.

SIMD
Ich hab mir nun mal überlegt, wie ich das SIMD Problem lösen könnte.

Das Problem ist folgendes, man Kapselt in Vector Klassen SSE oder ähnliche SIMD Extensions und tut dann seine Variablen von diesen Typen ableiten.
Dies nutzt nun zwar SIMD aber furchtbar ineffizient, teilweise so schlimm, dass eine for loop schneller sein kann.
Wieso ? Der Compiler sieht Klassen und kopiert alles fein hin und her, die intrinsic bzw. asm implementierungen kann er nicht wirklich anfassen und optimiert diese kaum bis garnicht.
Bei einer For loop kann er schalten und walten wie er mag, schleifen entrollen, SSE2 code verwenden(bei 64Bit Build) und code für den Cache besser optimieren.
Ich bin über dieses Problem selber gestolpert, als ich mein Code geprofiled hab und bin im Netzt dann fündig geworden, Bullet Physic hatte das Thema im Forum, es gibt ein paar Slides und Vorträge im Netzt, die das gleiche Problem beobachtet haben.

Die Lösung ?
Möglichst große Probleme mit SIMD optimieren und sonnst es dem Compiler überlassen.
Auf C++17 hoffen, denn aktuell wird darüber diskutiert SIMD unter die Arme zu greifen.
Dabei würde es schon reichen, die berühmten _m128 und ähnlichen inoffiziellen SIMD Datentypen zu standardisieren und damit den Compilern Tür und Tor auf zu machen dort mit SSE und Co zu optimieren.

Meine Idee
Nicht auf C++17 hoffen und meine SIMD Implementierung soll auf Container funktionieren.
SIMD hat seine eigenen Register und die Daten müssen immer erst in die Register ge und entladen werden und genau da ist die Schwäche von den üblichen Klassen Kapselungen.
V1 + V2 bedeutet, dass aus dem Speicher/Cache die Daten in die SIMD register geladen werden müssen, dann eine Addition und dann das Ergebnis zurück in den Cache/Speicher geladen. Die Addition kostet in der Regel 1 Cycle egal ob SIMD, APU oder FPU aber das laden in die Register und Cache/Speicher kostet 10-130Cycles auf einer modernen CPU.
Schlimmer wird es bei einer Matrix z.B. TransformMatrix * V1, denn dann lade ich die einzelnen Vektoren von der Matrix und V1 in die Register, mach die Multiplikation, bestehend aus mehreren Operationen und lade das Ergebniss wieder in den Cache/Speicher. Die teuren Operationen sind mehr geworden aber die günstigen nicht im Verhältnis.
Ein Container enthällt z.B. 400 Vektoren, für mein Partikel System und ich will es Transformieren, dann ist es sinniger die Matrix in die Register zu laden und dann in einer Schleife jeweils ein Vektor und dann die Multiplikation zu machen. Nun hab ich 404 Vektoren in die Register geladen, statt 2000Vektoren für einzelnen SIMD Klassen.
Diese Art von Optimierung macht Sinn und deswegen will ich nur noch SIMD Container Lösungen einbauen.
Ich makiere dann die Vektor und Matrix Klasse deprecated und werde neue Container basierte Alternativen bauen.

_________________
"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: Radon Framework
BeitragVerfasst: Mo Nov 09, 2015 02:34 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Erste Optimierung von HashList war ausreichend
Ich hab die erste Optimierung gemacht, in dem ich den Code mal durch VS2015 Instruction Profiler(cool und neu) und noch parallel CodeXL Sampling Profiler gejagt hab.
Beide hatten das gleiche Ergebnis, ich verbrauche 33% der Zeit mit dem Modulo Operationen.
Also hab ich versucht raus zu bekommen, ob ich das Modulo irgendwie umgehen kann und die einfachste Methode ist mit 2er Potenzen zu arbeiten.
Code:
  1. RF_Type::Size index = Key % m_Capacity;
  2. RF_Type::Size index = Key & (m_Capacity-1);// equivalent, wenn m_Capacity 2er Potenz enthält

Ich musste ledeglich ein paar Berechnungen in der Grow Funktion anpassen und schon wurde das gewährleistet.

Im Debug Mode
FetchSuccessfulTiming
672179 wird zu 445546 = HashList
176788410 = google sparse map
10436057 = google dense map
FetchMixedTiming
1380427 wird zu 988018 = HashList
173030559 = google sparse map
11796702 = google dense map

Im Release Mode
FetchSuccessfulTiming
366582 wird zu 106744 = HashList
811903 = google sparse map
130179 = google dense map
FetchMixedTiming
572482 wird zu 167005 = HashList
1833678 = google sparse map
408431 = google dense map

_________________
"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: Radon Framework
BeitragVerfasst: Fr Nov 13, 2015 14:59 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Ich hab dieses Woche begonnen die Software für ein BRDF/BTDF Scanner zu schreiben, den ich auf Arbeit entwickel.
Bisher hab ich das alles über ein VisualGDB Addon mit einem custom Visual Studio Projekt gelöst, um den ganzen kram zu testen.
Nun brauch ich aber etwas, was auch unser Buildgrid mit hantieren kann und VS ist nicht gerade ein Linux oder OSX Freund.
Ich teile das ganze prinzipiell in 2 Bereiche, Anwendungssoftware, welche überwiegend mit QT und einigen Teilen Radon framework sowie Radon CMake framework arbeitet und Hardware Service, welche ausschliesslich mit Radon CMake und Radon framework arbeiten.
Die Anwendungen sind für Windows, Linux X11 und OSX geplant und die Hardware Services für Raspberry Pi.

Daher arbeite ich aktuell gut 16h am Tag an beiden Radon und Radon CMake framework und hab einiges geschafft.

Radon CMake framework
Ich hab vor langer Zeit das Framework gebaut, weil ich nicht immer und immer wieder die gleichen CMake Scripte in Projekte copy/pasten wollte und teilweise auch recht komplexe Projekte zusammen führen wollte.

Ich hab nun automatisches updates von Abhängigkeiten sowie vom Framework selber eingebaut.
Dabei guckt er im Konfiguration Schritt von CMake, ob die Abhängigkeiten per Git oder SVN(aktuell unterstützte Datei Versionierung) geladen wurde und lässt ein update drauf laufen.
Das wurde Notwendig, weil ich diverse Projekte habe und ich nicht immer alle manuell updaten will, wenn ich eine Änderung mache.
Ich hab auch ein fix gemacht, damit mehrere Parameter pro Tool möglich sind, wie z.B. das quiet flag vom git pull Befehl.

Visual GDB support ist ausgerollt aber noch in der weiterentwicklung. Wer das kostenpflichtige Addon VGDB nicht kennt, der sollte es mal ausprobieren, wenn man Visual Studio benutzt und für Linux Platformen mit entwickelt.
Das Addon kümmert sich um Datensynchronisierung, integration vom gdb in die VS debugger Umgebung und ist neben Visual Assist X etwas, wofür ich gerne Geld ausgebe.

Intrinsic support für die Compiler hab ich hinzugefügt.
Radon CMake schaut nun nach den Compiler spezifischen intrinsics und setzt die dann für jedes Projekt.
Die Projekte können so alle Optimierungen aktivieren und mit rein bauen, die der Compiler unterstützt und zur Laufzeit muss dann dann Programm sich kümmern nur vom Host unterstützten instruction code aus zu führen, sonnst hat man ganz tolle Fehler.
Radon nutzt hier z.B. ein function dispatcher, welcher auf Basis von cpuid dann die Funktionspointer umbiegt.

Nach folgenden intrinsic suche ich aktuell in VC++
MMX SSE SSE2 SSE3 SSSE3 SSE41 SSE42 SSE4A AVX AVX2 AVX512 FMA3 FMA4 NEON AES XOP SHA CPUID
Bis auf AVX512 und SHA kann VC++ alle Intrinsics unterstützen, wenn man die entsprechende Version hat.

Für GCC und XCode hab ich es noch nicht gemacht.
Was als nächste auf der Liste steht, neben dem weiterem Ausbau von VGDB.

Radon framework
Beim aktualisieren vom Code für Linux und OSX hab ich mir mit der HashList ein eigentor geschossen, da ich SSE2 intrinsics benutzt hab.
Temporär hab ich ein SSE2NEON wrapper eingebunden, damit es auf dem PI geht und XCode beherrscht SSE2 für Mac und iOS benutz ich aktuell noch nicht aber da würde es mit dem Wrapper code gehen.
Der richtige weg ist natürlich Funktions Dispatcher ein zu bauen und passenden NEON Code zu schreiben.

Ich hab die Speicheralloziierung angepasst gehabt, dass auch Aligned Speicher alloziiert werden kann, dies hab ich nun auch für Linux nachgeholt. Sowas ist einfach notwendig, weil man ab und zu einfach bestimmte Alignments braucht, damit Code schnell läuft und die Systemfunktionen nicht immer das passende liefern.

Durch die Einführung von Compiler Intrinsic support im Radon CMake Framework kann ich nun beim bauen von Code schon bestimmte Teile ausklammer, das mach ich nun für sämtliche intrinsics, die nicht überall verfügbar sind, also die SIMD und CPUID Geschichte in meinem Fall.
Das war ein großes Problem für den gcc auf dem Raspberry Pi ^^

In den Unit Tests für die HashList hab ich bestimmte C++11 traits in verwendung, die aber von GCC älter als Version 5 nicht unterstützt werden und diese sind nun entsprechend von Guards umgeben, damit auch di e Unit Tests auf Raspberry Pi bauen.

Mit der Einführung weiterer Intrinsics hab ich auch mal den CPUID Code auf ein aktuelleren Stand gebracht und ihn mit einem Guard umgeben, damit der nicht auf ARM aktiv ist.

Visual GDB hab ich raus geworfen, weil das nun vom Radon CMake Framework gemacht wird.

Ich hab den OpenGL und X11 Konfiguration in CMake angepasst, damit auf nicht X11 fähigen Systemen, der entsprechende Code nicht gebaut wird.
Damit hab ich alle Linux Probleme beim kompilieren gelöst und somit kann ich nun Windows, Linux, wackelig OSX mit x86 und Linux ARM bauen.
Funktionieren tut der ganze kram aktuell auf keinen der Plattformen sauber, da ich mir das Threading zerschossen hab und das elementarer Bestandteil ist.

Als nächste muss ich die VGDB Geschichte zementieren, damit ich komplexe Projekte auf dem Pi zum laufen bekomme.
Danach will ich Smoketests für den System Layer schreiben. Dies hat den Grund, dass ich ein Verhalten für System Funktionen festlege und die Implementierung genau diese abdecken muss, damit ich sicherstellen kann, das der Highlevel Code korrekt funktioniert.
Damit planen ich die Probleme mit dem Threading und Sockets in Griff zu bekommen.
Dafür werde ich im Buildgrid auch passende Reports generieren.
Ich habe auch vor zukünftig die Reports in kompakte weise generieren zu lassen und als Website bereit zu stellen.
Die CppDepend Reports, sowie Doxygen kann ich 1zu1 rausgeben, da die dafür ausgelegt sind aber die Unit Test, Resource test und co müssen erst durch JUnit Aufbereitung.

_________________
"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: Radon Framework
BeitragVerfasst: Fr Mai 27, 2016 00:57 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Ich hab mal ein bisschen hausputz gemacht und dabei fest gestellt, dass ich ziemlich viele Baustellen beim Radon Framework offen stehen gelassen habe.
Das hab ich mir zum Anlass genommen mal eine Todo zusammen zu stellen und auch einige Designentscheidungen mal zu überdenken.

OpenGLVM nun GraphicMachine
Meine Größte Baustelle ist und war multithreaded rendering auf einer recht abstrakten Ebene.
Dafür hatte ich zuletzt mit einer OpenGL Virtual Machine gearbeitet, welche ich aus den Khronos Specs generiere.
Ein weiteres Problem aus der gleichen Ecke war das laden von OpenGL Funktionen und Extensions.

Nach reiflicher überlegung sieht mein Plan wie folgt aus.
Ich will GLEW komplett raus werfen, dann Code generieren, der aus einer Dispatcher Funktion und einem globalen Funktionspointer für jeden OpenGL Befehl besteht.
Der Dispatcher versucht die Funktion zu laden, also wenn man das erste mal die Funktion aufruft wird sie in den OpenGL Treiber gesucht und ersetzt den Funktionspointer. Sollte es scheitern, dann wird das Programm beenden.
Der Vorteil, dass jede Funktion sich selbst laden muss, erlaubt das optimieren beim Linken.
Bei GLEW und allen anderen Frameworks die ich bisher gesehen hab benutzt man Initialisierungs Funktionen, die dann die einzelnen Funktionpointer die OpenGL Funktionen zugewiesen werden. Es bleiben dadurch alle OpenGL Funktionen im Binary, weil jede Funktion in der Init Funktion verlinkt ist und deswegen braucht GLEW auch mal 200KB für alle OpenGL Funktionen mit bester Optimierung.
Wenn man die Funktionen einzeln Dispatcht wäre sie alle zusammen größer(weil ja noch eine zusätzliche dispatchfunktion existiert) aber beim Linken fliegen so ziemlich wieder alle raus, weil sie nicht verwendet werden.
Das Konzept mit der OpenGL VM hab ich geändert, es gibt keine Opcodes mehr für Register und OpenGL Funktionen.
Das ganze nenne ich nun GraphicMachine und funktioniert wie folgt.
Ich definiere ein Befehl für jede Funktionalität die ich brauch.
Code:
  1. struct GMTexSubImage2D
  2. {
  3.     static const GraphicMachineFunction GRAPHICMACHINEFUNCTION;
  4.     GLenum target;
  5.     GLint level;
  6.     GLint xoffset;
  7.     GLint yoffset;
  8.     GLsizei width;
  9.     GLsizei height;
  10.     GLenum format;
  11.     GLenum type;
  12.     const void * pixels;
  13. };

An erster steller steht immer ein Callback für die GraphicMachine, diese nimmt das Kommando und ruft den Funktionspointer mit dem Struct als parameter auf.
Code:
  1. void TexSubImage2DFunction(void* Data){
  2.     GMTexSubImage2D* data = reinterpret_cast<GMTexSubImage2D*>(Data);
  3.     glTexSubImage2D(data->target, data->level, data->xoffset, data->yoffset, data->width, data->height, data->format, data->type, data->pixels);
  4. }

Die Funktion kümmert sich um die extraktion der Daten und ruft die Funktionen auf die von der GraphicMachine ausgeführt werden sollen.

Das ganze hat den Vorteil, dass die GraphicMachine nun keine Konstanden, Funktionen oder was auch immer kennen muss wie vorher.
Der Grund wieso es nun auch nicht mehr OpenGLVM heisst, liegt daran, dass diese nun nicht mehr OpenGL spezifisch ist, weil sie ja alles ausführen kann, D3D, MESA, Custom Funktionen, ich muss ledeglich ein Struct erzeugen, wo an erster Stelle eine GraphicMachineFunction hinterlegt ist und dannn kann ich sie zuweisen.
Die GraphicMachine bekommt noch eine Queue, damit die einzelnen Threads die Befehle einreihen können.
Diese ist auch wesentlich schneller, weil vorher über ein riesen switch case konstrukt nochmal die ID überprüft werden musste.
Einziges Problem wie vorher sind die Rückgabewerte, da tendiere ich aktuell dazu, diese zu synchronisieren, also wenn man solche Funktionen verwendet, blockieren die.
Will man das nicht, baut man neue Kommandos, die einfach mehrere Funktionen beinhalten, man geht mehr zum High Level Code über.
Ich hab auch ein Shortcut gebaut.
Code:
  1. // Header
  2. void glTexSubImage2D(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLenum type,const void * pixels);
  3. // Source
  4. void glTexSubImage2D(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLenum type,const void * pixels){
  5.     auto* cmdBuffer = RF_Draw::GraphicMachineCommandBuffer::GetThreadInstance();
  6.     RF_Draw::GMTexSubImage2D* cmd = cmdBuffer->AddCommand<RF_Draw::GMTexSubImage2D>();
  7.     cmd->target = target;
  8.     cmd->level = level;
  9.     cmd->xoffset = xoffset;
  10.     cmd->yoffset = yoffset;
  11.     cmd->width = width;
  12.     cmd->height = height;
  13.     cmd->format = format;
  14.     cmd->type = type;
  15.     cmd->pixels = pixels;
  16. }

Wenn man den Header included, dann kann man normal OpenGL Befehle benutzen und unter der Haube werden die GraphicMachine Befehle generiert und in die Kommando Buffer gepusht. Bei glFlush oder wenn der Buffer voll ist wird er an die GraphicMachine Queue geschickt.

Ich werde allerdings nur den Stateless bzw. DSA Teil von OpenGL benutzen, weil der viel schneller und unkomplizierter ist.

Keyboard Handling
Vor kurzem hatte ich das Keyboard handling im Framework überarbeitet und diverse Funktionalitäten für Tasteneingabe hinzugefügt.
Allerdings hatte ich noch so einige Probleme und ein anderes Konzept, was mir aufgezeigt wurde, hat diese nicht, von daher werde ich da nochmal anpacken.

Linear algebra
Ich hab nun meine SIMD Klassen raus gekantet, den ganzen generierten OpCode und Macro kram.
Mein Konzept ist es diesen Code über JIT code generierung laufen zu lassen.
Ich habe dafür asmjit in mein Framework aufgenommen und einige Experimente gemacht.
Die Lib besteht aus assembler -> cpu instruction Konverter, sowie einen primitiven assembler Token Compiler.
Also keinen Parser, VM oder ähnliches, man muss jede asm instruktion aufrufen und die lib kümmert sich dann darum aus diesen den bytebrei zu erzeugen und ein bisschen zu optimieren. Man kann dann Funktionen generieren und aufrufen.
Mein Plan ist meine Lineare Algebra Klassen intern die asm Instruktionen auf zu rufen und dann daraus Funktionen generieren zu lassen.
So kann ich zur laufzeit auf CPU spezifischen Code bauen und spare mir die teuren und unnützen Register-Speicher hin und her kopiere, der durch intrinsics und SIMD enstehen.

Font System
Ich hatte ein Font System halb gebaut, welches die Systemsprache abfragen kann, welche Zeichen zu welchen Unicode Range gehören und das abfragen der Glyphen Informationen für die verfügbaren Schriftarten.
Was noch fehlt ist das Abholen von Bitmap und Vektordaten für die einzelnen Glyphen.

MeshGenerator2D und Text2D
Diese Klassen sind Util Klassen für das Rendern.
MeshGenerator2D ist in prinzip wie SVG eine Aneinanderreihung von Pfad Befehlen und am Ende kann man das ganze Tesselieren.
Das Tesselieren hab ich nie fertig gemacht und will ich nun noch machen.
Text2D soll aus den Vektordaten vom FontSystem und den Glyphen Infos Text generieren.

Tokenizer
Ich bastel aktuell ein einer Tokenizer Klasse, mit der ich ein Interpreter für ein Netzwerk Protokoll Format implementiere.
Der Tokenizer versteht von Hause aus CodePart und Delimiter Tokens.
Man sagt welche Zeichen Delimiter sind und der Tokenizer zerlegt erstmal den Input Stream.
Dann kann man Visitor registrieren, die alle nacheinander über jeden Token Laufen und diese ändern(Löschen, ersetzen, verschieben, neue erzeugen) können.
Dabei baut sich der Baum dann Stück für Stück in ein Command Tree um.
Sobald ich das Protokoll Format sauber damit parsen kann bin ich glücklich.

Refactoring
Ich hab einiges an Balast raus geworfen, der nicht fertig wurde oder nie richtig angefangen war.
Einigen Code hab ich auch verschoben, z.B. StringProcessor liegt nun im recht jungen Text namespace.
Meine Quaternion und Math Klasse hab ich raus geworfen, weil ich das schon QuatF32/64 Klassen sowie Integer, Float32/64 Klassen für Math ersetzt hatte.
Ich hab auch neue Client und Server Klassen geschrieben, damit ich sehr einfach Netzwerkcode schreiben kann.
mDNS und Service Discovery über mDNS hab ich auch fertig gemacht.

Ich tagge eine neue Release Version, wenn ich das fertig 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: Radon Framework
BeitragVerfasst: Mo Mai 30, 2016 21:24 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
OpenGL function and extension loading
Ich hab mein Code generator nun so weit, dass ich den nötigen OpenGL code generieren konnte und Windows extensions hab ich aktuell per hand geschrieben.
So konnte ich nun glew raus werfen und mal ein Vergleich zwischen GLEW sein batch dispatching und meinem on demand dispatching machen.
Dabei hab ich meine OpenGL Test Applikation, die den Code generiert in Release/Debug, mit und ohne glew getestet.

Bild

Binary Size
Debug:
2.292.736 Bytes(glew)
2.081.280 Bytes(direct dispatch)
= 211.456 Bytes
Release:
620.544Bytes(glew)
562.688Bytes(direct dispatch)
= 57.856 Bytes

Also spare ich ~10% und ein weiterer Vorteil ist, dass ich kein Spagat bei der OpenGL 3 und aufwärts Context erstellung machen muss.

Bestimmte Extensions sind bestimmten Context und Pixelformaten zugewiesen, wenn ich also alle Extensions mit einem 1.2 Context lade, dann müssen die Treiber laut Specs diverse Extensions nicht laden, die gegen 3.0 und höher geschrieben sind.
Diese dürfen erst geladen werden, wenn ich mit dem 1.2 Context einen 3.0 oder höheren Context erstellt habe.
Das gilt bei Windows aber bei X11 darf man jede Extension auch ohne Context laden, mann muss nur ein Screenhandle haben und er geht dann davon aus, dass dies das Zielpixelformat ist. Blöd, wenn das nicht der Fall ist, dann können wie bei Windows einige Extensions ins leere laufen, wobei bei Windows sogar alle Extensions ins leere laufen, wenn man das Pixelformat wechselt.


Dateianhänge:
RGLStudio.jpg
RGLStudio.jpg [ 74.13 KiB | 24019-mal betrachtet ]

_________________
"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: Radon Framework
BeitragVerfasst: Mo Jul 25, 2016 13:25 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Grafik
Ich hab mich noch ein bisschen mit OpenGL, VM, code generierung und so weiter beschäftigt und mich entschieden kein Grafikwrapper oder VM zu bauen.
Radon Framework hat einen generierten OpenGL Extension header, um GLEW zu ersetzen, OpenGL Context erstellung aber dann war es das auch.
Ich will auf lange Sicht nur die Context erstellung für OpenGL, Vulkan und DX12 einpflegen aber an die APIs will ich selber ned ran.
Jede Engine hat ein eigenen Stil und API, von daher möchte ich noch Util Code einbauen, der das Aufrufen von den API code aus anderen Threads ermöglicht.
Dafür muss ich eine Multiple Producer Single Consumer Queue implementieren und ein Example schreiben(das existiert schon teilweise im Fast Entity Component System Repo).
Ich wollte ja ursprünglich eine VM bauen, die sich darum kümmert aber die Handhabung von Variablen und Rückgabewerten ist recht umständlich und kostet viel CPU Zeit.
Wenn man diese nicht korrekt verwendet ist es von sehr großem Nachteil und man sollte davon ausgehen, dass man es falsch verwendet.
Der Aufwand sowas noch für Vulkan zu pflegen und vieleicht noch DX12 wäre der horror und wahrscheinlich würde es wenig Akzeptanz finden.

Debug UI und Util Klassen werde ich auf keine API schreiben, sondern Triangle Suppe und Meta Infos generieren und man kann dann diese nutzen.
imGUI ist ein wunderschönes Beispiel. Ich wollte es auch erst aufnehmen aber es beherrscht kein Multi Window.
Aktuell plane ich ledeglich eine Docking, Button, Graph und Label Komponente, mehr brauch ich bisher nicht zum debuggen und es soll ja keine UI Lösung werden.

Netzwerk
ServiceResponder und ServiceDiscovery funktionieren nun mit einigen der DNS Typen, für mDNS-SD brauche ich ledeglich SRV und eventuell noch PTR.
Ich kann nun Devices wie unseren Drucker, NAS und die ganzen Apple Produkte im Netzwerk erkennen.
ServiceResponder erlaubt eigene Services zu publizieren.
KryptonBuild nutzt es, damit ich ohne Konfiguration ein Buildgrid aufbauen kann aber auch 2 Projekte auf Arbeit nutzen es.

Multi threading
Ich hab den ThreadPool überarbeitet, completion tasks sind rausgeflogen. Diese sind von Microsoft für blocking IO gedacht gewesen aber das ist ziemlich blöd und async ist die bessere wahl.
Dabei hab ich auch noch die Queue umgebaut und gefixt. Die Queue ist nun eine MPMC Queue und ich plane noch die anderen 3 Arten mit der Zeit zu implementieren.
Der Threadpool weist nun jeden Logischen Prozessor einen Thread zu und gibt ihnen auch eigene IDs.
Ich habe zu den Core IDs nun auch Unique IDs hinzugefügt, der Unterschied liegt darin, dass Core IDs von 0 bis N-1 gehen und damit bei mehr als einen Prozessor mehrfach vorkommen.
Die Unique ID garantiert, dass es keine dopplung gibt.

Ich hab die Threaderstellung so umgebaut, dass ich vor dem Start des Threads die Affinity Mask setzen kann.
Windows will dies erst, wenn man den Thread erstellt hat und die Thread Funktion aufgerufen wurde, Linux und Unix erlauben dies vorher.
So hab ich die Klasse umgebaut, dass man vorher die Maske setzen kann und beim Start dann diese übernommen wird, bevor die Run Methode aufgerufen wird.
Damit werden die Tasks immer auf den einzelnen Kernen verarbeitet.

API
Ich hab IApplication, IWindow und ICanvas3D umbenannt in Abstract..., da sie nicht wirklich Interfaces sind.

Die BitArray Klasse tut nun alle Bits mit 0 initialisieren, dass kostet zwar Konstruktionszeit aber ich hatte daran nicht gedacht und so ein Bug gebaut.

Ich hab die Mathe Header aufgeräumt, Matrix Klasse auf ein aktuellen Stand der API gebracht und auch die Defines gegen Alias ersetzt.

Ich benutze nun "user defined string literals"_RFS um den Code kürzer und übersichtlicher zu machen. Als Seiteneffekt kann man sicher sein, dass die schnellere API von der String Klasse verwendet wird, da zur Compile time schon die größe von string literals bekannt ist und somit der bounding check und Terminierung suchen weg fallen. Bei Strings die größer als der Lokale Puffer sind, wird automatisch auf Unmanaged memory gestellt, damit wird nur der Pointer zugewiesen und keine Kopie erstellt. string literals können im Programmverlauf nicht verschwinden, weil sie globale unveränderbare Daten sind.

Die StackAllocator Klasse ist nun Thread safe. Push kann von mehreren Thread gleichzeitig passieren und wird über ein Spinlock mit den anderen Funktionen synchronisiert.
Pop kann nur von einem Thread gleichzeitig aufgerufen werden und wird auch erst aufgerufen, wenn alle Push Anfragen durch sind.
Dies macht Push schneller und sollte man Push und Pop auf einem Thread brauchen, dann sollte man eher ein Thread Lokalen StackAllocator verwenden.

Es gibt nun eine Screenshot Funktion, welche imgui haben wollte.

AutoPointerArray hat nun eine Methode New, welche den aktuellen Speicher frei gibt und neuen Alloziiert. Bisher musste ich immer ein neues AutoPointerArray Objekt bauen und zuweisen, was nicht gerade leserlich noch Speicherfreundlich ist.

Neue kleine Projekte/Examples
Ich hab ein paar kleine Projekte aufgemacht, damit ich die API vom Framework testen kann, sowie auch einige Tools für das einfachere Arbeiten anbieten kann.
KryptonBuild ist ein minimaler Build Service, welcher sich mit allen Instanzen in einem Lokalen Netzwerk bekannt macht und dann Builds triggern soll.
Ich ärger mich ständig mit Jenkins, weil es eine sehr große Java Entwicklungsumgebung braucht, es beim start gute 300MB-1GB Speicher braucht und total umständlich ist mehrere Server zu verbinden.
KryptonBuild tut sich automatisch vernetzen, was es schon in der aktuellen Version kann(mDNS-SD).
Es soll ein minimalen HTTP Server starten, der zum Kommunizieren ist.
Um raus zu bekommen ob ein Server ein Linux ist und ein passenden Compiler hat kann man ein HTTP Query schicken und bekommt die Daten zurück, dann soll noch der Sync der Daten darüber passieren und die Kommandozeile die ausgeführt werden soll.
Das Ziel von dem Tool ist, dass man es lokal startet und per Parameter dann noch remote ein Build für andere Platformen und Compiler triggern kann.
Wenn ich am Radon Framework arbeite möchte ich eigentlich vor dem Commit noch mein Code auf Linux, OSX, Raspberry, NV Shield und co testen und das möglichst nicht manuell.
Es ist eine Form von Continuous Integration aber bei weitem nicht so umfangreich wie z.B. Jenkins, wo man Authentifizierung, Asset managment und so weiter braucht.

KryptonBuild nutzt http parser von nodejs, damit ich mir einiges an Arbeit sparen kann.
Beim synchronisieren denke ich aktuell an Brotli zum packen und xxhash um die veränderten Datein zu erkennen.

Ein weiteres kleines Projekt/Example ist Organesson.
Das ist mehr ne kleine Spielwiese, wo ich die Lineare Algebra und Multithreading vom Radon Framework ausbaue und gleichzeitig noch ein Mesh processing Tool baue.
Das Ziel ist eine stark abgespeckte hobby Variante von Simplygon und Umbra.
Beide Tools nutzen ein Software Rasterizer, sowas in gut zu schreiben ist ein riesen Aufwand, den ich nich leisten kann und die Grundlage für einige Features sind.
Was ich aber machen kann sind Silverlines erkennen, Triangles besser sortieren, Bounding Box und Sphere, sowie eine Hull zu errechnen.
Eine Datenbank auf zu bauen, wo die sichtbaren Objekte von einem Punkt aus erfasst werden und ein Frustum culling.
LOD Stufen bauen ist auch im Rahmen des Möglichen und sehr nützlich.
Das tollste wird allerdings eine Asset streaming Liste sein, anhand der LOD und Metadaten von einem Punkt weiß ich welche Assets gebraucht werden und die kann man dann abfragen und vorab laden.
Aktuell verwernde ich assimp und werde noch Brotli, OpenCascade hinzu ziehen.

Radon CMake Framework
Das Framework nutze ich ja in allein meinen Projekten und entsprechend passieren hier auch immer wieder Neuerungen und Bug fixes.
Ich habe das auschecken von spezifischen Revisionen eingebaut.
Wenn man nur ein SVN oder Git Repo angibt, dann prüft cmake bei jeden Konfigurieren die Repos auf den neuesten stand und lädt updates.
Sollte man eine spezifische Revision laden, dann entfällt der Update Schritt und die checkouts bekommen extra Ordner.
So kann man schnell zwischen 2 Revisionen hin und her switchen, während der Entwicklung und brauch keine Angst haben, dass man sein Projekt kaputt macht, weil ein Update von einer externen Lib nicht mehr korrekt baut.
Git und Subversionen bieten Module und server seitige sub repos, die aber beim checkout mit geladen werden und wenn man Repos abhängig von Optionen haben will, dann ist meine Lösung die bessere.

Lineare Algebra
Ich hatte mich noch viel mit SIMD für Vektor und co beschäftigt.
Mein Gedanke war erst alle Operationen zu sammeln und dann mit JIT in Blöcke zusammen fassen, damit ich nicht ständig die Variablen zwischen SIMD und normalen Registern hin und her schiebe.
Ich hab allerdings eine viel einfachere Alternative entdeckt.
CGal benutzt Meta programming um dies zu erreichen.
Also Vektor A + B wird zu VektorOperation mit den Werten Addition, A und B.
C = A + B wird also zu Assign mit C und VektorOperation.
Nun wird in Assign das ganze zu SIMD Code aufgelöst.
Der Code liest sich einfacher als Boost Meta Programming code aber die schreiben selber, dass es recht aufwändig ist und man schnell Fehler machen kann.

Die Jit wäre Fehlerunanfälliger, kann auch CPU Extension nutzen, die nicht vom Compiler supported werden(VC++ ist hier der Wackelkandidat) aber funktioniert auf iOS ned(es verbietet Jit, auch ein Grund wieso C# und Java so grottig lahm da sind).
Ich hatte zum testen der ganzen Grammer und Linearen Algebra angefangen eine mini VM zu bauen, die den Bytecode interpretiert.
So kann ich sehr einfach die Bytecode generierung und Optimierung debuggen und das Läuft auch auf iOS.
Ich hab das erstmal pausiert um ein bisschen länger drüber nach zu denken, was hier der optimale Weg ist.

Intel hat ein eigenen code generator mit clang gebaut, der optimierten code raus wirft und dann mit kompiliert.
AMD fackelt es mit OpenCL ab und NVidia nutzt Cuda.
Das sind Dimensionen, da möchte ich nicht hin kommen.

_________________
"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: Radon Framework
BeitragVerfasst: Mi Jul 27, 2016 14:23 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Ich hab heute per zufall ein Software rasterizer gefunden.
Vadim Shcherbakov blog enthält ein stark optimierten rasterizer für occlussion culling ausgelegt.
Hodgmen von gamedev.net hat den in seine Engine übernommen und überarbeitet.
Der ist so brachial einfach, dass ich das zu 99% out of the box nehmen kann und somit doch noch um einige mehr machbar ist.

Ich hab bisher 2 Ansätze in Engines dazu gesehen, einmal wird beim rendern für jedes Objekt noch das Physik Mesh gerendert und im nächsten Frame nutzt man die Daten vom vorigen, was die Wartezeit auf die GPU sinnvoll ausnutzt aber halt 1 frame hinterher hänkt und bei schnellen bewegungen zu ungewünschten Effekten führt.
Man packt zwischen der Physik und dem Rendern noch andere Schritte und führt dort auch das rendering des Physik Mesh aus und gibt den Render State nur noch die Objekte die sichtbar sind und verkürzt somit das culling und schickt verdeckte Objekte garnicht erst ins Rennen.
Killzone3 und Battlefield3 haben dazu mal Presentationen gemach und haben sich sogar noch mit Transparenz und simplifizierung von Mesh beschäftigt.
Beide nutzten das reduzierte Physik Mesh bzw. Hull Objekte, wenn die Objekte kein Physik Collider haben.
Als erstes hackt man alle Objekte weg, die nicht in der Viewrange sind, dazu nutzt man in der Regel Spacial Systems, wo man nicht Objekte einzeln raus kickt sondern einen Bereich, wo die Objekte zugewiesen sind. Dann kickt man die Objekte raus, die nicht im Frustum sind, dann wirft man die über den Software Occlussion verdeckten Objekte raus und schickt den Rest an den Renderer.
Battlefield lässt dabei alle Transparenten Objekte aus dem Rasterizer Prozess, weil sonnst ein Zaun alles dahinter verdeckt und raus culled.
Ein extra Fragment Shader, um die Alpha Map zu sampeln hatte sich als zu teuer raus gestellt.

Ich hab auch die http parser lib von nodejs nun angebunden und nun muss ich noch die paar Zeilen für ein korrekten http1.1 response schreiben und ich kann zwischen den nodes kommunizieren.
Das ging wesentlich einfacher als erwartet. Leider hat NodeJS keine lib für das erzeugen von Response Messages geschrieben, das liegt direkt im Projekt Code.

_________________
"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: Radon Framework
BeitragVerfasst: Mo Aug 01, 2016 02:24 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Was für ein produktives Wochenende und ich hab mit nur 1 Kaffee und Redbull gedoped :)

Der commit ist noch nicht raus aber ich hab ein neues Allocation System geschrieben.
Da hab ich mich von Lumberyard(genaugenommen Amazon C++ Framework -> namespace Core), Molecular Matter, Bitsquid(wurden von Autodesk gekauft) und meinem alten Allokator Klassen bedient.

Code:
  1. class AllocatorBase
  2. {
  3. public:
  4.     virtual void* Allocate(const RF_Type::Size Bytes, const RF_Type::UInt32 Flag = 0)=0;
  5.     virtual void Free(void* Pointer)=0;
  6.     /// Try to resize as closed as possible to the specified size of NewSize.
  7.     virtual RF_Type::Size Resize(void* Pointer, const RF_Type::Size NewSize) = 0;
  8.     /// Try to resize to the specified size of NewSize if not possible then
  9.     /// allocate a new block and move the data.
  10.     virtual void* ResizeOrAllocate(void* Pointer, const RF_Type::Size NewSize) = 0;
  11.     /// Return the size of an user allocation.
  12.     virtual RF_Type::Size AllocationSize(void* Pointer) = 0;
  13.     /// Return the amount of bytes which are allocated.
  14.     virtual RF_Type::Size GetAllocatedBytes()const = 0;
  15.     /// Return the amount of bytes which are used for allocation purpose.
  16.     virtual RF_Type::Size GetUsedBytes()const = 0;
  17.     /// Return the amount of bytes which the smallest allocation can handle.
  18.     virtual RF_Type::Size GetAllocationGranularity()const = 0;
  19. };

Das ist das Interface, was alle Allokatoren ableiten.
Ich hab für meine Arbeit nun 2 Allokator geschrieben, die auf den neuen System laufen.
Code:
  1. /// Use this class only for large chunk allocation at the bottom of an allocator stack.
  2. /// This allocator allocates blocks, use Allocate and AllocationSize to get the real size.
  3. /// You can obtain the minimal block size with GetAllocationGranularity or RF_SysMem::GetRegionSize.
  4. class SystemAllocator : public AllocatorBase
  5. {
  6. public:
  7.     SystemAllocator();
  8.  
  9.     virtual void* Allocate(const RF_Type::Size Bytes, const RF_Type::UInt32 Flag = 0) override;
  10.     virtual void Free(void* Pointer) override;
  11.     /// Try to resize as close as possible to the specified size of NewSize.
  12.     /// Resize has no effect this have to be implemented on higher level!
  13.     virtual RF_Type::Size Resize(void* Pointer, const RF_Type::Size NewSize) override;
  14.     /// Allocate a new block and move the data.
  15.     virtual void* ResizeOrAllocate(void* Pointer, const RF_Type::Size NewSize) override;
  16.     /// Return the size of an user allocation.
  17.     virtual RF_Type::Size AllocationSize(void* Pointer) override;
  18.     /// Return the amount of bytes which are allocated.
  19.     virtual RF_Type::Size GetAllocatedBytes() const override;
  20.     /// Return the amount of bytes which are used for allocation purpose.
  21.     virtual RF_Type::Size GetUsedBytes() const override;
  22.     /// Return the amount of bytes which the smallest allocation can handle.
  23.     virtual RF_Type::Size GetAllocationGranularity() const override;
  24. private:
  25.     RF_Type::AtomicInt64 m_Size;
  26.     static RF_Type::Size m_BlockSize;
  27. };
  28.  
  29. /// Use this class only for allocation of the same size and alignment.
  30. /// It reserves a large chunk from the specified arena and split it up.
  31. /// By default it's using the SystemAllocator which provide large chunks with no
  32. /// additional footprint.
  33. class PoolAllocator: public AllocatorBase
  34. {
  35. public:
  36.     PoolAllocator(const RF_Type::Size BlockSize, const RF_Type::Size Alignment,
  37.         AllocatorBase* Arena = nullptr);
  38.  
  39.     virtual void* Allocate(const RF_Type::Size Bytes, const RF_Type::UInt32 Flag = 0) override;
  40.  
  41.     virtual void Free(void* Pointer) override;
  42.     /// Return the size of an user allocation.
  43.     virtual RF_Type::Size AllocationSize(void* Pointer) override;
  44.     /// Return the amount of bytes which are allocated.
  45.     virtual RF_Type::Size GetAllocatedBytes() const override;
  46.     /// Return the amount of bytes which are used for allocation purpose.
  47.     virtual RF_Type::Size GetUsedBytes() const override;
  48.     /// Return the amount of bytes which the smallest allocation can handle.
  49.     virtual RF_Type::Size GetAllocationGranularity() const override;
  50. protected:
  51.     /// The size of the allocation is specified with the constructor and unchangeable.
  52.     virtual RF_Type::Size Resize(void* Pointer, const RF_Type::Size NewSize) override;
  53.     /// The size of the allocation is specified with the constructor and unchangeable.
  54.     virtual void* ResizeOrAllocate(void* Pointer, const RF_Type::Size NewSize) override;
  55. private:
  56.     struct ObjList;
  57.     ObjList* m_Chunks;
  58.     AllocatorBase* m_Arena;
  59.     RF_Type::AtomicInt64 m_Used;
  60.     RF_Type::AtomicInt64 m_Reserved;
  61.     RF_Type::Size m_BlockSize;
  62.     RF_Type::Size m_Alignment;
  63.     RF_Type::Size m_BlockOffset;
  64.     RF_Type::Size m_ChunkCount;
  65. };

Ich muss natürlich noch einige Tests schreiben aber der Pool allocator basiert auf existierenden Lockfree Code und sollte mit meinen ersten Papier und Debugtests lock free, thread-safe und unglaublich schnell sein.
Der System Allocator sollte auch lock free, thread-safe und unglaublich schnell sein, der hat 2 Races, der eine wird mit einem AtomicInt64 erschlagen und das andere ist OS API(Windows==VirtualAlloc).
Auf mein Windows allokiert der SystemAllocator 64kb chunks, weil das die Größe der kleinen Speicherblöcke ist, die Windows intern verwaltet.
Schneller als mit VirtualAlloc kann man nicht unter Windows Speicher bekommen aber man hat halt auch nahezu keine Features.
Egal was man macht, Windows gibt ein 64kb Block zurück, was du davon nicht nutzt ist fragmentiert, der Block ist immer PageSize aligned, also 4096Byte.
Zum vergleich zu Heap, malloc und co fallen keine weiteren Metadaten an, unter VirtualAlloc liegt das Memory System vom Kernel und für die Daten reserviert das OS schon beim booten sein Speicher ^^
Es hat wesentlich weniger Probleme mit multi threading.
Allerdings sollte man den Allocator nicht nehmen um seine Strings oder einzelnen Objekte zu alloziieren, 64kb Speicher für ein 16Byte Objekt zu holen ist dann doch ein bisschen verschwenderisch.
Daher hab ich auch ein Pool Allokator gebaut, naja eigentlich hab ich mehrere gebaut, weil ich mehrere Anläufe brauchte ^^
Der jetzige holt sich standardmässig ein 64KB Block vom SystemAllocator, packt am Anfang die Metadaten und danach kommen soviele Blöcke wie möglich.
Wenn die nicht mehr ausreichen oder ein Thread gerade auf die Liste zugreift, dann wird ein weiterer Block geholt und mit dem ersten verlinkt.
Im schlimmsten Fall hat man also bei 8 Kernen 8 Listen mit je einem Objekt in nutzung.
Das ganze wäre also sehr verschwenderisch, wenn man nur mit einer Handvoll Objekte arbeitet.
Man kann aber einen Allocator einem anderen übergeben und dann holt er sich sein Speicher von diesen, statt dem SystemAllocator.

Ein BuddyAllocator wäre hier in naher Zukunft bestimmt praktisch.
Aber erstmal lebe ich mit dem hohen Speicherverbrauch an den performance kritischen Punkten.

Lock free MPSC Queue
Ich hab eine multiple producer single consumer(MPSC) queue implementiert, welche auf auf diesen Code basiert.
Code:
  1. /** @brief This class is a lock free queue which can grow and shrink.
  2. *
  3. * - first-in-first-out(FIFO)
  4. * - multiple producer and single consumer(MPSC)
  5. * - dynamic size(link list)
  6. *
  7. * If you need other producer and/or consumer combinations then look for the
  8. * specialized implementation.
  9. */
  10. class TypelessDynamicQueueMPSC
  11. {
  12. public:
  13.     /// You can pass a custom Allocator instance to exchange the standard
  14.     /// memory allocation. The specified instance will not be destroyed on destruction.
  15.     TypelessDynamicQueueMPSC(RadonFramework::Memory::AllocatorBase* Arena = nullptr);
  16.     ~TypelessDynamicQueueMPSC();
  17.  
  18.     // Disable copy and assign for this class.
  19.     TypelessDynamicQueueMPSC(const TypelessDynamicQueueMPSC& Copy) = delete;
  20.     TypelessDynamicQueueMPSC& operator = (const TypelessDynamicQueueMPSC& Other) = delete;
  21.  
  22.     /// Removes all objects from the queue.
  23.     void Clear();
  24.  
  25.     /// Enqueue a pointer to the end of the queue. Take care that the pointer
  26.     /// is not managed by this class.
  27.     void Enqueue(void* Data);
  28.     /// Enqueue a pointer to the end of the queue. Take care that the pointer
  29.     /// is not managed by this class.
  30.     void Enqueue(const void* Data);
  31.     /// Remove the last pointer of the queue. If empty it return nullptr.
  32.     void* Dequeue();
  33.  
  34.     /// Return true if the queue was empty at call else false.
  35.     RF_Type::Bool IsEmpty()const;
  36.  
  37.     RadonFramework::Memory::AllocatorBase& GetArena()const;
  38. private:
  39.     RF_Mem::PoolAllocator m_NodeAllocator;
  40.     struct Node;
  41.     Node* volatile m_Head;
  42.     Node* m_Tail;
  43.     Node* m_Stub;
  44.     RadonFramework::Memory::AllocatorBase* m_Arena;    
  45. };

Ich benutze den PoolAllocator und verwende den SystemAllocator als Arena, damit werden die Nodes erzeugt und freigegeben.
Es gibt noch eine Template Version, welche ein weiteren PoolAllocator mit der Blockgröße von Typ T nimmt.
So kann ich dann in meinem SessionServer folgendes nutzen.
Code:
  1. RF_Collect::HashList m_Sessions;
  2. RF_Con::DynamicQueueMPSC<RF_Type::UInt32> m_SessionsWithResponses;

HashList hatte ich vor einiger Zeit geschrieben und ist auf UInt32 Keys ausgelegt und enthält die Pointer auf die Sessions.
m_SessionsWithResponse enthält alle Keys, wo Sessions noch ausstehende Pakete haben, die raus müssen.
Wenn also das nächste Update aufgerufen wird, tut sich die Funktion ein paar Keys raus nehmen und die Pakete verschicken.
Während der ganzen Zeit können alle Threads weitere Keys hinzufügen, deswegen auch eine MPSC Queue ;)
Das ganze funktioniert so, dass ich die Sockets in einem SelectObjectCollector sammel und sollte einer oder mehrere Socket zwischendurch redebedarf entwickelt haben, dann tut das select in der Update Funktion anschlagen und die Daten in die Sessionstreams werfen.
Dort werden die Daten dann validiert, verworfen oder weiter verarbeitet.
Code:
  1. RF_Net::StreamStatus ProtocolLogic::Process(RF_Net::NetworkStream<RF_IO::MemoryCollectionStream>& Stream)
  2. {
  3.     RF_Net::StreamStatus result = RF_Net::StreamStatus::ThrowAway;
  4.  
  5.     RF_Type::Size dataSize = 0;
  6.     const RF_Type::UInt8* data = Stream.Peek(dataSize);
  7.     if(data)
  8.     {
  9.         auto processedBytes = http_parser_execute(&m_Parser, &m_Settings,
  10.             reinterpret_cast<const char*>(data), dataSize);
  11.         if (IsComplete)
  12.         {
  13.             result = RF_Net::StreamStatus::Dispatch;
  14.             Stream.Seek(processedBytes, RF_IO::SeekOrigin::Current);
  15.         }
  16.     }
  17.     return result;
  18. }
  19.  
  20. void ProtocolLogic::Dispatch(RF_Mem::AutoPointerArray<RF_Type::UInt8>& Packet)
  21. {  
  22.     // we're not interested in the body which means no further processing of the packet
  23.  
  24.     if(Request.GetMethod() == RF_HTTP::Method::Get)
  25.     {
  26.         RF_Mem::AutoPointerArray<RF_Type::UInt8> data;
  27.         auto file = RF_IO::Directory::WorkingDirectory()->SubFile(Request.GetLocation());
  28.         if(file && file->Exists())
  29.         {// found it on the disk
  30.             data = file->Read();
  31.         }
  32.         else
  33.         {
  34.             // KryptonBuild specific http communication e.g. system infos, compiler infos, build queue
  35.             data = RF_Pattern::Singleton<DynamicHTMLContent>::GetInstance().Execute(
  36.                 Request.GetLocation());
  37.         }
  38.  
  39.         if(data)
  40.         {
  41.             static auto Text = "HTTP/1.1 200 OK\n"
  42.                 "Date: Thu, 19 Feb 2009 12:27:04 GMT\n"
  43.                 "Server: Apache/2.2.3\n"
  44.                 "Last - Modified: Wed, 22 Jul 2009 19 : 15 : 56 GM"
  45.                 "Content-Type: text/html\n"
  46.                 "Content-Length: %d\n"
  47.                 "Connection: keep-alive\n"
  48.                 "\n"_rfs;
  49.             auto text = RF_Type::String::Format(Text, data.Size());
  50.             RF_Mem::AutoPointerArray<RF_Type::UInt8> response(text.Size()+data.Size());
  51.             response.Copy(text.c_str(), text.Size()-1);
  52.             response.Copy(data.Get(), data.Size(), text.Size()-1);
  53.             SendResponse(response);
  54.         }
  55.         else
  56.         {
  57.             auto text = "HTTP/1.1 404 Not Found\n"
  58.                 "Date: Thu, 19 Feb 2009 12:27:04 GMT\n"
  59.                 "Server: Apache/2.2.3\n"
  60.                 "Content-Type: text/html\n"
  61.                 "Content-Length: 0\n"
  62.                 "Connection: keep-alive\n"
  63.                 "\n"_rfs;
  64.             RF_Mem::AutoPointerArray<RF_Type::UInt8> response(text.Size());
  65.             response.Copy(text.c_str(), text.Size());
  66.             SendResponse(response);
  67.         }
  68.     }    
  69. }

Der SessionStream arbeitet in 2 Schritten, der erste ruft Process auf und dort kann man je nach Protokoll entweder grob validieren oder halt komplett parsen.
In dem Beispiel tue ich den Header komplett validieren und den Body einfach mal ignorieren.
Dann wird der geparste Teil aus dem Stream geschnitten und über Dispatch als nun unabhängiger Speicherblock geliefert.
Hier kann ich z.B. nun den Block in den Threadpool schieben und sehr aufwändige Aufgaben verrichten, worauf ich hingearbeitet habe, weil der Dateizugriff langsam ist :\ .
Dafür muss SendResponse aber thread-safe sein und ich mag Lock-Free und eigentlich war der ganze Kram eh schon auf meiner Todo ^^

HTTP
Ich implementiere aktuell ein minimalistischen Webserver, damit meine Build Nodes im Netzwerk miteinander reden können.
Dank der ganzen tollen Sachen, die ich Implementiert hab, sowie die Erweiterungen an PacketStream, SessionServer und Server Klasse kann ich aktuell mein Working Directory mit dem Browser browsen ^^
Bild

Ich will ein Dashboard basteln, welches meine Lokale Instanz fragt, wen es alles kennt und diese dann fragen was für Hardware, Compiler, OS, system load, build queue die haben.
Dank mDNS-SD kennen die sich alle automatisch und mit einem gemeinsammen Web Interface können die auch untereinander problemlos reden.
Man schreibt dann einmal mit jQuery, Bootstrap und REST das Dashboard, packt den spezifischen Teil über die DynamicHTMLContent Klasse in JSON und schon kann man Daten synchronisieren, Builds triggern und man braucht ledeglich ein http request oder demnächst eine KryptonBuild Instanz mit Parameter starten, um sein Build Grid zu nutzen.


Dateianhänge:
http.JPG [86.09 KiB]
Noch nie heruntergeladen

_________________
"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: Radon Framework
BeitragVerfasst: Di Aug 02, 2016 22:01 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
HTTP Server

Request http://192.168.0.11:57630/dyninfo
Code:
  1. {"mfree":9884590080, "musage":"9752576", "mpeak":9752576, "pagefault":2479}

Es sind noch 9,2GB Ram frei, der Prozess braucht aktuell 9MB Speicher und genauso viel war sein höchster verbrauch, sowie 2479 mal gab es nen page fault(ich glaub das nehme ich wieder raus).

Request http://192.168.0.11:57630/info
Code:
  1. {"os":"Microsoft Windows NT", "memarch":64, "lp":4, "mem":17159053312}

OS ist Windows NT also alles nach 98se, ich glaub das ich da später auf einfachere Varianten, wie Windows, Linux, Unix, OSX, iOS, Android wechseln werde und nich die Versionsnummer anbiete.
Speicherarchitektur vom OS ist 64Bit, es wurden 4 logische Prozessoren durchgereicht und es gibt 16GB Ram.
Hier fehlt noch CPU features , die in einem Bitset zur verfügung stehen aber ich sehe aktuell noch keine Verwendung und müsste erst noch eine ToString Funktion schreiben.

Man ruft also einmal info auf und dann in einem Interval dyninfo und kann dann Graphen plotten oder was auch immer mit den Daten in HTML und Javascript machen.
Leider gehen meine Fähigkeiten von Websiten machen nicht über Bootstrap und JQuery Beispiele zusammen zu kopieren.

Hier mal der Code, den man für die Querys braucht.
Code:
  1. RF_Mem::AutoPointerArray<RF_Type::UInt8> GetStaticSystemInfos(const RF_Type::String& Parameter)
  2. {
  3.     RF_Mem::AutoPointerArray<RF_Type::UInt8> result;
  4.     const RF_Sys::OperatingSystem& platform = RF_SysEnv::OSVersion();
  5.     auto memArch = RF_SysEnv::MemoryArchitectureOfOS();
  6.     auto lp = RF_SysHardware::GetAvailableLogicalProcessorCount();
  7.     RF_SysHardware::ProcessorFeatureMask features;
  8.     RF_SysHardware::GetLogicalProcessorFeatures(features);
  9.     auto mem = RF_SysHardware::GetPhysicalMemorySize();
  10.  
  11.     auto text = RF_Type::String::Format("{\"os\":\"%s\", \"memarch\":%u, \"lp\":%u, \"mem\":%llu}"_rfs,
  12.         platform.PlatformString().c_str(),
  13.         memArch == RF_Sys::MemoryArchitecture::_64Bit ? 64 : 32,
  14.         lp, mem);
  15.     result.New(text.Size()-1);
  16.     result.Copy(text.c_str(), text.Size()-1);
  17.     return result;
  18. }
  19.  
  20. RF_Mem::AutoPointerArray<RF_Type::UInt8> GetDynamicSystemInfos(const RF_Type::String& Parameter)
  21. {
  22.     RF_Mem::AutoPointerArray<RF_Type::UInt8> result;
  23.     auto freeMem = RF_SysHardware::GetFreePhysicalMemorySize();
  24.     auto pid = RF_SysProc::GetCurrentProcessId();
  25.     RF_SysProc::MemoryInfo memInfo;
  26.     RF_SysProc::GetMemoryInfo(pid, memInfo);
  27.  
  28.     auto text = RF_Type::String::Format("{\"mfree\":%llu, \"musage\":\"%llu\", \"mpeak\":%llu, \"pagefault\":%llu}"_rfs,
  29.                                         freeMem, memInfo.MemoryUsage, memInfo.PeakMemoryUsage, memInfo.PageFaultCount);
  30.     result.New(text.Size() - 1);
  31.     result.Copy(text.c_str(), text.Size() - 1);
  32.     return result;
  33. }
  34.  
  35. // Konfigurationsphase
  36. RF_Type::UInt32 key = CalculateHash("/info"_rfs);
  37. RF_Pattern::Singleton<DynamicHTMLContent>::GetInstance().Content.Add(key, GetStaticSystemInfos);
  38. RF_Type::UInt32 dynKey = CalculateHash("/dyninfo"_rfs);
  39. RF_Pattern::Singleton<DynamicHTMLContent>::GetInstance().Content.Add(dynKey, GetDynamicSystemInfos);


Wenn ich auf ein Get Request kein Datei finde, dann gucke ich in den Cache/DynamicHTMLContent und gebe dann die Daten als Result zurück.
Das bedeutet, dass in den hinterlegten Funktionen ich den query und/oder fragment Teil von der Uri verarbeiten kann(REST lässt grüßen).
Code:
  1.         RF_Mem::AutoPointerArray<RF_Type::UInt8> data;
  2.         auto file = RF_IO::Directory::WorkingDirectory()->SubFile(Request.GetLocation());
  3.         if(file && file->Exists())
  4.         {
  5.             data = file->Read();
  6.         }
  7.         else
  8.         {
  9.             // Extract the path from the location string and pass the rest as parameter.
  10.             auto requestList = Request.GetLocation().Split("?#"_rfs);
  11.             auto key = CalculateHash(requestList[0]);
  12.             RF_Type::String query;
  13.             if(requestList.Count() > 1)
  14.             {
  15.                 for (RF_Type::Size i = 0; i < requestList.Count(); ++i)
  16.                 {
  17.                     query += requestList[i+1];
  18.                 }                
  19.             }
  20.             // KryptonBuild specific http communication e.g. system infos, compiler infos, build queue
  21.             data = RF_Pattern::Singleton<DynamicHTMLContent>::GetInstance().Execute(key, query);
  22.         }


Directory sync
Es wird nun Zeit remote sync einzubauen, das wird lustig.
Ich plane ein Build wie folgt abzuwickeln.
  • Archive vom Zielordner erzeugen(ob LZMA oder brottli bin ich noch nicht festgelegt)
  • Alle aktiven Nodes angucken und allen passenden ein build request schicken(.../buildjob?id=8)
  • Die nodes geben dann ein reuqest an den lokalen Server(.../job?id=8) um infos zum Job zu bekommen(z.B. archive Ort)
  • archive requesten
  • entpacken
  • das machen, was in dem job info request von vorher stand
Das ist ziemlich primitive und ein Diff wäre schöner aber brauch auch mehr Arbeit.
Später kann man ja viel mehr machen, z.B. ein Setup Phase, wo die Pakete installiert werden, die in den job infos stehen.

_________________
"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: Radon Framework
BeitragVerfasst: Fr Aug 05, 2016 00:03 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Directory Sync
Ich hab an dem Sync gewerkelt und gegrübelt.
Um ein Build zu triggern hab ich für /build eine DynamicHTMLContent Funktion hinterlegt, welche noch den Query interpretiert.
Code:

Man kann den Parameter dir mit einem Pfad angeben, dieser muss bei http entsprechend kodiert werden, damit er nicht mit URI reservierten Zeichen kollidiert.
Also dir=file:///E:/buildtest/ ist es eigentlich.
Darauf hin guck ich in den Ordner und hol mir alle Datein, dann erzeuge ich den job Ordner und erzeuge ein Virtuelles File Archive mit den Daten.
Zum Schluss gehe ich durch den Job Ordner und gebe die dort liegenden Datein zurück.
Code:
  1. {"files":[{"name":"0.var", "size":78},{"name":"1.var", "size":51},]}

Nun kann kann ich per einfachen file request die Datein laden.
Code:

Code:


Das VFS hab ich auf Arbeit für ein anderes Projekt mal geschrieben und ist sehr einfach gestrickt(~350 Zeilen Code).
Im 0.var wird ein FOURCC, Version, Archieve Informationen sowie die filenodes hinterlegt.
Der Name einer Datei und die Daten liegen in den folgenden Archieven 1 bis N-1.
Man kann also 0.var laden und sich seine Dateien gezielt raus suchen.
Die FileNodes haben ein hash vom Dateinamen, so kann ich auf meinem Server die lokalen Datein nehmen, hashen und im Archieve suchen.
Alle Nodes, die sich verändert haben müssen entweder gelöscht oder neu geladen werden und die restlichen Nodes gibt es lokal nicht also brauch ich die auch noch.
Das VFS zerlegt die Daten in eine feste Größe(Default:300MB).
Bei http kann man auch nur ein Teil einer Datei anfordern und das Feature möchte ich nun noch in meinem HTTP Server einbauen, damit ich gezielt ein Teil aus dem Archieve laden kann.
Aktuell kompremiere ich auch noch nicht die Daten aber das ist bei brotli ziemlich einfach und der Aufwand es in CMAKE einzupflegen ist höher.

_________________
"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: Radon Framework
BeitragVerfasst: Mi Sep 28, 2016 12:49 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Aus Alt wird Neu
Seit dem letztem Post hab ich nicht viel programmiert aber es geht trotzdem langsam vorran.

Mit der Console Klasse kann man nun auch Eingaben von der Konsole einlesen.
Ich hab bisher immer Parameter für die Usereingabe benutzt und erst jetzt brauchte ich zur Laufzeit weitere Informationen.

Es gab diverse Anpassungen in der Codebase, damit man nun auch mit Visual Studio und clang compiler arbeiten kann.
VC++ ist nicht so pedantisch wie clang und entsprechend sind einige kleinigkeiten nicht korrekt gewesen.

Die OpenGL VM ist nun komplett rausgeflogen samt aller OpenGL spezifischen Klassen für Rendering.
Ab nun gibt Radon nur noch einen gewünschten Render Kontext und für OpenGL liefer ich auch noch custom generierte Header, damit man keinen Extension Wrapper(z.B. GLEW) braucht.
Ein Klassenkonstrukt vor zu geben, um OpenGL zu rendern, hat diverse Nachteile, zum einen zwingt man Entwickler auf OpenGL und zum anderen auch in ein gewissen Stiel für Resourcenhandling und Rendertechniken. So löse ich nur noch das Problem des Render Kontext und des laden der OpenGL Funktionen.
Dies bedeutet auch, dass das UI nicht mehr auf OpenGL zugeschnitten ist und die API einfacher zu verstehen ist.
Aktuell arbeite ich an Vektorbasierten Zeichenvorschriften, gut bekannt von SVG.
Die UI Komponenten können unter dem Canvas3D Instanz eingehangen werden und dann werden mit Hilfe von der Path2D Klasse Zeichenvorschriften generiert.
Diese sind Render API unabhängig und gehen dann an das Canvas3D, welches dann an die Userimplementierung das Zepter abgibt.
Das ganze sieht im Prinzip nun wie bei VCL von Delphi aus, man baut eine Komponente, die bei mir von Control ableitet und über Canvas kann man sagen, was man zeichnen möchte und auf Input Events reagieren.
Im Programm kann man dann jede Instanz Eltern zuweisen oder das Form wird verwendet.

Das Build Tool hat nun auch Clientcode, um Anfragen zu stellen aber die Anfragen selber muss ich noch programmieren.
In der Regel will man mehrere Anfragen stellen und nicht auf die Antwort warten, sondern reagieren, wenn diese da ist.
Hierzu hab ich ein ClientPool gebaut, wo die Clients hinzugefügt werden können und mit der Select API kann ich recht effizient auf Änderungen reagieren.

Ich habe den Hash Service refactored, was mir schon länger ein Dorn im Auge war, weil ich ständig eine kleine Funktion kopiert hab, statt den Hash Service zu verwenden.
Das Problem war der Aufwand ein Hash Service in Anspruch zu nehmen.
Nun hab ich 3 Klassen gebaut LongHash, Hash32 und Hash64.
Hash32 nimmt den Default 32Bit Service, der im HashServiceLocator hinterlegt ist, Hash64 entsprechend für 64Bit und LongHash nimmt ein beliebig langen Hash Service, der als Default makiert ist.
Damit nutzt man nun nicht mehr den HashServiceLocator direkt, sondern hat einfache Klassen, die die Arbeit kapseln und durch die Default Services muss ich nicht mehr in einer Liste Iterieren, sondern greife auf den festgelegen Service direkt zu.
Der Service Locator funktioniert sonnst wie früher, man kann Services zur Laufzeit registrieren und dann einen existierenden als Default makieren oder über den Namen den Service suchen lassen.
Aktuell benutzte ich hashlib++ und MurmurHash.
Nun kommt noch xxhash hinzu, da dieser noch schneller als MurmurHash ist und ich fast immer hashfunktionen brauche die Geschwindigkeit über Bitverteilung stellen.

_________________
"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: Radon Framework
BeitragVerfasst: Mi Feb 15, 2017 15:04 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Ich will mal wieder ein Update machen und zeigen, was sich so seit dem letzten Update so getan hat.

Allgemein
  • Icon support für binaries.
  • Shared libraries können nun auch nur über den Namen gesucht und geladen werden.
  • BitArray hat nun GetUnsetIndices und GetSetIndices um mehrere Bits gleichzeitig zu setzen.
  • libtess2 integration für Vektorpfad triangulierung.
  • Der FontService kann nun auch Bitmaps generieren, neben den Vektorpfaden.
  • Man kann nun Bilder laden und speichern, je nach verfügbarkeit von einzelnen Encoder/Decodern.
  • Ich hab den Unit Test Code aufgebröselt und ein bisschen refactored. Ziel ist es neben Unit Tests auch in Zukunft mal andere Testarten zu implementieren.
  • Die Bild Klasse kann nun auch ein paar einfache Operationen, wie Farbkanäle ändern oder ein Teilausschnitt kopieren.
  • Es gibt nun die Möglichkeit Services Optional ins Framework einzubinden(statisch/dynamisch).
  • OpenGL ist nun eine Renderer Implementierung, die als optionaler Service eingebunden werden kann.
  • X11 und GDI sind nun Fenster Implementierungen, die als optionale Services eingebunden werden können.
  • Das Rendering läuft nun über Threadsafe CommandBuckets, was bedeutet dass mehrere Threads gleichzeitig Renderbefehle zusammen bauen können und der Main Thread diese dann ausführt.
  • Neben ForEach gibt es nun auch ForEachBulk, welches ein einzelnen Funktionsaufruf macht und die Anzahl der Elemente, die verarbeitet werden sollen übergibt.
    Dies hab ich Implementiert, weil beim simulieren von Partikeln wenig Rechenzeit gebraucht wird und es sehr viele gibt. Mit ForEach hätte ich jedes Partikel einzeln aufgerufen, was viel CPU Zeit verschwendet.
  • Der ThreadPool nutzt nun Pointer, statt byValue Daten, womit das Ausführen weniger Zeit verbraucht.
  • Ich hab die Queue, welche für Multithreading genutzt wird überarbeitet, damit sie noch schneller wird und dabei noch ein Bug gefixt. Die Queue nutzt nun Cache und Memory Page Alignment, damit sich die einzelnen Kerne nicht teure Synchronisierungen auslösen.
  • Es gibt nun auch AtomicUInt32, welche in der Queue verwendet wird.
  • Ich hab eine Systemfunktion ShortestPause hinzugefügt, welche in der Regel auf ein NOP runter bricht oder bei Hyper Threading CPUs auf den 2. Thread, vom Kern, wechselt.
  • Neuer Namespace Meta, für Meta programming mit templates. Ich mag einfaches Meta programming, nicht so übertriebene Monster wie bei Boost.
Neues Repo
In folgendem Repository lager ich nun den Code für Austausch Formate.
Da ich das Framework Repo so klein und möglichst unabhängig halten will, kann man nun als Option dieses Repo automatisch einbinden lassen.
Radon intermediate converter
  • Bitmap Encoder/Decoder
  • Jpeg Decoder
  • Png Encoder/Decoder
Künftig will ich dann auch Json und XML support dort einbauen.

Physik
Ich hab ein bisschen Lineare Algebra hinzugefügt
Es gibt nun eine Collider Klasse, in der ich die einzelnen Funktionen sammeln will.
Aktuell gibt es nur die RaySphereIntersection, da ich diese neben der SphereSphereIntersection benötige und sehr schnell ist.
Ich hab noch eine Normal Klasse hinzugefügt, damit ich auch dort entsprechend rechnen kann.
Was noch recht experimentel ist aber schon recht weit, ist eine PackRectangle Klasse.
Diese braucht man häufig, um Rechtecke möglichst effizient in ein Rechteck zu packen.
In mein Fall brauch ich es z.B. für Texturatlanten und System Font generierung.

UI
Ich hab einiges im UI Bereich gemacht.
So gibt es nun diverse Layouter Klassen, z.B. GridLayouter oder VerticalLayouter, um UI schnell zu platzieren.
Man kann nun auch das Maussymbol ändern, wenn man z.B. die Sanduhr einblenden will, wenn man was aufwändiges macht.
Die UI beachtet die DPI für jeden Monitor oder System(je nach OS).
Die Vektorpfade können nun auch Strokes verarbeiten und rendern, wie man im Screenshort später sehen kann.
Für die Farbverläufe muss ich noch den Code umbauen, weil diese aktuell ziemlich kaputte Ergebnisse liefern.

GPU info
Ich habe ein neuen Service hinzugefügt, welcher GPU Infos zurück geben kann.
  • Temperatur
  • Core clock
  • Memory clock
  • Lanes
  • Bus speed
  • Load/activity
  • Fan speed in RPM
Ich hab AMD und NV support im Code Beispiel angebunden.
Hier mal ein Echtzeit Plot von der GPU-Load(GF980) am Start, Fenstermodus, Vollbild und dann wieder hin und her wechseln.
Bild

Das Framework signalisiert NV und AMD Karten nun, dass immer die Leistungsstärkste GPU genutzt werden soll, was für mehr GPU-Systeme, wie Notebooks wichtig ist.
Wenn man ein Intel Chipsatz hat und als 2. Karte eine AMD oder NV Karte, dann wird die Ausführung auf dieser passieren.

Memory Allocator
Auf Basis dieses Videos hab ich meine Allocator komplett überarbeitet und angefangen diese auch wirklich zu nutzen ^^
Folgende Allocator benutzte ich nun.
  • HeapAllocator
  • FreeListAllocator
  • FixSizeStackAllocator
  • FallbackAllocator
  • AllocatorSelector
Wobei AllocatorSelector kein wirklicher Allocator ist, sondern ein bisschen Meta programming Foo.
Das Template schaut in die AllocatorTraits von der angegebenen Klasse und tut entsprechend dann ein passenden Allocator zurück geben.
Hier mal die Meta Infos von meiner TypelessDynamicQueueMPSC Klasse, welche z.B. für Netzwerkkommunikation verwendet wird.
Code:
  1. struct TypelessDynamicQueueMPSC::AllocatorTraits
  2. {
  3.     enum
  4.     {
  5.         StackSize = 1024,
  6.         BlockSize = sizeof(Node),
  7.         AlignSize = alignof(Node),
  8.         HeapAllocation = true,
  9.         FixedBlockCount = false
  10.     };
  11. };
  12. typedef RF_Mem::AllocatorSelector<TypelessDynamicQueueMPSC>::Type Allocator;
  13. Allocator* NodeAllocator = nullptr;
  14.  
  15. TypelessDynamicQueueMPSC::TypelessDynamicQueueMPSC()
  16. {
  17.     NodeAllocator = &RF_Pattern::Singleton<Allocator>::GetInstance();
  18.     m_Stub = reinterpret_cast<Node*>(NodeAllocator->Allocate(sizeof(Node)).Data);

Beim erstellen der ersten Instanz, wird der Allocator erzeugt und zwischen allen Instanzen genutzt, da ja alle exakt das gleiche Memory Layout verwenden.
Im Prinzip ein PoolAllocator aber ich kommuniziere nur das Speicher Layout und was dann wirklich für ein Allocator raus kommt wird an einer zentralen Stelle(AllocatorSelector) fest gelegt und damit kann man mit neuen besseren Allocator sämtlichen Code mit einmal optimieren.


Dateianhänge:
gpuinfo.png
gpuinfo.png [ 8.35 KiB | 22389-mal betrachtet ]

_________________
"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: Radon Framework
BeitragVerfasst: Fr Feb 02, 2018 11:47 
Offline
DGL Member
Benutzeravatar

Registriert: Di Mai 18, 2004 16:45
Beiträge: 2621
Wohnort: Berlin
Programmiersprache: Go, C/C++
Ich habe mir ein Schlachtplan für die nächsten Jahre angefangen und wie dort meine coding Projekte rein passen.
Radon Framework, Radon CMake framework, Radon fast entity component system, Tellurium(asset processor), Krypton Build(zero conf cluster task execution), Organesson(spatial/visual optimization) und RadiumDB(document based, jittet, game centered db) werden zu Codefeed wandern.
Dies wird dieses/nächstes Jahr ein Startup werden und die Software wird ein Lizenzänderung bekommen.
Es bleibt open source aber es wird die freie Nutzung auf X€ Umsatz beschränken und ab dann benötigt man eine commercial license.
Entsprechend werde ich die Projekt Roadmaps für Version 1.0 und 2.0 planen und dokumentieren.
Dies wird auch bedeuten, dass ich mit dem Promoting anfangen werde, Firmengründung, Geld investiere, mich massive auf die Nutzbarkeit konzentrieren werde(Doku, pre builds, CI).
Ich plane aktuell noch wie ich das Viral angehen werde, ob Blog/VLog, Patreon, DTube, Steemit, Förderprogramme, BountySource, Firmenart und so weiter.
Die tatsächliche Gründung und Release 1.0 für RF plane ich Q4 2018/Q1 2019.

Krypton Build und Tellurium will ich mit einem anderen Hobby Projekt verbinden, welches nun durch neue Kontakte realistisch ist ein Spin zu bekommen und weniger Zeit investieren müsste als bisher der Fall war.

Organesson wird mein Hauptaugenmerk für dieses Jahr sein und die meisten RF erweiterungen/änderungen werden dafür passieren.

Neues vom Radon Framework
Die Änderungen seit dem letztem Post sind viele ^^

  • IPv6 support
  • mDNS bug fixes(funktioniert nun recht zuverlässig)
  • String Klasse unterstützt nun string literals
  • Neue Allocator
  • AppVoyer support
  • Nested namespaces(C++17) "namespace ... { namespace ... {" -> "namespace A::B::C {"
  • in app scope profiler
  • started BDD test support
  • range loop support for Container
  • Rectangle packing
  • System font rendering(ttf support ohne extra bibliothek)
  • moved from local 3rd party configuration to Radon CMake package support
  • file mapping API support um Speicherblöcke beliebig zu mappen(z.B. ein Virtueller Speicher der 2x hintereinander den selben Speicher mapped)
  • Hardware beschleunigter RingBuffer support
  • ObserverPointer template hinzugefügt(dümster smart pointer)
  • viele coder vereinfachungen wie namespace Alias, typedef von template spezialisierungen, namespace polution(per define)
  • libmorton integration
  • lazy evaluation für Scalar operationen
  • WorkBalancer für inteligentes task balancing angefangen
  • Frustum, Plane, AABB, Normal, Octree Klassen hinzugefügt
  • viele bugfixes
  • C++17 support VS2015 und aufwärts, teilweiser clang, g++, linux, osx, arm support

Als nächstes fokussiere ich mich auf Octree fertigstellung, Software rasterizer, WorkBalancer, lazy evaluation linear algebra, JIT und sauber machen für Version 0.5.

_________________
"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  [ 64 Beiträge ]  Gehe zu Seite Vorherige  1, 2, 3, 4, 5  Nächste
Foren-Übersicht » Sonstiges » Projekte


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 29 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.059s | 20 Queries | GZIP : On ]