DestroyBuffer(rgbBuffer)-- gibt den Speicher frühzeitig frei.
-- do some fancy stuff
RegisterAsset(filename .. '.ycc')
}
RegisterAsset, CreateBuffer, DestroyBuffer, GetTempDir und GetAssetDir sind Builtin Funktionen. RGB2YCC ist eine Plugin Funktion. Ein Plugin ist eine Shared Library, welche folgende funktionstypen enthalten müssen.
BasicInfo Struct ist die Kommunikationschnittstelle zwischen Plugin und Converter. So muss das Plugin z.B. den FreeCallback setzen, welcher vom Converter beim Entladen versucht wird aus zu führen. So wird auch der Name und die gelieferten Funktionen hinterlegt. Es gibt aktuell IsNumber, IsInteger, IsString, GetNumber, GetInterger, GetString und GetBuffer als Kommunikations Schnittstelle. RGB2YCC ruft in der Plugin Funktion z.B. folgenden Code auf, um an den zuvor erzeugten Buffer zu kommen.
Wenn eine Änderung im Asset Odner passiert, dann wird für jedes Script ein lua VM erzeugt, die BuiltIn registriert, alle Plugin Funktionen, dann Start aufgerufen und zum Ende die VM zerstört und erstellte Buffer frei gegeben.
Das Script muss selber entscheiden, ob es auf die Datei reagieren will oder nicht, dafür wird der Dateiname samt Pfad mit übergeben.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2622 Wohnort: Berlin
Programmiersprache: Go, C/C++
CMAKE Nach längerer Pause hab ich nun wieder angefangen weiter zu programmieren. Ich habe realisiert, wie sich Radon Framework über die Zeit aufgebläht hat und mitlerweile gute 2GB mit kompilierten objekten war. Daher hatte ich angefangen Tools und Modules in seperate Repositories zu verfrachten.
Module sind framework erweiterungen für z.B. Datenbank, Sound oder ähnliches. Ich habe mitlerweile ein paar kleine Tools und ein sehr großen Editor(der aber nix kann ^^) und die liegen nun auch seperat.
Die seperierung hatte zufolge, dass ich wieder vor einem alten Problem stand "Wie integriere ich RF in ein anderes Projekt?". Also hab ich dann angefangen cmake komplett von vorne zu refaktorn. Mein Ziel ist es die Konfigs zu zerlegen und sehr ausführlich zu dokumentieren. Auch wie Settings nach aussen(cmake gui) und innen(cmake variablen) gehandhabt werden überarbeite ich. Ich hab nun ein kleines Script, welches man sich ins projekt kopiert oder direkt includiert und das sucht das Framework und includiert dann die eigentliche Konfiguration. Das ganze passiert aktuell auf meinen Bahnfahrten.
Optimieren optimieren optimieren ... Zuhause arbeite ich daran den System Kern zu überarbeiten. Ich habe angenfangen RF eine eigene runtime library zu verpassen, aktuell gibt es eine MicroRuntimeLibrary und noch eine halbe MiniRuntimeLibrary.
C runtime library und andere Bibliotheks Abhängigkeiten Micro ist das minimalste, es enthält ein entrypoint, statische Klassen initialisierung, atexit stack und ne handvoll wichtiger funktionen wie atexit, stack check und stdout/in/err. Mit Micro konnte ich meine Binaries aktuell auf 75KB bringen, wenn ich eine volle initialisierung mache und 4,5KB ohne initialisierung. Micro soll sich an Entwickler richten, die ausschliesslich RF nutzen und keine 3rd party libs, die statisch gelinkt werden aber als shared lib. Es wird nicht das volle Framework funktionieren.
Mini existiert nur als bruchstücke und erweitert Micro um übliche clib funktionen, wie z.B. memcpy, memset, strlen und macht das volle framework funktionsfähig.
Wird weder Micro noch Mini genutzt, dann nimmt RF C runtime library(CRT) vom compiler.
Binary size Ich hab auch mal eine Analyse(mit amap und /map) von meinen Builds gemacht und fest gestellt, dass GLEW ziemlich übel ist und es verbannt. Wieso ist es übel ? Es braucht mit VC++ knapp 250kb an binary daten, das sorgt dafür, dass der zugriff auf die Daten extrem langsam ist, weil diese nie in irgendeinen Cache passen. Es hat auch einige dependencies an CRT(hierzu hilft Assemblyausgabe Setting in vc++). Meine tools sind in der Regel zwischen 300-500KB groß und 250KB davon macht glew aus, obwohl alle Daten, die notwendig sind in opengl.dll und gdi.dll stehen und die entsprechenden header(gl.h, windows.h, glext.h) alle informationen halten. Was also so teuer eingekauft wird ist das automatisierte laden und binden aller extensions. Das macht für mein Framework überhaupt kein Sinn.
Im Zuge der Analyse hab ich auch fest gestellt, dass ich eine System Funktion habe, die gute 3KB groß ist, keine Idee und der Grund war ein statischer Array, der nur teilweise gefüttert wird. Nachdem ich ihn nun dynamisch alloziiere, ist es nur noch 400byte groß und die binaries 2,5KB kleiner.
Abschlusswort Wieso betreibe ich solch ein Wahnsinn wird sich der eine oder andere nun fragen. Die Antwort ist folgende. Ich entwickel ein Framework und keine Anwendungs Software, die Prioritäten sind also andere. Anwendungen werden mit Framework entwickelt und daher sollte diese so klein und leistungsfähig wie möglich sein. Eine Anwendung kann wesentlich verschwenderischer sein, wenn man nicht gerade 4K/64K Demos programmiert.
Ich verspreche, dass beim nächsten Post weniger Text und viel mehr Programmiererbilder drin sind
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2622 Wohnort: Berlin
Programmiersprache: Go, C/C++
CMake Ich hab mein CMake framework weiter ausgebaut, bugs behoben und angefangen XCode, clang und gcc spezifische optimierungen ein zu bauen. Das Framework unterstützt nun auch ASM und baut valide Projekte für Win, Linux und OSX. Es gab da ein paar Probleme mit Linux/UNIX/OSX erkennung aber die sind nun behoben.
Target system support Ich kann nun Radon framework unter OSX bauen aber einige System Funktionen sind noch nicht drin, damit läuft nicht der ganze Code ohne absturz :\ Vor einer weile hatte ich ja mal geschrieben, dass im Framework alle Systemfunktionen über ein CPU-Dispatcher laufen oder laufen sollten(gibt noch eine Hand voll die es nicht tun) und es gibt eine Funktion, die alle nicht gepatchten Funktionen zurück gibt. So kann ich mit dem neuen SystemTest raus bekommen, ob auf ein Target System alle Systemfunktionen verfügbar sind und somit das Framework korrekt läuft. Das Test Programm ist neu und hat mir erstmal gezeigt, dass ich unter Windows 2 Funktionen vergessen hab und das Linux und OSX noch einige fehlen. Der Vorteil ist, ich kann Systemfunktionen Stück für Stück implementieren und testen kann, ohne für alle anderen Funktionen leere Implementierungen an zu legen. Läuft der Code über eine Funktion, die nicht implementiert ist, dann gibt es eine NULL-Pointer interrupt vom System. Eine Util Funktion erlaubt mir ein Array von Strings zu bekommen, welche die Namen aller nicht dispatchten Funktionen enthält. Damit muss ich nicht Test Applikationen schreiben, die über alle Funktionen rüber laufen und auf den Crash warten.
Umbau der Entwickler Umgebung Ich versuche gerade meine Entwicklungs Umgebung zu vereinfachen. In letzter Zeit wurde mein Root-Server mal teilweise missbraucht, gekapert, lahm gelegt und diverse male versucht in Botnetze zu integrieren. Zum glück ist das System sicher genug gewesen, das nix schlimmes passiert ist und die Alarmglocken rechtzeitig läuten. Doch kostet mich das hardening und instant halten mehr Zeit als ich nutzen von hab. Ich hab nun angefangen löschrige Systeme in externe Hände zu geben. Konkret hab ich vom eigenem SVN auf GIT umgestellt. https://github.com/tak2004/RadonFramework GitHub ist wahnsinnig schnell und kann sogar gleich noch Redmine für mich ersetzen. Ich bin noch dabei mein Redmine Wiki auf github rüber zu bringen und mein Jenkins build um zu stellen. Jenkins will ich vom Root-Server auf ein lokalen ESXI Server verschieben. Ich hab ein AMD FX(8Kern), 16GB, 8TB HDD, 64GB SSD System, auf dem aktuell schon Debian6-64Bit, Centos6-64Bit, Fedora20-64Bit, Win7-64Bit mit Jenkins laufen und verbunden. OSX bekomme ich leider nicht zum laufen, weil der neue OSX Kernel noch kein AMD Patch hinter sich hat, daher läuft die VM noch auf mein Arbeitssystem(Intel).
Optimieren optimieren optimieren... Als ich mit der Optimierung angefangen hab fiel mir recht schnell auf, dass ich mehr Daten von mein Code brauch und dafür nutzt man entweder für jedes System einzeln ein Profiler und analysiert diesen oder beobachtet in synthetischen Tests jede Funktion für sich selbst. 1. ist für Applikationen sehr Sinnvoll und 2. bekommt man eigentlich mit den Unit-Tests umsonnst. Bei der Optimierung soll mir das Buildgrid helfen, indem es automatisiert für alle Platformen die Test durchläuft und die Ergebnisse mit dem vorigen Build vergleicht. Jenkins hat Plugins für diese Arbeit, man muss halt alles einmal einrichten.
Es gibt sehr tolle Bibliotheken in der freien Wildbahn, die sich gezielt auf low level Funktionen stürzen und diese bis ins extrema optimieren. Solche Bibliotheken sind nahezu immer ASM und deswegen hab ich schon mal ASM support in CMake framework eingebaut. Wer sich für optimierung von Basisfunktionen interessiert, der sollte unbedingt mal hier vorbei schauen. Das ist eine Goldgrube an Wissen und der Code ist der Wahnsinn(CPU-Dispatching für diverse CPU-Extensions, getestet und optimiert für diverse Architekturen).
Ich hab vor ner weile von RadonInit/RadonExit auf ein RAII Radon Objekt umgestellt und dort die ganze initialisierung und dispatching gemacht. Mir ist mitlerweile das ein Dorn im Auge, wenn ich nur ein Teil des Frameworks verwenden will, z.B. Mathematik, Task System oder Diagnostics, dann hab ich das ganze Framework an der Backe und muss noch ne menge Zeug machen, was ich eventuell garnicht braucht. Deswegen hab ich mal ne Code Analyse gemacht und fest gestellt, dass ich mit wenig Aufwand auch diesen Teil vorläufig in ein Singleton packen kann und somit die Initialisierung OnDemand passiert, statt explizit. Auf lange Sicht will ich natürlich auf OnDemand Dispatching umsteigen, für ein großen Teil meiner System Funktion ist dies schon passiert aber einige Teile müssen erstmal refactored werden, bevor das auch dort geht und da ist die Lösung mit dem Singleton erstmal einfacher.
Wie sieht eigentlich solch ein Dispatcher aus ?
Code:
// *.hpp
float(*FunctionType)(constfloat Bla, constfloat Blupp);// wie soll die Funktion aussehen
extern FunctionType TolleOperation;// es gibt irgendwo ein Funktionspointer von dem Typ
TolleOperation = TolleOperationA;// ab nun ist jeder Aufruf auf TolleOperation ein aufruf von TolleOperationA
else
TolleOperation = TolleOperationB;// ab nun ist jeder Aufruf auf TolleOperation ein aufruf von TolleOperationB
return TolleOperation(Bla, Blupp);// den eigentlich aufruf machen
}
FunctionType TolleOperation = InitDispatcher;// ab jetzt ist jeder aufruf von TolleOperation ein aufruf von InitDispatcher//
Wenn man TolleOperation(a, b) aufruft, dann ist es ein normaler Funktionsaufruf, keine extra kosten, kein indirekter pointer oder so. Der erste Aufruf läuft auf die Init Funktion, welche den pointer auf eine andere funktion umbiegt und diese dann noch aufruft, damit der erste Aufruf auch das tut, was er sollte. Jeder weitere Aufruf läuft direkt auf Funktion A oder B, weil der pointer nun dort hinzeigt. Damit ist nur der erste Aufruf teurer, weil noch der Overhead vom Dispatcher ausgeführt werden muss. Üblicherweise ist SWITCH eine Globale Konstante oder Funktion, die Informationen über das OS oder Hardware besorgt und darauf hin entscheidet, welcher Pfad die bessere Lösung ist. Wenn man z.B. eine Implementierung von einer Funktion mit ALU/FPU, eine mit MMX, SSE2, AVX256 und AVX512 macht, dann würde man mit cpuid nach den Extensions fragen und auf die beste Lösung mappen. So kann man z.B. auf ein Windows XP 32Bit, welches auf ein Intel i7 läuft trotzdem AVX512 verwenden und so 32Bytes pro Operation verarbeiten, statt 4Byte, wie auf dem System üblich ist. Ein AMD FX könnte immer noch AVX256 verwenden und ein AMD X2 SSE2, ein Intel P3 MMX und der rest ALU/FPU.
Das ganze ist natürlich in der Binary größer, weil alle Codepfade enthalten sein müssen, bei 64Bit Binary würde der ALU und MMX weg fallen, weil SSE2 laut Spezifikation vorhanden sein muss. Allerdings sind Funktionen sehr klein und eher nicht auf fällt. Diesen Aufwand betreibt man üblicher weise für memcpy, memcmp, strlen, memmove, vprintf und einige andere low level Funktionen, die von so ziemlich jeder Funktion verwendet werden. Praktische Implementierung Es gibt auch noch schnellere CPU-Dispatcher, die auch weniger groß sind aber dafür braucht man asm und ich wollte an der Stelle lieber lesbaren/verständlichen Code und goto vermeiden. Diese Lösung ist mit Compiler und Linker Optimierung fast genau so schnell und ist verständlicher C Code. Intel Compiler erkennen dank guter Heuristik solche Dispatcher und bauen daraus den best möglichen Dispatcher. Die Heuristik von Compiler wird generell besser und so erkennt so ziemlich jeder C++ Kompiler float[4] als potenzielle MMX/SSE2 optimierungs stelle. Sobald man Compiler Optimierungen an schaltet, bauen viele bereits SSE Code und intrinsic Funktionen, wie memmove, memcpy sind sowieso optimiert. Daher macht es immer mehr Sinn sauberen C/C++ Code zu schreiben und dem Compiler es zu überlassen, statt ASM zu programmieren. Man sollte einfach mal vorher testen, was die Compiler auf den Zielplatformen so an Code generieren und daran entscheiden ob es sich noch lohnt bestimmte low level Funktionen in ASM zu liefern.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2622 Wohnort: Berlin
Programmiersprache: Go, C/C++
Pattern und Anti-Pattern In der letzten Woche hab ich mich wieder der Aufgabe gewidmet, meine ServiceLocator basierten Kapselungen auf zu lösen. Service Locator kennt man eigentlich aus der Java Welt recht gut und hier und da werden sie auch verwendet. Den Pattern fand ich vor einigen Jahren mal sehr praktisch, doch kristallisierte sich mit der Zeit einige Probleme herraus. Ich hatte ein zu starke Bindung von standard Services in mein Code, da die Services vorher registriert werden müssen, bevor man sie verwenden kann. Also sind dinge wie OGLContext1, OGLContext2, HashX, HashY und so weiter fest im System und viel schlimmer, es gibt eine Funktion die unbeding durchlaufen werden muss, bevor man auf einen der Services zugreifen kann. Als ich nach einer sinnigen Alternative gesucht hab, entdeckte ich, dass unter C++ der Service Locator Pattern mitlerweile ein Anti-Pattern ist und zählt mit unter die genannten Probleme auf. Die alternative ist Dependency Injection. Die pattern sehen sich extrem ähnlich aber haben den großen unterschied, dass DI die Factory explizit im Constructor übergibt und sich das Objekt nicht per Name/Id die Factory über den Service Locator holt. Man kann also gewehrleisten, dass das Objekt korrekt instanziiert wird, da man die abhängig mit dem Constructor injiziert, daher der Name.
Der Service Locator macht den noch Sinn, wenn man eine fixe Gruppe an Services hat, zwischen den zur Laufzeit gewechselt wird. Diesen Fall hat man bei IO Bezogener Funktionalität, wie z.B. Zugiff auf festplatte, Virtuelles Datei System und internet oder bei der wahl des richtigen decoders für eine Datei. Hier werde ich beim Service Locator bleiben aber die System API wird nun über Funktionsdispatcher gelöst und OGL, sowie Hashfunktionen laufen demnächst über DI.
Die Code-Leichen aus dem Keller entsorgen. Ich hab wieder ein guten stück Code aus dem Framework geworfen. In der Vergangenheit hatte ich SMBios und CPUId recht umfassend eingebaut aber kaum was von verwendet. Daher hab ich mal genau analysiert was ich eigentlich so an daten verwende, was ich nicht auch über anderen Weg finden kann. Letzlich stellte sich raus, dass cpuid alles abdeckt. Ich benötige im Framework sämtliche Cacheinformationen, cpu extensions und wissen, wieviele logische Prozessoren über alle CPUs verfügbar sind. Ich hab diese Infos einmal mit cpuid zusammen getragen und für Windows hab ich noch die Windows Funktionen eingebaut, die das gleiche implementieren. Bei der cpuid Version muss man die Cacheinformationen ziemlich umständlich mit sequenziellen Aufrufen, auf Level 2, sich alle Daten holen und dann erstmal prüfen welche register belegt sind, die Bytes auseinander nehmen und dann aus einer Liste von 255 Einträgen die Cachekonfiguration holen. Das Problem ist, dass man nun für jede Zahl ein equivalentes Objekt erzeugen muss und da gibt es verschiedene Lösungen.
1.) Erstens ist wahnsinnig groß aber braucht keine Erzeugungszeit. Wie man hier sehen kann, benötigt ein Objekt 16 Bytes und mit 255 Variationen davon kommt man auf 4Kb. Diese Daten sind 1zu1 in der Binary drin und müssen auch in den Speicher. Wird das Objekt in der Zukunft wird es doppelt so groß, also 8KB(wegen 16Byte alignment). 2.) Mit einem Switch von 255 Cases und je 5 Variablen zuweisungen spart man nicht viel, 4 Bytes pro Fall, weil man die 6. Variable aus 2 der 5 errechnen kann und dies nur 1x machen muss. Letzlich bleiben 3KB an Daten + Code. 3.) 5 einzelne Arrays mit den jeweiligem Werten ergeben das gleiche Szenario wie bei 2. aber der Code ist wesentlich kompakter. 4.) Je ein Switch Case für jede Variable. Da bei den 255 Variationen viele wiederholungen drin sind, die aufeinander folgen, kann man mehrere aufeinander folgende Indizes zu blöcken zusammen fassen. Objekt Pascal hat z.B. case 124-128, C++ hat das nicht, macht es aber trotzdem und das sehr Effizient. Diese Lösung sieht richtig schlimm aus aber ist am Ende knapp unter 2Kb groß.
Duff Device Ein sehr schöner Algorithmus. Früher wurde das Duff Device benutzt, damit man sich soviele Operationen im Schleifenkopf sparen kann wie möglich. Heute ist es auch interessant, es wird hauptsächlich für das verarbeiten der n-Bytes bis zum nächsten 16 Byte Speicher Block und für die letzten Bytes in einem 16Byte Speicherlbock verwendet, dazwischen verwendet man dann SSE, hat man AVX256 oder AVX512, dann sind es natürlich 32 oder 64Byte alignment. Ein Byte zu verarbeiten, ist in etwa so teuer wie ein if mit latenz, bei einem Core 2 Due kann man z.B. 13 Vektoren multiplizieren, in der Zeit wo 1 Byte multipliziert wird. Also ist es heute sinniger für SSE die 15 Varianten in einem switch case unter zu bringen und den compiler eine jumptable daraus machen zu lassen. In den cases nutzt man dann so große Variablen wie möglich, z.B. 8Byte, 4Byte, 1Byte für case 13: und spart sich viele teure short und byte operatoren. Die eigentliche SSE operationen tut man dann aber in ein Duff device, weil wir mehrere SSE operationen ausführen können für den Preis eines if.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2622 Wohnort: Berlin
Programmiersprache: Go, C/C++
Entwicklungs Pipeline In den letzen Wochen hab ich mich um mein Buildserver gekümmert. Da drauf laufen aktuell Centos6, Debian6, Window7 und noch ein Centos6 für Jenkins. Ein Grund wieso ich von SVN auf GIT gewechselt bin sind Hooks. In Git kan man für bestimmte Events eigene Scripte laufen lassen, z.B. post/pre-commit, push, pull und so weiter. Ich hab mir ein postcommit hook eingerichtet, welcher mein lokalen Stand auf ein Netzwerk Share abgleicht und Jenkins sagt, dass er mal ein Build triggern soll.
Git bringt für den Abgleich eigene Boardmittel mit, damit auch dieser praktisch mit diff, commit meldung und so arbeitet aber das hat bei mir nicht so gewollt und ich verwende aktuell erstmal robocopy.
Der Jenkins Job ruft beim triggern ein Code Coverage Job auf, der nur auf dem Centos6 ausgeführt werden kann. Jenkins macht nun eine exakte kopie von dem repo in das working directory, auf der Centos6 slave Machiene und startet den Job. Der Job erstellt ein Unix Makefile projekt mit cmake und baut dann mit make alles. So der aktuelle Stand und so hab ich immer die Info, dass der Code unter Linux baut. Wenn ich an der System API schraube, was aktuell der Fall ist, dann commit ich mit "Message only" flag, was Tortoise sagt, dass er es nicht wirklich commited sondern nur mal so tut als ob und ich bekomme dann trotzem mein Build von Jenkins und weiß ob ich gerade was kaputt gemacht hab oder ob der gerade in VS2010/13 geschriebende Linux/Unix Code auch unter Linux baut.
Zukünftig soll dann der Unit Test laufen, welcher mir als code coverage test dient und mit lcov bunte grafiken herbei zaubert Okey und weil ich wissen will ob ich was kaputt gemacht hab ^^ Da der nächste große Punkt auf meiner Agenda eine neue String Klasse ist, soll die auch möglichst Bugfrei sein und code coverage ist sehr hilfreich für Unit Tests. Des weiteren will ich für die üblichen String Anwendungen performance tests schreiben, damit ich ein überblick habe, auf welchen OS und Hardware sich bei Code Änderungen was tut. Das ganze hatte ich schon auf meinem alten Jenkins Server am laufen und ich muss ledeglich die Job Konfiguration übernehmen.
Ich will auch künftig auf mehr als nur einem statische Code analyse Tool setzen, es sollen cccc, cppcheck, clang und pvs werden. PVS kommt als job auf die Windows VM, cppcheck und cccc werde ich wohl auf die Centos VM packen und clang auf Windows oder Centos VM. Die Reports haben sich in der Vergangenheit als sehr nützlich erwiesen und so einige Bugs aufgedeckt. PVS z.B. kann einem sagen ob man für Funktionen mehr als 4 Parameter oder für ein Register zu große Parameter verwendet und markert diese als "Performance Optimierungs Möglichkeit" an. Dies hat mit den calling conventions von C++ auf 32 und 64Bit zu tun und macht bei häufigen aufrufen tatsächlich messbare Änderungen aus.
Ich habe nun auch eine gültige VS2013 Ultimate Lizenz Zuhause, was echt praktisch ist. Auf Arbeit habe ich es schon seit einigen Wochen und ich bin begeistert, die Refactoring und Code Feature sind nicht existent oder nur gut getarnt aber das Debuggen, Profiling und UI wurde extrem verbessert. Ich plane recht zeitnahe auf 2013 um zu steigen und noch 2010 build auf der Win7 VM zu machen.
String Ich arbeite aktuell noch an einem Refactoring Konzept für meine String Klasse, damit sich von praktisch zu alternativlos wechselt. Dazu hab ich als Konkrete Ziele fest gesteckt, dass die gleichen Funktionalität mit höchst möglicher Performance kommt und ich in allen Bereichen schneller sein will als QT, STL und C Funktionalität.
Dafür ich verschiedene Möglichkeiten angeguckt und ausgelotet.
edit: Der ursprüngliche Speudocode führte zu unbeabsichtigten Fehlinterpretation von Code.
Code:
enum Type: UInt8{// enum ist nun nur 1byte groß(C++11 Feature)
...
};
class ALIGN(32) String{// 32 byte alignment, bei 64byte cacheline passen 2 strings in eine cacheline
...
protected:
union{
FixString<27> m_Fix;
AutoPointerArray<Char> m_Dyn;//RAII Pattern: besteht aus T* m_Data und Size m_Size(16Byte bei 64bit und 8Byte bei 32Bit)
}
DataManagment::Type m_MemoryManagment;
UInt32 Length;
};
template<int LEN>
struct FixString{
...
char m_Buffer[LEN];
};
edit end
Code:
enum Type: UInt8{// enum ist nun nur 1byte groß(C++11 Feature)
...
};
class ALIGN(16) String{// 16 byte alignment, bei 64byte cacheline passen 4 strings in eine cacheline
...
protected:
union{
FixString<13> m_Fix;
AutoPointerArray<Char> m_Dyn;//RAII Pattern: besteht aus T* m_Data und UInt32 m_Size(12Byte bei 64bit und 8Byte bei 32Bit)
}
DataManagment::Type m_MemoryManagment;
UInt16 m_Length;
};
Der Zugriff funktioniert dann wie folgt(in den meisten Fällen).
Code:
switch(m_MemoryManagment)
{
// fix buffer
case Copy:
...
break;
// dynamic buffer
case AllocateAndCopy:// string passt nicht in fix buffer
case UnmanagedInstance:// für const char*, es wird kein destructor für aufgerufen
case TransfereOwnership:// für char* von der System API oder bestimmte optimierungen zwischen 2 String Objekten zu machen
...
break;
}
Die C++ Compiler machen das zu einer Sprungtabelle, weil meine möglichen Werte 4 sind(k.a. ab wievielen er keine Sprungtablle mehr baut) und diese sequenziell numeriert sind. Das ist eine der Stellen, wo Typsicheres programmieren Vorteile in der Geschwindigkeit bringt.
m_Fix ist immer aligned, weil es als erste Variable kommt und so kann SIMD code mit den schnelleren aligned operationen arbeiten. m_Dyn benötigt eine spezialisierte Memory Allocation. Das ist aktuell alles noch nicht getestet und nur Theoretisch. Aktuell ist mein Plan ein eigenen Allocator dafür zu verwenden, welcher 16byte alignment hat. Resize Möglichkeiten stehen erstmal nicht auf der Prioliste. Der standard allocator reicht dafür aus, man muss einfach nur Bytegröße+15Byte alloziieren und dann kann man den pointer um bis zu 15 Byte verschieben, um alignment hin zu bekommen. Sobald ich so weit bin, wäre der nächste Schritt auf 64byte alignment um zu steigen, damit ich mit Hilfe des Caching verfahrens die Strings immer so im Speicher legen kann, dass diese immer mit einer Cacheline beginnen und je nach Cacheverfahren(4Way, 8Way ... set associative cache) sogar größere Blöcke nutzen.
Die String Klasse ist eine Klasse, die extrem häufig im Framework zum einsatz kommt und auch in Produktivumgebungen sind Strings sehr stark vertreten. Auf den C++ Game Servern, mit den ich bisher zu tun hatte, verbrennen Strings die meiste Zeit, weil man sehr viele Instanzen erzeugt und selten sowas optimiert. Viel Zeit kann man dabei schon nur die verwendung eines extra Memory Allocator sparen, weil man so System Calls für HeapAlloc verhindert und in einigen Umgebungen kann man dann auch sich das delete sparen und ein Objekt garnicht erst zerstören, weil der Speicherblock eh am Scopeende vernichtet wird.
Ich habe überlegt ein StringBuilder Klasse an zu bieten, welches intern ein Scratch Pad erzeugt und sämtliche String operationen auf diesen laufen lässt. Damit kann ich sämtliche temporären String Objekte ohne Speicher Alloziierung erzeugen und muss diese auch nicht zerstören(~String()), da der StringBuilder am Scopeende den Speicher frei gibt. String::Format müsste dies dann aber selber auch intern machen, da es nicht auf StringBuilder setzen kann.
Das ist ein Auszug aus meinem Performance und Unit Test. CustomCompareFunction führt die Zukünftige Variante von ForEach auf ein Array Container aus. Das ForEach erwartet, dass der Container einen Enumerator mit GetEnumerator() erzeugen kann. Dieser wird dann verwendet um alle Element des Containers zu durchlaufen. Als 2. Parameter übergebe ich eine Lambda Funktion, welche das gleiche wie Increase(...) macht. CustomCompareFunction ist eine lokale Variante, welche ein delegate erwartet und diese für jedes Element ausführt.
Soviel zum offensichtlichen und nun wieso ich darüber schreibe ^^ Das ForEach, in CustomCompareFunction, ist nicht nur eine einfache Kopie des STL Konzeptes, Logik von Daten zu trennen, sondern ist doch um einiges smarter. Seht und staunet.
Code:
...
template<class C, typename FUNCTION>
struct EnumeratorTaskData
{
FUNCTION Function;
typename C::EnumeratorType Enumeable;
RFTYPE::UInt32 From;
RFTYPE::UInt32 Steps;
RFTYPE::AtomicInt32* OverallWork;
};
template<class C, typename FUNCTION>
void EnumeratorTaskFunction(void* Data)
{
auto data =reinterpret_cast<EnumeratorTaskData<C, FUNCTION>*>(Data);
data->Enumeable.MoveBy(data->From);
for(RFTYPE::UInt32 i =0, end = data->Steps; i < end;++i, ++data->Enumeable)
{
data->Function(data->Enumeable);
data->OverallWork->Decrement();
}
}
template<class C, typename FUNCTION>
void ForEach(const C& Enumerable, FUNCTION Function)
Das ForEach erzeugt entsprechend der Elemente in dem Container für jeden Thread im ThreadPool ein Task und enqueue diese. Damit das ganze auch Funktioniert, hab ich ein bisschen Arbeit in ein neues Enumerator Konzept gesteckt.
Code:
struct GenericEnumeratorType{};
template<typename T, class ENUMERATORTYPE = GenericEnumeratorType>
Der neue Enumerator besteht aus 2 Template Parameter, 1. der Elementtyp und 2. Enumeratortyp. 2. ist default GenericEnumeratorType und dient nur als Möglichkeit Enumeratoren zu spezialisieren. Alle Methoden sind mit compiletime asserts gespickt, damit jeder Zugriff auf ein nicht spezialisierten Enumerator nicht baut. Nun kann ich für unterschiedliche Containertypen Enumeratoren erzeugen und jeden Container einen Enumerator zuweisen, indem den passenden 2. Template Parameter verwende.
Assert(m_Current < m_Start + m_Elements, "Out of bound");
auto result =*this;
++m_Current;
return result;
}
...
Ein Enumerator für Array hab ich bereits gebaut und wie man schnell merken sollte ist er sehr spartanisch, keine Vererbung, keine Constructor/Destructor/CopyConstructor, assign operator oder ähnliches, was für bösen vtable oder kompliziertere kopier mechaniken sorgt. Dies bedeutet aber auch, dass der Enumerator in Probleme läuft, wenn man den Array in der größe verändert. Die Implementierung ist schneller aber man sollte nicht selber mit dem Enumerator rum hantieren und nur den Funktionen von Algorithm überlassen oder gefahr laufen Fehler zu machen.
Soviel zum Code nun mal zu ein paar Zahlen. Die neue Variante brauchte am Anfang 1115Cycles und die alte 15Cycles im worst case Debug Build. Das war schon übel und mit meinen RAII ScopeTimer hab ich dann die einzelnen Blöcke ausgemessen und fest gestellt, dass nahezu die ganze Zeit in das Enqueue, vom ThreadPool ging. Der ThreadPool hatte mit Mutex, Semaphore und Condition gearbeitet, langsam war hier nur das Mutex, welches gelockt hat, weil 9 Threads drum gekämpft haben(8 Worker und der Hauptprozess). Da ich schon länger auf ein lockfree System umstellen wollte, hab ich ein weilchen gegoogelt und meine Queue Klasse entrümpelt und lockfree gemacht. Bei lockfree wird zwischen mehreren Varianten unterschieden, multiple Consumer/single Consumer und multiple Producer/single Producer in jeweiliger Kombination. Ich brauche eine queue welche mutliple Consumer und multiple Producer fähig ist, weil mehrere Threads Jobs übergeben können und die Threads im ThreadPool gleichzeitig sich aus der Queue bedienen. Nach einigem hin und her, diversen threading Probleme hat es funktioniert und hatte nun 86Cycles für die neue Variante und 15Cycles für die alte im worst case Debug Build.
Nun hat noch das Task sheduling in den Worker Threads gestört, die haben versucht ein Task zu bekommen, wenn dies nicht ging, dann haben die mal ein nickerchen gehalten und es erneut probiert. Dafür hab ich Sleep(1) verwendet. Ein bischen recherche hat ergeben, dass Sleep(0) in dem Fall viel besser ist, weil er nur schlafen geht, wenn ein anderer Thread auf dem Kern arbeiten will und dann auch nur für die restliche Zeit für den aktuellen time-slice. Die Betriebssysteme geben jeden Prozess eine feste Zeit N(Windows üblich 16ms und mehr wenn man den Prozess zu Echtzeit hoch stuft und weniger wenn man ihn als Background Service startet) und je nach dem wann ich z.B. Sleep(1) mache, kann ich zwischen 1-15ms wieder die Kontrolle bekommen. Sleep(0) ist toll, wenn man nicht auf einem Mobilen Gerät arbeitet, denn Sleep(0) garantiert 100% CPU Auslastung ^^ Sleep(1) hält die CPU auf ein paar % und je höher Sleep je niedriger die Auslastung. Mit Sleep(0) bin ich bei 22Cycles mit der neuen Variante, 8Cycles für die alte im Debug Build und 15Cycles für neue und 9Cycles für alte im Reelase Build gekommen. Leider hab ich auch ein VS2013 Update2 installiert, was wohl effizienteren Code baut.
Man sollte bedenken, dass ich nur 3 Werte durchlaufe und dadurch auch nur 3 von 8 Threads verwende und parallelisierung eigentlich erst bei mehreren Werte die Muskeln spielen lässt.
Zu den Messergebnisse kann ich noch sagen, dass ich den QueryPerformanceCounter verwendet habe und auf die Frequence abfrage verzichtet habe, da ich nicht an Sekunden Umrechnung interessiert war und es auch das Ergebniss ungenau macht. Noch dazu tut meine CPU die Frequence nicht ändern, die ist auf ~3,4Milliarden Cycles pro Sekunde fest genagelt, das hab ich extra nach gelesen. Es gibt nun auch ein neuen MSDN Artikel zu den Counter unter Windows. Ich hab dann auch noch ein Intel Paper zum messen raus gekramt und mein ScopeTimer aktualisiert.
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2622 Wohnort: Berlin
Programmiersprache: Go, C/C++
Wahnsinn, ich hab 3 Wochen lang kein commit gemacht und entsprechend viel Code hatte sich angesammelt :\
Zitat:
2 commits / 2,412 ++ / 2,252 --
Strings Das gröbste an der String Klasse ist getan. Die neuste Variante hat keine char* kompatibilität mehr, für Operationen, weil das einfach unsicher ist und Entwickler sich natürlich dran gewöhnen. In C und C++ wird zwischen C-Strings und String-Literals unterschieden. 1. sind die als char* bekannten Variablen. 2. sind zur compiletime bekannte Zeichenketten, wie z.B. "Hello World!" oder "true".
Der Versuch, ein String-Literal über andere Verpackungen zu erkennen ist in beiden Standards noch nicht gelöst aber es gibt Code im C++11 Standard, der für die gängigen Compiler gleiches erkennungsverhalten hat. Dabei werden Traits kombiniert. Dies hebe ich mir für spätere optimierungen auf.
Code:
charconst* text ="Hello World";
Dieser Codefetzen z.B. kann aber muss nicht ein String-Literal werden, dafür hat der Standard zuviel Spielraum gelassen.
Code:
String text("Hello World");
Dieser Codefetzen hingegen lässt kein Spielraum in bezug auf den Constructor. Der Compiler muss
Code:
charconst(&Other)[N]
als parameter übergeben, also die Referenz zu einem char array mit größe N und dieser ist nicht veränderbar. Sollte es solch ein Konstructor nicht geben, dann darf der Compiler über 1 Schritt noch ein Umweg probieren, z.B. als const char* oder char const* aber auch andere Objekte erzeugen, wofür ein Konstruktor existiert. Wenn man nun ein Constructor baut, der wie
Code:
String(charconst(&Other)[12]);
aus sieht, dann funktioniert der "Hello World" String-Literal, weil Hello World 11 Zeichen + 0 Byte terminierung 12 Zeichen lang ist. Wollte man nun ein anderen String übergeben, dann braucht man natürlich ein weiteren Konstruktor für diesen und weil niemand gerne viel Code schreiben will, kann man dies mit einem Template erschlagen.
Code:
template<int N>
String(charconst(&CString)[N])
{
String(&CString[0], N);
}
Das hat allerdings nicht nur Vorteile, weil nun für jede String-Literal Länge, was man in seinem Code benutzt, nun auch ein Constructor generiert wird. Dies macht die Binary größer, trotzdem ein guter Trade-off. Das Wissen, wie größ ein String ist macht ein großen unterschied in der Verarbeitung. Es kann z.B. kein Speicherüberlauf geben, wenn der String kaputt ist, bestimmte Optimierungen können angewendet werden, wie z.B. SIMD und Lokale Buffer. Neben den Konstruktoren biete ich noch eine statische Funktion
um doch noch die Konvertierung zu ermöglichen. Diese verwende ich aktuell bei Konsolen Parametern, weil diese weder größen Informationen mit bringen noch als String-Literal kommen können. OS API's z.B. bieten fast immer Buffergrößen an, entweder als Rückgabewert/Parameter oder als Konstanten/Defines und so kann ich die nutzung von UnsafeStringCreation an einer Hand abzählen.
Der C++11 Standard ermöglicht auch Post und Pre String-Literal Operationen zu erzeugen.
Code:
float rad = 9grad;
charconst* upperCase = uppercase"lowercase";
Diese gab es schon vor dem Standard in folgender Form.
Code:
char const* str = T"Text";
wchar_t const* str = L"Text";
// neu
char const* str = u8"Text";
char16_t const* str = u"Text";
char32_t const* str = U"Text";
Damit will ich mich auch noch mal intensiver beschäftigen, ob es da nützliche Anwendungsfälle gibt.
Womit ich mich nun auch noch beschäftigen werde sind folgende Punkte. -die letzten String Operationen hinzu fügen -Kommentieren der String Klasse -noch fehlende Unit Test für String schreiben -übliche Operationen in Performance Test verpacken -weitere optimierung von String durch SIMD -den ersten Stück Code als final für Radon Framework 1.0 betrachten
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2622 Wohnort: Berlin
Programmiersprache: Go, C/C++
Kein wirkliches Update aber trotzdem eine wichtige Information. Ich hab heute den Support für VC++ 2010 als beendet erklärt. Nun wird VS2013 Supported und das ermöglicht mir einige bessere Änderungen im Code. Ich hab z.B. auf eine neuere FastDelegate Version gewechselt, welche Variadic Templates verwendet und somit diesen Code wesentlich kleiner und verständlicher macht. Des weiteren hat das Auswirkungen wie die Delegates für meine Event und Signal Klasse erzeugt werden. Die sind nun auch logischer, weil sie einer Funktion entsprechen und nicht einer recht schrägen aufzählung von Datentypen.
Code:
typedef Delegate1<CONAP> DefaultMethod; // alt
typedef Delegate1<void(CONAP)> DefaultMethod;// neu
Die Delegate1, Delegate2 und so weiter werden demnächst raus fliegen, das waren Macros um zu wissen was Parameter und was ein Rückgabetyp ist.
Ich will auch noch einige weitere Funktionalitäten von VC++13 verwenden z.B. Stringliterals und explicit conversion operator. Im großen und ganzen eigentlich wegen Typensicherheit und einfach lesbaren Code.
Ich hab meine Projekte ein weiter zerlegt wie man sehen kann. https://github.com/tak2004 So hab ich z.B. ein Fast Entity Component System Implementierung auf basis von Radon Framework gemacht und in kürze werde ich noch ein Converter Tool auf basis von RF rein packen.
Das Tool erzeugt ein TCP Server, FileWatcher, Plugin System und LuaJit. Wenn sich ein Asset im Target Directory verändert, dann kommuniziert der FileWatcher dies an LuaJit und es werden alle Scripte ausgeführt, die im Scripts Ordner hinterlegt sind. Diese Scripte können dann anhand des Dateinamens entscheiden was zu machen, z.B. Lua, LuaJit und Plugin gelieferten Funktionen aufrufen und dann dem Tool zurück geben, welche Assets erzeugt wurden. Diese Informationen werden dann an alle Verbundenen TCP Clients gesendet und diese können dann z.B. per Hot Asset loading resourcen neu laden. Das ganze hab ich vor ner weile mal geschrieben und nie weiter ausgebaut, da ich aber am Wochende angefangen hab Zero-Prime(ein Spiel was mich Ursprünglich in dieses Forum brachte) zu Programmieren hab ich diverse alte Projekte und Codes raus geholt und neu aufgebohrt.
So hat z.B. RF ein OpenGL Context hinzu bekommen, der immer den höchst möglichen Context erzeugt, denn ich plane auf OpenGL 4.3-4.4 als min Specs zu setzen. Ich hab auch namespace alias eingebaut, um nicht immer mit den langen namespaces zu kämpfen.
Ein CMake generierter include header, der alle header included und per defines bestimmte namespace abschaltet ist nun auch drin. Da hab ich mich an das Windows.h Konzept gehalten.
Code:
#define RF_NO_REFLECTION 1
#define RF_NO_NET 1
#include <RadonFramework/Radon.hpp>
Bisher sinde meine Header recht gut mit forwad declaration gespickt und es kompiliert alles sehr schnell, so dass ich bisher kein Grund sehe überhaupt was nicht zu includen. Dank VC++13 Ultimate kann man auch sich die Include Bäume als Diagramme generieren und dann gezielt vereinfachen.
Da ich für Zero-Prime plane fmod studio, bullet3, luaJit, PhysFS, RakNet, AssImp, SQLite, libRocket, NV/AMD Toolchain zu verwenden fallen eigentlich kaum Neuerungen im Bereich RF an. Ich werde die Sound und Database ServiceLocator wohl wieder ins leben rufen und für luaJit kann ich eventuell mein Reflection System auf ein neueren Stand bring.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2622 Wohnort: Berlin
Programmiersprache: Go, C/C++
Ich hab meine OpenGL LibOpenGLSpecification und OGLVMCodeGenerator weiter ausgebaut. Die Lib kann nun auch die Konstanten auslesen und der generator generiert nun entsprechende Header für die Konstanten. Output Hier ist der aktuelle library code. Ich will nun endlich GLEW aus mein Projekt bekommen, dass frisst immer mehrere hunder Kilobyte in der Binary und nur weil die unglücklich geschrieben wurde. Ausserdem hat es seit 2 Jahren ein Bug, der die Extension mit der falschen Funktion aus ließt. Seit OGL3 muss man die Extensions anders erfragen.
Produziert wesentlich mehr calls aber hey, die reden nicht mit der GPU und sind wesentlich Fehlerresistenter. Benutzt man noch den alten Weg ab OpenGL 3, dann wird ein Fehler generiert. Okey nicht ganz, NV hält sich wie üblich nicht an die Specs und nur im Debug mit KHR_DEBUG bekommt man da ne Warnung aber AMD gibt wie erwartet auch im normal betrieb ne Fehlermeldung Bei dem aktuellen OSX wird dann auch gleich die Anwendung mit einem Fehler beendet, weil OGL3 minimum ist(so durfte ich es im GLEW Bugtracker lesen).
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2622 Wohnort: Berlin
Programmiersprache: Go, C/C++
DateTime hat eine Format Funktion bekommen, welche auch lokalisiert. Unter Windows hab ich strftime verwendet.
In meinem Netzwerk Code hab ich 2 Speicheroptimierungen vor genommen, die mir beim erweitern des Radon Converter aufgestossen sind. Ich habe in beiden Fällen Laufzeit allokationen gemacht die jeden Frame immer wieder liefen und diese haben die meiste Prozessorzeit aufgefressen.
Es gibt nun in der FileSystem API eine Funktion RealPath, welche die gegebenen Pfade auflöst. Die klassischen Fälle sind z.B. ./ oder ../ .
Ich habe auch ein mini WebserverWeb++ in Radon Converter implementiert. Dieser soll dazu dienen in das Log zu gucken und genaue Fehlermeldungen zu erhalten. Die Fehlermeldungen werde ich mit ID's abkürzen und detailierte Beschreibungen über die Webbeschreibung liefern.
Der nächste Schritt ist mDNS ins Radon Framework zu bringen und dieses für Radon Converter zu verwenden. Mein Ziel ist, dass die einzelnen Radon Converter Instanzen sich im Netzwerk finden, ohne etwas zu konfigurieren und dafür wurde mDNS entwickelt. Es gibt leider keine Bibliotheken, die ich direkt nutzen könnte und hab beschlossen eine eigene Spec implementierung zu machen. Meine Referenz sind hierbei mdnsd und wxservdisc. Sobald dies abgeschlossen ist werde ich Radon Converter in ein seperates GitHub Projekt verschieben und von dort aus weiter entwickeln.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Das Bild stammt aus meinem Radon Converter, welcher kein Fenster hat und die Konsole will man in der Regel nicht sehen, so hab ich SystemTray Icon und Popup Menu eingebaut. Folgender Codeblock zeigt die integration
Code:
// Definiere Programmspezifische Aktionen
namespace Actions
{
enum Type
{
ToggleHideCmd=1,
Exit
};
}
...
// wenn ein SystemTray verfügbar ist dann benutzt es
// SystemTray ruft intern den SystemTrayServiceLocator an und fragt jeden registrierten Service ob er auf dem System funktioniert.
// Dies ist aktuell recht sinnfrei bei Windows aber wenn ich mal die Linux Backends schreibe,
// dann brauch ich verschiedene Services für KDE und GNOME.
mDNS Ich hab auch weiter am mDNS gearbeitet aber bin da erstmal irgendwie nicht weiter gekommen, die Kommunikation wollte einfach nicht wie ich wollte. Mein Plan ist den Code noch ein paar Tage liegen zu lassen und dann wieder rein zu gucken aber ohne fest gefahrenden Blick. Service Discovery ist so super cool und man kann Tools soviel besser mit sowas machen aber eigene Protokolle will man doch nicht schreiben und irgendwie gibt es einige Homebrew Lösungen im Netzt und dann beim mDNS Standard auch nur 2-3 Implementierungen die nicht begeistern. Testen tu ich mit dem Bonjour SDK und iTunes(bietet diverse services zum discovern). Am liebsten hätte ich ein mDNS Drucker oder ein anderes Stück Hardware im Netz aber leider hab ich sowas nicht :\
RadiumDB Ich brauch für Radon Converter eine Meta Daten Datenbank, welche text basiert ist. SQLite und ähnliche leichtgewichte mangelt es an klartext, denn sobald man die Daten mit versionieren will, dann ist binary immer ein Problem. Man nutzt in der Regel Locks um dann Kollisionen zwischen Leuten zu verhindern und das ist in der Praxis einfach übelst Zeitfressend. Ein weiteres Problem bei Versionierung ist das Branching, wenn man monolithische Datein wieder zusammen führt, dann können sehr viele Konflikte entstehen, die schwer beherrschbar sind. Wenn man die Daten auf einzelne Datein verteilt, wie z.B. bei Cassandra, MongoDB, CouchDB, dann wird sowas viel einfacher. Metadaten sollten auch nicht festen Strukturen wie z.B. bei Tabellen der Fall ist unterliegen, diese sind üblicherweise recht unterschiedlich in der Praxis.
Daraus hab ich geschlossen das ich also eine InApp, lesbare, Objekt orientierte, NOSQL Lösung brauche. Ich hab ein weilchen gegoogelt und für C++ nix vergleichbares gefunden und ehrlich gesagt ist sowas nicht wirklich kompliziert. Während meiner Schulzeit hab ich schon mal ein SQL98 DB in Freepascal geschrieben und da hatte ich noch kein Internet und zugriff auf diverse Opensource mini libs wie mapreduce.
Ich werde heute noch anfangen mit dem Konzeptionieren und Diagramme malen aber lange wird das nicht dauern. So will ich z.B. kein serialisieren zu C++ Objekten einbauen, das wird alles auf String Basis bleiben aber Formatbeschreibungen wird es geben(ich will das ein bisschen wie CastleDB machen). SQL werde ich wohl einfach mein alten Code portieren und das Laden/Speichern werde ich abstrahieren, damit man eigene Loader/Writer benutzen kann(z.B. XML).
Das ganze macht auch für Spiele Content selber Sinn, z.B. für die Level Editor Daten. Der Sound Mensch kann am Sound arbeiten, der 3D Grafiker ersetzt die Modelle und ein Game Designer balanced oder weißt Logik zu. Probleme dies bzgl. haben wir auf Arbeit doch eher regelmäßig ^^
Profiling Als ich für ein Forum Beitrag im Netz was gesucht hab bin ich auf JRocket Mission Control gestoßen und ich bin sehr von angetan. Viele der Kniffeligen Dinge decken Tools wie gperf, Concurrency Analyzer, CodeXL, GPUPerf Studio, VMMap wesentlich besser als JRocket Mission Control ab aber die VM spezifischen und Sprachspezifischen dinge kennen diese Tools natürlich nicht. So kann man z.B. auch den GC, Logger und andere Komponenten überwachen und ein Vorteil zu meinem aktuellen Vorgehen für diese Art von Daten hat es. Ich könnte entsprechenden Code erst an schalten, wenn ich ihn brauche und dann wieder abschalten. Ein Mini Webserver ist eh schon in Radon Framework drin und diese mit den Komponenten im RadonFramework::Diagnostic Namespace zu verbinden wäre nicht so schwer. Mein Problem mit sowas war bisher immer das Problem interaktion und Graphen zeichnen. Allerdings haben wir mitlerweile HTML5 und Canvas, daher will ich dieser Möglichkeit eine 2. Chance geben, denn zu HTML4 Zeiten hab ich unmengen Code schreiben müssen um einfache dinge zu machen und JSON hab ich damals auch ned benutzt. Wann ich daran mache steht aber in den Sternen. Ein bisschen hat das überschneidung mit meinem Radon Converter, weil ich für die Meta DB auch ein Webinterface benutzen werde(wird ein Button im neuen TrayIcon Popup Menü geben).
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2622 Wohnort: Berlin
Programmiersprache: Go, C/C++
Seit mein letztem Update ist einiges passiert und ich hab wenig Zeit gehabt hier zu schreiben. Ich habe im April mein Job gewechselt und bin von der Spielebranche(Bigpoint) in den Luft- und Raumfahrt(meiner eigene Firma Part Time Scientists) gewechselt. Die Firma hatten wir schon vor 5Jahren gegründet aber wie der Name verrät war das alles Freizeitmäßig und eine Namensänderung zu PTScientists ist schon im gange. Wie das so bei selbständigkeit ist, hat man eigentlich keine Freizeit mehr und entsprechend komme ich kaum dazu am Radon Framework und dazugehörige Projekte zu Arbeiten. Das schlimmere ist, ich habe soviel Spielzeug womit ich arbeiten will, z.B. Oculus Devkit, Leap Motion, NV K1 Devkit, Mac Mini für iOS und OSX Entwicklung, Ultimaker 2 und demnächst lerne ich Solidworks. Ich hab noch ein Projekt, was ich im März eigentlich Privat starten wollte mit in die Firma genommen und arbeite an der Produktionsreife bis Ende diesen Jahres. Dabei geht es um ein Scanner der Teile des BRDF Models erfassen kann und den wollen wir im November rum auf Kickstarter vorstellen. Nebenbei kümmer ich mich um unser Office und die Erweiterung auf ein weiteres.
So ist das Ausmaß des Wahnsinns bekannt und nu zu den Updates ^^ Radon CMake Framework Hier sind viele kleinigkeiten passiert, da ich das auch für meine normale Arbeit verwende. -Ausführbare Projekte haben ein Fenster Modus Flag, um ohne Konsole zu starten. -Integrate.cmake enthält nur noch das absolute Minimum um das Repo zu laden und führt dann den eigentlichen Code aus. -ProjektIDs können nun Repos(git/svn), Download(http), Pfade zugewiesen werden und werden geladen und eingebunden, wenn ein aktives Projekt es benötigt. -Man kann über die ProjektID nun raus bekommen wo ein Projekt liegt(wichtig wenn man es aus dem Inet lädt). -Wenn mehrere Projekte das gleiche Projekt Integriert haben, dann wurde der CMake Befehl add_subdirectory mehrmals aufgerufen, was schlecht ist und das Framework besitzt nun dafür rcf_add_subdirectory_once. -Viele Bugfixes. Um das Framework zu benutzen braucht man nur noch die Integrate.cmake in sein Projekt packen, z.B. https://github.com/tak2004/RadonFramewo ... rate.cmake . In der eigenen CMake file muss man dann noch diese einbinden und kann auch schon externe Projekte Integrieren.
RadiumDB Ich hab meine Document basierte Datenbank, die ich für den Radon Converter verwenden will von SVN auf GIThub umgezogen. https://github.com/tak2004/RadiumDB Die ist aber nicht Funktionsfähig und ist aktuell eher im Interface design Status. Ich hab als letztes angefangen die Datenmanipulation zu designen. https://github.com/tak2004/RadiumDB/blo ... m/JSON.cpp Ich werde es wie bei ZeroPrime mit den Daten Handhaben, in dem ich ein String zur beschreibung verwende und den Hash mit einer Hashliste aus dem Dokument generiert vergleiche. Damit steigt die Ladezeit für JSON, XML basierte Dokumente aber für mein Natives kann ich die Hash(key)-Value abspeichern und somit ohne zwischenschritt arbeiten. Wer das System nicht kennt kann hier mal reinlesen.
Durch '.' wird ein Member makiert und '[]' makiert eine dynamische Liste, wofür als parameter der angeforderte Index mitgegeben wird. Der ganze String kann als hash abgespeichert werden, da er sich nie ändern kann, denn die Indices sind nicht Teil des Strings. Um die größe des jeweiligen dynamischen Members zu erfahren nimmt man den String, der bereits angegeben wurde und wenn man das Element zugreifen will, dann nimmt man den Hash vom String und fügt die indices mit '_' dran, der daraus folgende Hash ist das eigentliche Element. Also, wenn der Hash von "people[].Phone[]" 1234 wäre dann würde man "1234_42_0" als String erstellen und den Hash daraus als Key für das Element verwenden. Der Hash für "people[].Phone[]" kann zur compiletime erzeugt werden und taucht garnicht erst im Code auf. Was im Code auftaucht ist dann grob reduziert.
Radon Examples https://github.com/tak2004/RadonFrameworkExample Ich hab 2 weitere Beispiele hinzugefügt, eines für SystemTray und eines für Fenster. In dem Konsolenbeispiel hab ich noch den neuen Debugger output hinzugefügt, der aktuell zwar nur Visual Studio Debug Konsole unterstützt aber XCode hab ich auch schon geplant und in die API geschaut.
Radon Framework -Ich hab Radon CMake Framework Datein rausgeworfen, da nur noch die Integrate.cmake benötigt wird. -SystemTray API ist vollständig drin -Regex support ist hinzu gekommen(dank C++11) -UUID support hab ich eingebaut, welche mehrere Versionen unterstützt. -Ich bin vom alten C++ function definition auf die neue C++11 using syntax umgestiegen. -Die C++11 delete modifier für Konstruktor/Destruktor und Operatoren hat einzug gehalten und z.B. NoInstance und NotCopyable Idiom ersetzt. -Eine Erweiterung der Atomic Counter um addieren und subtrahieren von spezifizierten Werten. -Ich benutzte nun C++11 override modifier für Virtuelle Funktionen. -AutoPointerArray ist nun mit Concurrent Namespace Fähig. -IPAddress hat nun weitere Resove Funktionen von Strings bekommen und erlaubt auch nun den internen ByteArray ab zufragen. -Server Klasse ist nun überarbeitet und Funktioniert -Socket Optionen sind nun wieder verfügbar, gefixt und stark erweitert. -Man kann nun den echten Systempfad von einer URI abfragen oder auch Systemkonstanten wie z.B. %TEMP% unter Windows. -Systemfunktion Swap für Speicher ist hinzugekommen und mit SSE2 Branch Hardwarebeschleunigt verfügbar. -Man kann nun Datein mit dem vom System vorgesehenden Default Programm starten(z.B. eine html file mit dem Firefox). -Man kann sich nun die Zeit als String formatiert zurück geben lassen. -Namespace Text und Util sind hinzugekommen. -PixelFormat Klasse für Bildverarbeitung ist hinzugekommen. -IDecoder und IImageDecoder sind nun Decoder und ImageDecoder, sowie ein größerer umbau der API. -Zu den Decoder gibt es nun ein passenden Encoder gegenstück. -Systemfunktion Fill für Speicher ist hinzugekommen. -List::Size ist nun List::Count um ein einheitliche Namenskonvention zu haben. -Diverse Glew Codepunkte sind rausgeflogen und durch Generierten Code ersetzt worden(GLEW ist groß und nicht immer aktuell und hat sogar bugs). -Floatingpoint Math hat nun eine Clamp und Round Funktion. -MulticastRequest Klasse für mDNS hinzugefügt. -Membership Fuktionen für Socket Optionen hinzugefügt. -Erste ServiceDiscovery Klassen Implementierung. -Visual Studio Debugger output -Debugger Klasse für Spezielle Debug Funktionen hinzugefügt. -Überarbeitung und ausbau der WaitTill Mechanik im ThreadPool. -Namespace RFENV heisst nun RF_SysEnv -Font Service hinzugefügt, der alle System bekannten Schriftarten ermittelt. -Neue Systemfunktionen für ermittlung der Sprache, Ort und in Nativer Sprache des Users(ISO639 und ISO3166 = en_US, en_GB).
Was kommt als nächstes Ich versuche natürlich immer noch mDNS fertig zu machen, da ich es für den Scanner verwenden will. Der Scanner meldet sich auch schon als solches im Netzwerk nur mag mein eigener Code den nicht finden, der Bonjour Browser findet ihn aber :.( Der Teufel steckt halt im Detail. Die Prio ist hier allerdings gerade recht gering.
RadiumDB ist nicht Zeitkritisch und hat auch keine hohe Prio und da bastel ich immer mal ein stück weiter dran.
Aktuell springen extrem viele Spiele auf HTML5/Javascript basierte UI um und ich kann das nicht verstehen, weil es ledeglich für ein Web-Programmierer praktisch ist aber der ist nicht der Designer und Web-UI und Game-UI sind 2 Paar Schuhe. Ich hab nun schon mit Scaleform, libRocket und Berilium gearbeitet und die sind alle sehr entäuschend. Scalform verbiegt sich so, dass es an Flash Editor passt und die anderen beiden verbiegen sich um an HTML zu passen. HTML ist sowas von ungeeignet für Performante UI und der Standard ist gigantisch komplex.
Ich will mal an mein Form Namespace weiter arbeiten und ein paar Komponenten sowie ein einfaches layouting einbauen. Als Basis nehme ich NanoVG und OUI. Das kommt Delphi schon recht nahe mit seinem Canvas, wo die Zeichenroutinen liegen und den Komponenten, die Canvas benutzen. Bei Radon hab ich ein Form, welches ich ein Canvas3D hinzufüge, um OpenGL zu rendern und das werde ich als Canvas verwenden. Als erstes will ich Schrift hinzufügen, daher hab ich schon angefangen und ein Font Service hinzugefügt und auch zugriff auf die User Sprache garantiert. Damit kann ich dann alle Schriftarten raus filtern, die Script Kompatibel zu der aktuellen Sprache sind und von diesen kann ich mir die Outline geben lassen. Ich werde das ganze über quadratische Berzierkurven rendern, damit hab ich in der Vergangenheit schon gearbeitet und sie sind schnell, Auflösungsunabhängig und verbrennen nicht die Leistung wie bei Font distance field rendering oder reinen textur rendering. Ach und die Outlines von Glyphen werden mit quadratischen Berzierkurven beschrieben, hat auch ein Vorteil ^^
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2622 Wohnort: Berlin
Programmiersprache: Go, C/C++
In den letzten 1 1/2 Wochen hatte ich mal wieder nahezu normale Freizeit Bedingungen und hab diese in folgende Baustellen gesteckt.
FontService Wie ich beim letzten mal schon erwähnt habe, kann ich seit kurzem die Usersprache abfragen und bekomme bei mir z.B. de_DE bzw. en_US, wenn ich mal wieder ausversehen alt+shift gedrückt hab und somit zwischen den Sprachen gewechselt habe. Nun habe ich im FontService weiter gemacht und kann nun alle System Fonts erhalten, die Verfügbar sind und die Filterbedingungen bestanden haben. Ich kann nun die Unicode Range IDs für die jeweilige Sprache abfragen und alle Fonts raus werfen, die nicht die Glyphen enthalten. Dann kann ich noch nach bestimmten Schriftstielen und Orientierung der Zeichensetzung aus sortieren.
Nun bekomme ich nur noch die Schriften und Variationen, die Latin und Latin Extended enthalten, Horizontal ausgerichtet sind und mindestens Normale Schriftstiel besitzen. Nun sind noch knapp 100 von vorher ~1200 Schriften über und die haben auch noch einige Variationen.
Was da noch Fehlt ist die Möglichkeit raus zu bekommen ob die Sprache vertikal oder Horizontal ausgerichtet ist, in welche Richtung es verläuft und das laden der Vektordaten aus dem Font.
Screen Ich habe die Screen Klasse und was so an API hinter steckt weiter ausgebaut. Die Klasse ist dafür zuständig, dass man raus bekommt welche Monitore am System angeschlossen sind und welche Auflösungen Unterstützt, aktuell benutzt wird und die Änderung der Auflösung. Ich habe die API so erweitert, dass man nun auch die Position im Virtuellem Desktop abfragen kann und die DPI des Monitors.
Folgender Code holt sich alle Monitore und erstellt für jeden ein Fenster, positioniert es in der linken oberen Ecke des jeweiligen monitors und korrigiert die Größe entsprechend der DPI des jeweiligen Monitor.
Code:
auto& screens = RF_Form::Screen::AllScreens();
windows.Resize(screens.Count());
for(RF_Type::Size i = 0; i < screens.Count(); i++)
Da ich noch Windows 7 benutze und erst mit Windows 8.1 eine API eingeführt wurde, die die Abfrage der DPI für ein Monitor ermöglicht und diese nicht in die älteren Windows Versionen nach gepatcht wurde musste ich ein recht umständlichen Weg gehen. EDID ermöglicht das Abfragen der interessanten Daten eines Monitors und wird verwendet, um den Monitor korrekt an zu steuern und die Fähigkeiten desssen zu bekommen. Windows kopiert seit Win2000 diese Daten bis Win10 und wohl auch danach immer in die Registry zu den Monitor Informationen und diese zu finden ist der schwere Teil. Der erste Versuch war die Registry direkt nach den passenden Eintrag zu suchen aber über die Windows Versionen hinweg liegen die Daten an unterschiedlichen stellen und teilweise mehrfach und einige davon sind nicht die die man will. Windows bietet seit Win2k die Public Device Installation API an, welche den Registry Kudelmuddel über die Versionen hinweg für einen übernimmt und den für das aktuelle Windows den richtigen Key zurück gibt.
Ich habe hier 2 Monitore ein 30" mit 2560x1600, ein 22" mit 1680x1050 Pixel Auflösung und beide haben unterschiedliche DPI Werte.
Links sieht man ein Foto von mein Handy, damit man sieht wie man die DPI korrektur im Code wahr nimmt und rechts sieht man ein Screenshot vom Desktop. Durch die korrektur sehen beide Fenster gleich groß aus aber eigentlich ist das Fenster auf dem 30" größer als das auf dem 22" Monitor. Bei den beiden Monitoren ist der Unterschied nicht wirklich dramatisch aber auf den 15" 4k Laptop macht es kein Spaß nicht skalierte Texte mit Schriftgröße 12 zu lesen. Windows 8 und 10 User sind in die Zeit gekommen wo sich so langsam die 4 und 5k Displays verbreiten und das OS ist bereit dafür aber die meisten Programme sind nicht skalierungsfähig programmiert. Im Code oben sieht man eine Division mit 96, dies ist die übliche Referenz DPI bei der Schriften und früher UI konzeptioniert wurde. Die Zahl rührt von den Röhrenmonitoren und den bis jetzt noch normalen TFT. Die moderneren TFT bzw. größere Monitore haben andere DPI Werte, wie man an mein 30" Monitor erkennen kann(siehe Foto Titelbar vom Fenster).
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2622 Wohnort: Berlin
Programmiersprache: Go, C/C++
Fast Entity Component System Ich hab vor einer weile mal ein Fast Entity Component System geschrieben, welches Radon Framework verwendet und ich für mein Technologieprototypen verwende. Ich hab das nie ganz glatt gezogen und selbst für mich war es erstmal ein bisschen Code angucken um die Strukturen wieder zu verstehen. Von daher hab ich nun das ganze mal glatt gezogen, Bugs bereinigt, Util Code ausgebaut und ein Resource Test geschrieben, der rest einfach erklärt, wie man die API nutzt. Der Test vergleich single threaded vs multithreaded skalierung mit 3000 Komponenten und 1000 Entities. Jede Komponente schläft 1ms und jeder Test läuft 10x, dass sinnvolle Werte raus kommen. Gemessen wird in Cycles und es sind 4 Kerne: Singlethreaded: 19.069.531-20.595.926 Multithreaded: 5.816.016-6.037.888 ~3,3-3,4x schneller Das System skaliert also äußerst gut die Anzahl der Kerne. Der Sourcecode liegt hier.
Radon CMake framework Ich hab einige Bugfixes und Erweiterung für mein CMake frameworks gemacht. So kann ich nun Ressourcen als Teil des Buildprozesses kopieren, was bei Visual Studio und Xamarin über die Properties geregelt wird und praktisch ist, wenn man z.B. Icons, XML Formulare oder ähnliches im Build Ordner haben will. Ich benutze das Framework in allen meinen Projekten, auch auf Arbeit und es erspart mir ne menge Zeit. Was mir noch als Feature Fehlt ist das automatische pullen von dependencies und Funktionsketten bei Downloads. Aktuell sucht er nach den Dependencies und wenn sie nicht da sind guckt er nach den registrierten Locations, wie z.B. Git, HTTP, SVN oder Umgebungsvariablen und lädt dann z.B. das Git Repo aber es wird nie geupdated. Funktionsketten würde ich z.B. beim laden einer Zip für das entpacken nutzen. Da ich bisher noch kein externes Codeprojekt hatte, wo ich nur ein Archieve bekommen konnte oder noch was nach dem laden der Abhängigkeit noch was machen wollte, hab ich das bisher nicht eingebaut.
Xenon Xenon war ursprünglich der Versuch Bildverarbeitung zu machen und dann die Kritischen Stellen als API in Radon Framework zu bringen. Als Kritische Stellen zählten hier Performance Bottlenecks und immer wiederkehrender Code. Ich habe es dann irgendwann liegen lassen, weil ich kein Verwendungszweck für hatte. Später hab ich mit dem Code einige Möglichkeiten von Transformationen ausprobiert, um die Kompression von DXT zu verbessern. Ich wollte eine verlustfreie Kompression und die Größe möglichst in die Nähe von Texture Crunch(verlustbehaftet) bringen. Input DXT5: 349.680 Bytes Texture Crunch: 150.051 Bytes(verlust) JPEG: 128.663 Bytes(verlust) DXT5+lzma2: 283.045 Bytes(kein verlust) DXT5 in streams pro Komponente zerlegt + brotli: ~240.000 Bytes(kein verlust, aktuelle Xenon Variante) ASTC: 29.600 Bytes(verlust ca. Texture Crunch Quali, benötigt DX12 Hardware) Ich wollte noch Move-Front und Burrow-Wheeler auf die Streams ausprobieren aber hab noch keine Motivation gefunden mal weiter dran zu arbeiten. Sollte das nix bringen könnte man noch mit compressed sensing experimentieren oder verlustbehaftete low pass Daten quantisieren. Was mir noch zu sagen bleibt, guckt euch brotli an, wenn ihr bessere kompressionsraten als lzma2 und nahe deflate decompression Geschwidnigkeit haben wollt.
Radon Framework Ich hab ein quadratisches Logo für Icons gemacht, was auch beim klein skalieren noch recht eindeutig ist.
Dateianhang:
rn.png [ 26.26 KiB | 61680-mal betrachtet ]
Okey war jetzt nicht der so tolle Wurf, bzgl. neuerungen Ich lese dann mal aus dem Commit Log vom git vor ^^
WDM ist der Windows Desktop Manager und den gibt es seit Windows Vista und hat GDI für die Fensterdeko abgelöst. Die API soll schneller sein und ermöglicht bei Windows 8 und aufwärts einige weitere Informationen für das Fenster zu setzen bzw. zu bedenken(z.B. per Monitor DPI).
Ich hab nie den Device Context frei gegeben, dass passiert schon mal, wenn man Resourcen hat, die die gleiche Lebenszeit wie die Applikation selbst haben.
Wenn man je ein Fenster auf ein Monitor setzen will, dann braucht man Informationen über den Virtuellen Desktop und seine Monitore. Nun hab ich die Funktionalität hinzugefügt.
Ein bisschen refaktoring hab ich gemacht, damit die API wieder einheitlicher wird. So hab ich z.B. nun das Wort Count als Anzahl in verwendung, wo es voher häufig Size war aber das benutze ich für Datengröße. Die Compiler Defines hab ich zusammen geführt, da diese bisher an mehreren Stellen verstreut lagen. Es gibt nun auch die Möglichkeit Header und Methoden/Funktionen als deprecated zu makieren.
Ich hab eine Art Canvas API eingeführt, welche sich Draw2D nennt und OpenGL VM Code aus Shapes und Text generiert. Das ganze soll für UI sein und Basis Komponenten wie z.B. Label ermöglichen. Man hat ziemlich ähnliche SVG Befehle im Path2D Objekt und Text2D Objekte kümmern sich um Fonts. Beide Objekte generieren dann ein NativeShape, welches der Bytecode- und Resourcen-Container für meine OpenGL VM ist. Leider immer noch nicht einsatzbereit aber daran arbeite ich noch.
Ich hab an den Streamklassen gearbeitet, so gibt es nun auch ein MemoryStream und NetworkStream Klasse. Die 2. kann über ein beliebigen Stream drüber gepackt werden und kümmert sich um die Endian Problematik zwischen Client und Internet.
Meine mDNS Implementierung ist auch vorran geschritten, ich kann nun Queries absetzen und die meisten Antwortmöglichkeiten abhandeln(ein Fall ist noch offen). Ich muss noch den einen Fall abdecken und ein Registrierungsservice bauen, dann kann ich es Produktiv einsetzen. Wer mDNS nicht kennt, es ist eine UDP basierte broadcast DNS Lösung, auf der z.B. Apple alle ihre Dienste und Geräte miteinander verbindet und diverse Drucker, NAS und andere Netzwerkgeräte benutzen es um sich bekannt zu machen. Wenn man z.B. eine Software hat und man möchte, dass die sich im Lokalen Netzwerk automatisch finden und miteinander Arbeiten können, dann kann man mDNS verwenden, um Clients zu suchen und bekannt zu machen. Jeder client wird sich selbst einmal per Broadcast bekannt machen, in dem er ein oder mehrere DNS Einträge über sich sendet z.B. PTR, AAA oder SRV. Letztes sagt dass er ein Server ist mit dem Netzwerk Protokoll XY, auf Port Z und Namen sowie IP ist. Jeder andere mDNS Browser im Netzwerk bzw. auch DNS Server merken sich nun das und wenn wer anderes nach dem Protokoll fragt, dann bekommt er alle Server die das Protokoll bekannt gemacht haben. ZeroConf, Bonjour und Avahi nutzen dies. Ich hab z.B. mein Raspberry Pi mit Avahi und 2-3 Zeilen XML als Service im Netzwerk registriert, der hostname.PBMS.tcp.local. als Namen hat und auf Port 19999 verfügbar ist. Mein Programm sucht dann nach PBMS.tcp.local. und findet den Pi mit IP, Alias, DNS, Infos, Port und so weiter. Nun kann ich, weil ich das PBMS Protokoll spreche auf den Port und IP verbinden, ganz ohne Konfiguration(da kommt auch ZeroConf als Name her ).
Ich hab das transparente Multithreading ausgebaut und einige Bugs gefixt. Im Framwork gibt es aktuell 3 Funktionen Exists, ForEach, FindAll, welche auf Container angewendet werden können und im Hintergrund das auf den ThreadPool verteilt wird. Ich hab nun noch die Möglichkeit hinzugefügt, das man den aktuellen Status des Queuing abfragen kann, also darf ich oder nicht. Die 3 Funktionen nutzen dies nun, um zu entscheiden ob Single threaded oder multi threaded gearbeitet werden soll. Dies hab ich im Fast Entity Component Test benutzt um Single und Multithreaded zu testen.
Ich hab angefangen ein kleines Tool zu basteln, womit ich mir den Bytecode zusammen klicken kann. Das hinterlegen von Parametern fehlt aktuell und natürlich 1000 kleine Features, die ich gerne haben möchte
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Registriert: Di Mai 18, 2004 16:45 Beiträge: 2622 Wohnort: Berlin
Programmiersprache: Go, C/C++
Dieses Wochenende wahr einfach EPIC Ich hab mal ein ganzes Wochenende Zeit gehabt und in die Tasten gehauen.
OpenGLVM OpenGLVM hab ich nun mal zusammen gekratzt und in ein Repo gepackt. Davon ist noch nix wirklich in einen "ich spiel mal mit rum" Stand. Die test Applikation benutzt mein Fast Entity Component System, damit ich recht einfach multithreaded rendering in echter Wildbahn ausreizen kann. Sobald ich die Variablen Funktionalität fertig hab kann man auch praktisch mit rum spielen. Im Repo liegt ein .Net C# Tool um sich Intermediate Code zusammen zu bauen aber da fehlt noch das zuweisen von Parametern, drag&drop innerhalb von den States und das speichern in das neu definierte Intermediate Format.
Radon Framework Ich hab am Wochenende den Linux Port begonnen und unter Ubuntu 15, sowie Fedora 20 baut und startet nun auch die Test Applikationen, um dann mit einem threading problem zu explodieren. Ich hab vor einer weile ein runtime dispatching Mechanik entwickelt und damit meine Systemfunktionen implementiert. Daher kann ich kompilieren und linken obwohl ich keine Systemfunktionen für eine Platform hab. Erst wenn ich auf solch eine zugreifen möchte, wird der dispatch Mechanismus gestartet und wenn danach die Funktion noch der Dispatcher ist, dann wird das Programm abgeschossen und vorher noch darauf hin gewiesen, dass die Systemfunktion implementiert werden muss. Ich hab recht lange gebraucht mein Code zu GCC und CLang kompatibel zu bekommen aber nachdem das ging konnte ich fix die Tests zum laufen bringen.
Mein Plan ist nun den Linux Ubuntu, Fedora und OSX support zu zementieren. Testen tue ich das ganze auf PC, raspberry pi und Tegra TK1. Mein Fokus liegt hier erstmal auf Sockets(für mdns) und Render-Context(für openglvm).
Als ich setaffinity für threads unter Linux implementiert hab bin ich auf eine recht interessante lib gestoßen. HWLoc ist eine cross platform c bibliothek, die sich um die Erkennung von Speicher und CPU kümmert, sowie auch thread und memory binding kann. Ich überlege aktuell die Bibliothek an zu binden, da ich aktuell den Code für das erkennen von CPU und Speicher selber schreibe und das zu portieren viel Aufwand ist. Was mich noch aktuell davon abhält ist der Umfang, ich will mir das noch genauer angucken bevor ich ein Rattenschwanz an Abhängigkeiten, weiterem Code oder ähnlichem ins Framework hole.
Xenon Ich hab das Projekt getötet ^^ ASTC hat nun sein Einzug in die DX12 Hardware gehalten und ist auch mitlerweile verfügbar. Es macht einfach kein Sinn das Projekt weiter zu führen, da ASTC letzlich die bessere Wahl ist.
_________________ "Wer die Freiheit aufgibt um Sicherheit zu gewinnen, der wird am Ende beides verlieren" Benjamin Franklin
Mitglieder in diesem Forum: 0 Mitglieder und 2 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.