c# - Ponga GC en espera durante una sección de código
.net garbage-collection (3)
Actualmente, lo mejor que pude encontrar fue cambiar al servidor GC (que no cambió nada por sí mismo) que tiene un tamaño de segmento más grande y me permite usar un número mucho mayor para ninguna sección de gc:
GC.TryStartNoGCRegion(10000000000); // On Workstation GC this crashed with a much lower number, on server GC this works
Va en contra de mis expectativas (esto es 10GB, sin embargo, de lo que pude encontrar en el documento en línea el tamaño de mi segmento en mi configuración actual debería ser de 1 a 4GB, así que esperaba un argumento inválido).
Con esta configuración tengo lo que quería (GC está en espera, tengo 22GB asignados en lugar de 7, todos los objetos temporales no tienen GC, pero el GC se ejecuta una vez (¡una sola vez!) Durante todo el proceso por lotes en lugar de muchos muchas veces por segundo (antes del cambio, la vista de GC en Visual Studio parecía una línea recta de todos los puntos individuales de activación de GC).
Esto no es bueno ya que no se escalará (agregar un 0 conduce a un bloqueo) pero es mejor que cualquier otra cosa que haya encontrado hasta ahora.
A menos que alguien descubra cómo aumentar el tamaño del segmento para que pueda seguir adelante o tenga una mejor alternativa para detener por completo el GC (y no solo una cierta generación, sino todo), aceptaré mi propia respuesta en unos días.
¿Hay alguna manera de poner el GC en espera por completo para una sección de código? Lo único que he encontrado en otras preguntas similares es GC.TryStartNoGCRegion
pero está limitado a la cantidad de memoria que especifiques, que está limitada al tamaño de un segmento efímero.
¿Hay alguna forma de eludirlo por completo y decirle a .NET "asignar lo que necesites, no hacer el período de GC" o aumentar el tamaño de los segmentos? Por lo que descubrí, es como máximo 1GB en un servidor central y esto es mucho menos de lo que necesito asignar, pero no quiero que ocurra GC (tengo hasta terabytes de RAM libre y hay miles de picos de GC) durante esa sección, estaría más que feliz de intercambiar esos por 10 o incluso 100 veces el uso de la RAM).
Editar:
Ahora que hay una recompensa, creo que es más fácil si especifico el caso de uso. Estoy cargando y analizando un archivo XML muy grande (1GB por ahora, 12GB pronto) en objetos en memoria usando LINQ to XML. No estoy buscando una alternativa a eso. Estoy creando millones de pequeños objetos de millones de XElements
y el GC está tratando de recopilar sin parar, mientras que estaría muy contento de mantener toda esa RAM XElements
. Tengo 100s de GB de RAM y tan pronto como llega a los 4GB, el GC comienza a recopilar sin parar, que es muy amigable con la memoria, pero con un rendimiento antipático. No me importa la memoria, pero sí me importa el rendimiento. Quiero tomar la contraoferta opuesta.
Si bien no puedo publicar el código real aquí, hay algún código de muestra que está muy cerca del código final que puede ayudar a quienes solicitaron más información:
var items = XElement.Load("myfile.xml")
.Element("a")
.Elements("b") // There are about 2 to 5 million instances of "b"
.Select(pt => new
{
aa = pt.Element("aa"),
ab = pt.Element("ab"),
ac = pt.Element("ac"),
ad = pt.Element("ad"),
ae = pt.Element("ae")
})
.Select(pt => new
{
aa = new
{
aaa = double.Parse(pt.aa.Attribute("aaa").Value),
aab = double.Parse(pt.aa.Attribute("aab").Value),
aac = double.Parse(pt.aa.Attribute("aac").Value),
aad = double.Parse(pt.aa.Attribute("aad").Value),
aae = double.Parse(pt.aa.Attribute("aae").Value)
},
ab = new
{
aba = double.Parse(pt.aa.Attribute("aba").Value),
abb = double.Parse(pt.aa.Attribute("abb").Value),
abc = double.Parse(pt.aa.Attribute("abc").Value),
abd = double.Parse(pt.aa.Attribute("abd").Value),
abe = double.Parse(pt.aa.Attribute("abe").Value)
},
ac = new
{
aca = double.Parse(pt.aa.Attribute("aca").Value),
acb = double.Parse(pt.aa.Attribute("acb").Value),
acc = double.Parse(pt.aa.Attribute("acc").Value),
acd = double.Parse(pt.aa.Attribute("acd").Value),
ace = double.Parse(pt.aa.Attribute("ace").Value),
acf = double.Parse(pt.aa.Attribute("acf").Value),
acg = double.Parse(pt.aa.Attribute("acg").Value),
ach = double.Parse(pt.aa.Attribute("ach").Value)
},
ad1 = int.Parse(pt.ad.Attribute("ad1").Value),
ad2 = int.Parse(pt.ad.Attribute("ad2").Value),
ae = new double[]
{
double.Parse(pt.ae.Attribute("ae1").Value),
double.Parse(pt.ae.Attribute("ae2").Value),
double.Parse(pt.ae.Attribute("ae3").Value),
double.Parse(pt.ae.Attribute("ae4").Value),
double.Parse(pt.ae.Attribute("ae5").Value),
double.Parse(pt.ae.Attribute("ae6").Value),
double.Parse(pt.ae.Attribute("ae7").Value),
double.Parse(pt.ae.Attribute("ae8").Value),
double.Parse(pt.ae.Attribute("ae9").Value),
double.Parse(pt.ae.Attribute("ae10").Value),
double.Parse(pt.ae.Attribute("ae11").Value),
double.Parse(pt.ae.Attribute("ae12").Value),
double.Parse(pt.ae.Attribute("ae13").Value),
double.Parse(pt.ae.Attribute("ae14").Value),
double.Parse(pt.ae.Attribute("ae15").Value),
double.Parse(pt.ae.Attribute("ae16").Value),
double.Parse(pt.ae.Attribute("ae17").Value),
double.Parse(pt.ae.Attribute("ae18").Value),
double.Parse(pt.ae.Attribute("ae19").Value)
}
})
.ToArray();
No estoy seguro si es posible en su caso, sin embargo, ¿ha intentado procesar su archivo XML en paralelo? Si puede descomponer su archivo XML en partes más pequeñas, puede generar múltiples procesos desde dentro de su código. Cada proceso maneja un archivo separado. A continuación, puede combinar todos los resultados. Esto sin duda aumentará su rendimiento y también con cada proceso por separado tendrá su asignación de memoria por separado, lo que también debería aumentar su asignación de memoria en un momento determinado al procesar todos los archivos XML.
Creo que la mejor solución en su caso sería esta pieza de código que utilicé en uno de mis proyectos hace algunas veces
var currentLatencySettings = GCSettings.LatencyMode;
GCSettings.LatencyMode = GCLatencyMode.LowLatency;
//your operations
GCSettings.LatencyMode = currentLatencySettings;
Está haciendo una gran presión tanto como puede (según mi conocimiento) y aún puede llamar a GC.Collect()
manualmente.
Mire el artículo de MSDN aquí
Además, te sugiero encarecidamente que pagues la colección analizada utilizando los métodos LINQ Skip()
y Take()
. Y finalmente unir las matrices de salida