D programmation sans le garbage collector

J’ai regardé D aujourd’hui et en surface, ça a l’air incroyable. J’aime la manière dont il inclut de nombreuses constructions de niveau supérieur directement dans le langage, de sorte qu’il n’est pas nécessaire d’utiliser des méthodes stupides ou des méthodes laconiques. Une chose qui m’inquiète vraiment si le GC. Je sais que c’est un gros problème et j’ai lu beaucoup de discussions à ce sujet.

Mes propres tests simples issus d’une question ici montrent que le GC est extrêmement lent. Plus de 10 fois plus lent que le C ++ droit faisant la même chose. (évidemment le test ne convertit pas directement dans le monde réel mais le coup de performance est extrême et ralentirait le monde réel qui se comporte de la même manière (allouer rapidement de nombreux petits objects)

Je cherche à écrire une application audio à faible latence en temps réel et il est possible que le GC ruine les performances de l’application pour la rendre pratiquement inutile. En un sens, s’il a des problèmes, il va ruiner l’aspect audio en temps réel, ce qui est beaucoup plus crucial puisque, contrairement aux graphiques, l’audio est beaucoup plus rapide (44000+ vs 30-60). (en raison de sa faible latence, il est plus crucial qu’un lecteur audio standard capable de stocker des quantités importantes de données)

La désactivation du CPG a amélioré les résultats à environ 20% du code C ++. Ceci est significatif. Je donnerai le code à la fin pour l’parsing.

Mes questions sont:

  1. Est-il difficile de remplacer le GC de D par une implémentation standard de pointeurs intelligents afin que les bibliothèques qui dépendent du GC puissent toujours être utilisées? Si je supprime complètement le GC, je perdrai beaucoup de travail, D ayant déjà des bibliothèques de limites par rapport au C ++.
  2. Est-ce que GC.Disable arrête temporairement le ramasse-miettes (empêchant l’exécution du thread GC) et GC.Activer le ramassage là où il s’est arrêté? Ainsi, je pourrais éventuellement désactiver le GC pour qu’il s’exécute dans des moments d’utilisation élevée du processeur afin d’éviter les problèmes de latence.
  3. Existe-t-il un moyen d’imposer un modèle pour ne pas utiliser le GC de manière cohérente. (C’est parce que je n’ai pas programmé en D et quand je commence à écrire mes lunettes qui n’utilisent pas le GC, je voudrais être sûr de ne pas oublier de mettre en œuvre leur propre nettoyage.
  4. Est-il possible de remplacer le GC en D facilement? (ce n’est pas ce que je veux mais ça pourrait être amusant de jouer avec différentes méthodes de GC un jour … c’est similaire à 1 je suppose)

Ce que je voudrais faire, c’est échanger de la mémoire contre de la vitesse. Je n’ai pas besoin que le CPG fonctionne toutes les quelques secondes. En fait, si je peux correctement implémenter ma propre gestion de la mémoire pour mes structures de données, il y a des chances que cela ne soit pas nécessaire du tout. Je pourrais avoir besoin de l’exécuter seulement lorsque la mémoire devient rare. D’après ce que j’ai lu, cependant, plus vous attendez longtemps pour l’appeler, plus il sera lent. Comme il y aura généralement des moments dans ma candidature où je pourrai appeler sans problème, cela aidera à réduire la pression (mais encore une fois, il pourrait y avoir des heures pendant lesquelles je ne pourrai pas l’appeler).

Je ne m’inquiète pas autant des contraintes de mémoire. Je préférerais “gaspiller” la mémoire sur la vitesse (jusqu’à un certain point, bien sûr). En premier lieu, les problèmes de latence.

D’après ce que j’ai lu, je peux au moins suivre la voie du C / C ++ tant que je n’utilise aucune bibliothèque ou construction de langage qui s’appuie sur le GC. Le problème est que je ne connais pas ceux qui le font. J’ai vu de la chaîne, du nouveau, etc., mais cela signifie-t-il que je ne peux pas utiliser les chaînes de assembly si je n’active pas le GC?

J’ai lu dans certains rapports de bogues que le GC pourrait être vraiment bogué et que cela pourrait expliquer ses problèmes de performances?

De plus, D utilise un peu plus de mémoire, en fait, D manque de mémoire avant le programme C ++. Je suppose que c’est environ 15% de plus ou plus dans ce cas. Je suppose que c’est pour le GC.

Je me rends compte que le code suivant n’est pas représentatif de votre programme moyen, mais il est dit que lorsque les programmes instancient beaucoup d’objects (disons au démarrage), ils seront beaucoup plus lents (10 fois est un facteur important). Du GC pourrait être “suspendu” au démarrage alors ce ne serait pas nécessairement un problème.

Ce qui serait vraiment sympa, c’est si je pouvais en quelque sorte faire comstackr automatiquement un object local par le compilateur si je ne le libérais pas spécifiquement. Cela donne presque le meilleur des deux mondes.

par exemple,

{ Foo f = new Foo(); .... dispose f; // Causes f to be disposed of immediately and treats f outside the GC // If left out then f is passed to the GC. // I suppose this might actually end up creating two kinds of Foo // behind the scenes. Foo g = new manualGC!Foo(); // Maybe something like this will keep GC's hands off // g and allow it to be manually disposed of. } 

En fait, il pourrait être intéressant de pouvoir associer différents types de GC à différents types de données, chaque GC étant complètement autonome. De cette façon, je pourrais adapter la performance du GC à mes types.

Code:

 module main; import std.stdio, std.conv, core.memory; import core.stdc.time; class Foo{ int x; this(int _x){x=_x;} } void main(ssortingng args[]) { clock_t start, end; double cpu_time_used; //GC.disable(); start = clock(); //int n = to!int(args[1]); int n = 10000000; Foo[] m = new Foo[n]; foreach(i; 0..n) //for(int i = 0; i<n; i++) { m[i] = new Foo(i); } end = clock(); cpu_time_used = (end - start); cpu_time_used = cpu_time_used / 1000.0; writeln(cpu_time_used); getchar(); } 

Code C ++

 #include  #include  #include  #include  #include  using namespace std; class Foo{ public: int x; Foo(int _x); }; Foo::Foo(int _x){ x = _x; } int main(int argc, char** argv) { int n = 120000000; clock_t start, end; double cpu_time_used; start = clock(); Foo** gx = new Foo*[n]; for(int i=0;i<n;i++){ gx[i] = new Foo(i); } end = clock(); cpu_time_used = (end - start); cpu_time_used = cpu_time_used / 1000.0; cout << cpu_time_used; std::cin.get(); return 0; } 

  1. D peut utiliser à peu près n’importe quelle bibliothèque C, il suffit de définir les fonctions nécessaires. D peut également utiliser des bibliothèques C ++, mais D ne comprend pas certaines constructions C ++. Donc … D peut utiliser presque autant de bibliothèques que C ++. Ils ne sont tout simplement pas des librairies natives.

  2. De la référence de la bibliothèque de D
    Core.memory:

     static nothrow void disable(); 

    Désactive les collectes automatiques effectuées pour minimiser l’empreinte du processus. Les collections peuvent continuer à se produire dans les cas où l’implémentation juge nécessaire pour un comportement correct du programme, comme lors d’une condition de mémoire insuffisante. Cette fonction est réentrante, mais enable doit être appelée une fois pour chaque appel à désactiver.

     static pure nothrow void free(void* p); 

    Affecte la mémoire référencée par p. Si p est nul, aucune action ne se produit. Si p fait référence à la mémoire qui n’a pas été allouée à l’origine par ce garbage collector, ou s’il pointe vers l’intérieur d’un bloc de mémoire, aucune action ne sera entreprise. Le bloc ne sera pas finalisé, que l’atsortingbut FINALIZE soit défini ou non. Si la finalisation est souhaitée, utilisez plutôt delete.

     static pure nothrow void* malloc(size_t sz, uint ba = 0); 

    Demande un bloc aligné de mémoire gérée à partir du garbage collector. Cette mémoire peut être supprimée à volonté avec un appel à libérer, ou elle peut être supprimée et nettoyée automatiquement lors d’une collecte. Si l’allocation échoue, cette fonction appellera onOutOfMemory qui est censé lancer une erreur OutOfMemoryError.

    Donc oui. Lire plus ici: http://dlang.org/garbage.html

    Et ici: http://dlang.org/memory.html

    Si vous avez vraiment besoin de classes, regardez ceci: http://dlang.org/memory.html#newdelete delete a été déconseillé, mais je pense que vous pouvez toujours le libérer ().

  3. N’utilisez pas de classes, utilisez des structures. Les structures sont atsortingbuées par stack, les classes sont tas. À moins que vous ayez besoin d’un polymorphism ou d’autres éléments pris en charge par les classes, ils sont inutiles pour ce que vous faites. Vous pouvez utiliser malloc et gratuit si vous le souhaitez.

  4. Plus ou moins … remplissez les définitions de fonction ici: https://github.com/D-Programming-Language/druntime/blob/master/src/gcstub/gc.d . Un système de proxy GC est configuré pour vous permettre de personnaliser le GC. Ce n’est donc pas comme si les concepteurs ne voulaient pas que vous fassiez.

Peu de connaissances du GC ici: le garbage collector ne garantit pas l’exécution du destructeur pour tous les objects non référencés. En outre, l’ordre dans lequel le récupérateur de mémoire appelle des destructeurs pour les objects de référence n’est pas spécifié. Cela signifie que lorsque le récupérateur de mémoire appelle un destructeur pour un object d’une classe dont les membres sont des références à des objects collectés, ces références peuvent ne plus être valides. Cela signifie que les destructeurs ne peuvent pas faire référence à des sous-objects. Cette règle ne s’applique pas aux objects automatiques ou aux objects supprimés avec DeleteExpression, car le destructeur n’est pas exécuté par le garbage collector, ce qui signifie que toutes les références sont valides.

import std.c.stdlib; cela devrait avoir malloc et gratuit.

import core.memory; ceci a GC.malloc, GC.free, GC.addroots, // ajoute de la mémoire externe au GC …

les chaînes nécessitent le GC car ce sont des tableaux dynamics de caractères immuables. (immutable (char) []) Les tableaux dynamics nécessitent un GC, statique non.

Si vous voulez une gestion manuelle, allez-y.

 import std.c.stdlib; import core.memory; char* one = cast(char*) GC.malloc(char.sizeof * 8);. GC.free(one);//pardon me, I'm not used to manual memory management. //I am *asking* you to edit this to fix it, if it needs it. 

pourquoi créer une classe wrapper pour un int? vous ne faites que ralentir les choses et gaspiller la mémoire.

 class Foo { int n; this(int _n){ n = _n; } } writeln(Foo.sizeof); //it's 8 bytes, btw writeln(int.sizeof); //Its *half* the size of Foo; 4 bytes. Foo[] m;// = new Foo[n]; //8 sec m.length=n; //7 sec minor optimization. at least on my machine. foreach(i; 0..n) m[i] = new Foo(i); int[] m; m.length=n; //nice formatting. and default initialized to 0 //Ooops! forgot this... foreach(i; 0..n) m[i] = i;//.145 sec 

Si vous en avez vraiment besoin, écrivez la fonction sensible au temps dans C et appelez-la à partir de D. Heck, si le temps presse vraiment, utilisez l’assembly inline de D pour optimiser tout.

Je vous suggère de lire cet article: http://3d.benjamin-thaut.de/?p=20 Vous y trouverez une version de la bibliothèque standard qui gère votre propre mémoire et qui évite complètement la récupération de mémoire.

D’s GC n’est tout simplement pas aussi sophistiqué que d’autres comme Java. C’est open-source donc tout le monde peut essayer de l’améliorer.

Il existe un GC expérimental simultané nommé CDGC et il existe un projet GSoC en cours pour supprimer le verrou global: http://www.google-melange.com/gsoc/project/google/gsoc2012/avtuunainen/17001

Assurez-vous d’utiliser LDC ou GDC pour la compilation afin d’obtenir un code optimisé.

Le projet XomB utilise également un runtime personnalisé, mais je pense qu’il s’agit de la version D 1. http://wiki.xomb.org/index.php?title=Main_Page

Vous pouvez également simplement allouer tous les blocs de mémoire dont vous avez besoin, puis utiliser un pool de mémoire pour obtenir des blocs sans le GC.

Et au fait, ce n’est pas aussi lent que vous l’avez mentionné. Et GC.disable () ne le désactive pas vraiment.

Nous pourrions examiner le problème sous un angle un peu différent. La performance sous-optimale de l’allocation de nombreux petits objects, que vous mentionnez comme justification de la question, n’a rien à voir avec le GC seul. Il s’agit plutôt d’une question d’équilibre entre les outils de gestion de la mémoire à usage général (mais non optimal) et hautement performants (mais spécialisés dans les tâches). L’idée est la suivante: la présence de GC ne vous empêche pas d’écrire une application en temps réel, il vous suffit d’utiliser des outils plus spécifiques (par exemple, des pools d’objects ) pour des cas particuliers.

Comme il n’a pas encore été fermé, les versions récentes de D ont la bibliothèque std.container qui contient une structure de données Array nettement plus efficace en termes de mémoire que les tableaux intégrés. Je ne peux pas confirmer que les autres structures de données de la bibliothèque sont également efficaces, mais il peut être utile de se demander si vous devez être plus conscient de la mémoire sans avoir à créer manuellement des structures de données qui ne nécessitent pas de récupération de place.