garbage-collection - recolector - qué es el garbagecollector
D programación sin el recolector de basura (6)
D''s GC simplemente no es tan sofisticado como otros como Java. Es de código abierto, por lo que cualquier persona puede intentar mejorarlo.
Existe un GC concurrente experimental denominado CDGC y hay un proyecto GSoC actual para eliminar el bloqueo global: http://www.google-melange.com/gsoc/project/google/gsoc2012/avtuunainen/17001
Asegúrese de usar LDC o GDC para la compilación para obtener un código mejor optimizado.
El proyecto XomB también usa un tiempo de ejecución personalizado, pero creo que es la versión D 1. http://wiki.xomb.org/index.php?title=Main_Page
He estado mirando a D hoy y en la superficie parece bastante sorprendente. Me gusta cómo se incluyen muchos constructos de alto nivel directamente en el lenguaje, por lo que no es necesario utilizar hacks tontos o métodos sencillos. Una cosa que realmente me preocupa si el GC. Sé que este es un gran problema y he leído muchas discusiones al respecto.
Mis propias pruebas simples surgidas de una pregunta aquí muestran que el GC es extremadamente lento. Más de 10 veces más lento que C ++ directo haciendo lo mismo. (Obviamente, la prueba no se convierte directamente en el mundo real, pero el golpe de rendimiento es extremo y ralentizaría el mundo real que se comporta de manera similar (asignando muchos objetos pequeños rápidamente)
Estoy buscando escribir una aplicación de audio de baja latencia en tiempo real y es posible que el GC arruine el rendimiento de la aplicación para que sea casi inútil. En cierto sentido, si tiene algún problema, arruinará el aspecto de audio en tiempo real, que es mucho más importante ya que, a diferencia de los gráficos, el audio se ejecuta a una velocidad de cuadros mucho mayor (44000+ frente a 30-60). (debido a su baja latencia, es más crucial que un reproductor de audio estándar que puede amortiguar grandes cantidades de datos)
Deshabilitar el GC mejoró los resultados hasta aproximadamente el 20% del código C ++. Esto es significativo. Daré el código al final para el análisis.
Mis preguntas son:
- ¿Qué tan difícil es reemplazar D''s GC con una implementación de punteros inteligentes estándar para que las bibliotecas que dependen de la GC todavía se pueden utilizar. Si elimino el GC por completo, perderé mucho trabajo duro, ya que D ya tiene bibliotecas de límites en comparación con C ++.
- ¿GC.Disable solo detiene la recolección de basura temporalmente (evitando que el hilo del GC se ejecute) y GC.Enable recupera donde lo dejó. Por lo tanto, podría potencialmente deshabilitar el funcionamiento del GC en momentos de uso de alta CPU para evitar problemas de latencia.
- ¿Hay alguna forma de imponer un patrón para no usar GC consistentemente? (Esto se debe a que no tengo programación en D y cuando empiezo a escribir mis lentes que no usan el GC, me gustaría asegurarme de que no olvide implementar su propia limpieza.
- ¿Es posible reemplazar el GC en D fácilmente? (No es que yo quiera, pero podría ser divertido jugar con diferentes métodos de GC un día ... esto es similar a 1, supongo)
Lo que me gustaría hacer es intercambiar memoria por velocidad. No necesito que el GC se ejecute cada pocos segundos. De hecho, si puedo implementar correctamente mi propia gestión de memoria para mis estructuras de datos, es probable que no necesite ejecutarse muy a menudo. Puede que necesite ejecutarlo solo cuando la memoria escasea. Por lo que he leído, cuanto más espere para llamarlo, más lento será. Dado que generalmente habrá momentos en mi aplicación en los que puedo salirse con la suya sin problemas, esto ayudará a aliviar parte de la presión (pero, de nuevo, puede haber horas en que no pueda llamarlo).
No me preocupan tanto las limitaciones de memoria. Preferiría "desperdiciar" memoria sobre la velocidad (hasta cierto punto, por supuesto). Lo primero y más importante son los problemas de latencia.
Por lo que he leído, puedo, como mínimo, seguir la ruta de C / C ++ siempre que no utilice bibliotecas ni construcciones de lenguaje que dependan del GC. El problema es que no conozco a los que sí. He visto cadena, nuevo, etc. mencionado, pero ¿eso significa que no puedo usar la construcción en cadenas si no habilito el GC?
He leído en algunos informes de errores que el GC podría tener errores y eso podría explicar sus problemas de rendimiento.
Además, D usa un poco más de memoria, de hecho, D se queda sin memoria antes del programa C ++. Supongo que es aproximadamente un 15% más o menos en este caso. Supongo que es para el GC.
Me doy cuenta de que el siguiente código no es representativo de su programa promedio, pero lo que dice es que cuando los programas crean instancias de muchos objetos (por ejemplo, al inicio) serán mucho más lentos (10 veces es un factor importante). De la GC podría estar "en pausa" al inicio, entonces no sería necesariamente un problema.
Lo que sería realmente bueno sería si de alguna manera pudiera hacer que el compilador automáticamente convirtiera GC en un objeto local si no lo desasignaba específicamente. Esto casi da lo mejor de ambos mundos.
p.ej,
{
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.
}
De hecho, podría ser bueno poder asociar diferentes tipos de GC con diferentes tipos de datos, con cada GC completamente autónomo. De esta forma podría adaptar el rendimiento del GC a mis tipos.
Código:
module main;
import std.stdio, std.conv, core.memory;
import core.stdc.time;
class Foo{
int x;
this(int _x){x=_x;}
}
void main(string 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();
}
Código C ++
#include <cstdlib>
#include <iostream>
#include <time.h>
#include <math.h>
#include <stdio.h>
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;
}
Le sugiero que lea este artículo: http://3d.benjamin-thaut.de/?p=20 Allí encontrará una versión de la biblioteca estándar que posee administración de memoria y evita por completo la recolección de basura.
Podríamos ver el problema desde una perspectiva un poco diferente. El rendimiento subóptimo de la asignación de muchos objetos pequeños, que mencionas como una justificación para la pregunta, tiene poco que ver con GC solo. Más bien, se trata de un equilibrio entre herramientas de administración de memoria de propósito general (pero subóptimo) y de alto rendimiento (pero especializadas en tareas). La idea es: la presencia de GC no le impide escribir una aplicación en tiempo real, solo tiene que usar herramientas más específicas (por ejemplo, grupos de objetos ) para casos especiales.
También puede asignar todos los bloques de memoria que necesite y luego usar un grupo de memoria para obtener bloques sin el GC.
Y por cierto, no es tan lento como lo mencionaste. Y GC.disable () realmente no lo deshabilita.
Como esto no se ha cerrado aún, las versiones recientes de D tienen la biblioteca std.container que contiene una estructura de datos Array que es significativamente más eficiente con respecto a la memoria que las matrices integradas. No puedo confirmar que las otras estructuras de datos en la biblioteca también sean eficientes, pero puede valer la pena investigar si es necesario tener más memoria consciente sin tener que recurrir a la creación manual de estructuras de datos que no requieren recolección de basura.
D puede usar casi cualquier biblioteca C, simplemente defina las funciones necesarias. D también puede usar bibliotecas C ++, pero D no comprende ciertas construcciones C ++. Entonces ... D puede usar casi tantas bibliotecas como C ++. Simplemente no son D libs nativos.
De la referencia de la biblioteca de D''s.
Core.memory:static nothrow void disable();
Inhabilita las recolecciones de basura automáticas realizadas para minimizar la huella del proceso. Las colecciones pueden continuar ocurriendo en instancias donde la implementación lo considere necesario para un comportamiento correcto del programa, como durante una condición de falta de memoria. Esta función es reentrante, pero se debe llamar a enable una vez para que cada llamada se deshabilite.
static pure nothrow void free(void* p);
Desasigna la memoria a la que hace referencia p. Si p es nulo, no se produce ninguna acción. Si p hace referencia a la memoria no asignada originalmente por este recolector de basura, o si apunta al interior de un bloque de memoria, no se tomará ninguna medida. El bloque no se finalizará independientemente de si se establece el atributo FINALIZE. Si se desea la finalización, use eliminar en su lugar.
static pure nothrow void* malloc(size_t sz, uint ba = 0);
Solicita un bloque alineado de memoria administrada del recolector de basura. Esta memoria se puede eliminar a voluntad con una llamada gratuita, o puede descartarse y limpiarse automáticamente durante una ejecución de recopilación. Si la asignación falla, esta función llamará aOutOfMemory, que se espera arroje un OutOfMemoryError.
Entonces sí. Lea más aquí: http://dlang.org/garbage.html
Y aquí: http://dlang.org/memory.html
Si realmente necesita clases, observe esto: http://dlang.org/memory.html#newdelete delete ha quedado obsoleto, pero creo que todavía puede liberarlo ().
No use clases, use estructuras. Los structs están asignados a la pila, las clases son de montón. A menos que necesite polimorfismo u otras cosas que soporte las clases, están sobrecargadas para lo que está haciendo. Puedes usar malloc y gratis si quieres.
Más o menos ... complete las definiciones de función aquí: https://github.com/D-Programming-Language/druntime/blob/master/src/gcstub/gc.d . Hay un sistema proxy de GC configurado para permitirle personalizar el GC. Entonces, no es algo que los diseñadores no quieran que hagas.
Poco conocimiento de GC aquí: no se garantiza que el recolector de basura ejecute el destructor para todos los objetos sin referencia. Además, no se especifica el orden en que el recolector de elementos no utilizados llama a los destructores de los objetos no referenciados. Esto significa que cuando el recolector de elementos no utilizados llama a un destructor para un objeto de una clase que tiene miembros que son referencias a objetos recogidos basura, es posible que esas referencias ya no sean válidas. Esto significa que los destructores no pueden hacer referencia a los sub objetos. Esta regla no se aplica a los objetos u objetos automáticos eliminados con DeleteExpression, ya que el recolector de basura no está ejecutando el destructor, lo que significa que todas las referencias son válidas.
import std.c.stdlib; eso debería tener malloc y gratis.
import core.memory; esto tiene GC.malloc, GC.free, GC.adroots, // agrega memoria externa a GC ...
las cadenas requieren GC porque son matrices dinámicas de caracteres inmutables. (Inmutable (char) []) Las matrices dinámicas requieren GC, static no.
Si quieres una gestión manual, adelante.
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.
¿Por qué crear una clase contenedora para un int? no haces nada más que frenar las cosas y desperdiciar memoria.
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 realmente lo necesita, escriba la función sensible al tiempo en C y llámela desde D. Heck, si el tiempo es realmente tan grande, use el ensamblaje en línea de D''s para optimizar todo.