Je travaille sur un projet d'interface utilisateur qui doit fonctionner avec d'énormes jeux de données (toutes les 35 nouvelles valeurs) qui seront ensuite affichées dans un graphique. L'utilisateur doit être en mesure de changer la vue de 10 minutes à la vue mensuelle. Pour archiver cela, je me suis écrit une fonction d'aide qui tronque beaucoup de données à un tableau de 600 octets qui devrait ensuite être affiché sur un graphique LiveView. J'ai découvert qu'au début le logiciel fonctionne très bien et rapidement, mais plus le logiciel fonctionne (par exemple pendant un mois) et l'utilisation de la mémoire augmente (jusqu'à environ 600 mb), la fonction obtient beaucoup de plus lent (jusqu'à 8x).C# la performance change rapidement en bouclant seulement 0,001% plus souvent
J'ai donc fait quelques tests pour trouver la source de ceci. Très surpris, j'ai découvert qu'il ya quelque chose comme un chiffre magique où la fonction get 2x plus lent, en changeant 71494 boucles à 71495 de 19ms à 39MS exécution
Je suis vraiment confus. Même lorsque vous commentez la deuxième boucle for (où les tableaux sont tronqués), c'est beaucoup plus lent. Peut-être que cela a quelque chose à voir avec le Garbage Collector? Ou est-ce que C# compresse la mémoire automatiquement? Utilisation de Visual Studio 2017 avec les mises à jour les plus récentes.
Le code
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace TempoaryTest
{
class ProductNameStream
{
public struct FileValue
{
public DateTime Time;
public ushort[] Value;
public ushort[] Avg1;
public ushort[] Avg2;
public ushort[] DAvg;
public ushort AlarmDelta;
public ushort AlarmAverage;
public ushort AlarmSum;
}
}
public static class Program
{
private const int MAX_MEASURE_MODEL = 600;
private const int TEST = 71494;
//private const int TEST = 71495;//this one doubles the consuming time!
public static void Main(string[] bleg)
{
List<ProductNameStream.FileValue> fileValues = new List<ProductNameStream.FileValue>();
ProductNameStream.FileValue fil = new ProductNameStream.FileValue();
DateTime testTime = DateTime.Now;
Console.WriteLine("TEST: {0} {1:X}", TEST, TEST);
//Creating example List
for (int n = 0; n < TEST; n++)
{
fil = new ProductNameStream.FileValue
{
Time = testTime = testTime.AddSeconds(1),
Value = new ushort[8],
Avg1 = new ushort[8],
Avg2 = new ushort[8],
DAvg = new ushort[8]
};
for (int i = 0; i < 8; i++)
{
fil.Value[i] = (ushort)(n + i);
fil.Avg1[i] = (ushort)(TEST - n - i);
fil.Avg2[i] = (ushort)(n/(i + 1));
fil.DAvg[i] = (ushort)(n * (i + 1));
}
fil.AlarmDelta = (ushort)DateTime.Now.Ticks;
fil.AlarmAverage = (ushort)(fil.AlarmDelta/2);
fil.AlarmSum = (ushort)(n);
fileValues.Add(fil);
}
var sw = Stopwatch.StartNew();
/* May look like the same as MAX_MEASURE_MODEL but since we use int
* as counter we must be aware of the int round down.*/
int cnt = (fileValues.Count/(fileValues.Count/MAX_MEASURE_MODEL)) + 1;
ProductNameStream.FileValue[] newFileValues = new ProductNameStream.FileValue[cnt];
ProductNameStream.FileValue[] fileValuesArray = fileValues.ToArray();
//Truncate the big list to a 600 Array
for (int n = 0; n < fileValues.Count; n++)
{
if ((n % (fileValues.Count/MAX_MEASURE_MODEL)) == 0)
{
cnt = n/(fileValues.Count/MAX_MEASURE_MODEL);
newFileValues[cnt] = fileValuesArray[n];
newFileValues[cnt].Value = new ushort[8];
newFileValues[cnt].Avg1 = new ushort[8];
newFileValues[cnt].Avg2 = new ushort[8];
newFileValues[cnt].DAvg = new ushort[8];
}
else
{
for (int i = 0; i < 8; i++)
{
if (newFileValues[cnt].Value[i] < fileValuesArray[n].Value[i])
newFileValues[cnt].Value[i] = fileValuesArray[n].Value[i];
if (newFileValues[cnt].Avg1[i] < fileValuesArray[n].Avg1[i])
newFileValues[cnt].Avg1[i] = fileValuesArray[n].Avg1[i];
if (newFileValues[cnt].Avg2[i] < fileValuesArray[n].Avg2[i])
newFileValues[cnt].Avg2[i] = fileValuesArray[n].Avg2[i];
if (newFileValues[cnt].DAvg[i] < fileValuesArray[n].DAvg[i])
newFileValues[cnt].DAvg[i] = fileValuesArray[n].DAvg[i];
}
if (newFileValues[cnt].AlarmSum < fileValuesArray[n].AlarmSum)
newFileValues[cnt].AlarmSum = fileValuesArray[n].AlarmSum;
if (newFileValues[cnt].AlarmDelta < fileValuesArray[n].AlarmDelta)
newFileValues[cnt].AlarmDelta = fileValuesArray[n].AlarmDelta;
if (newFileValues[cnt].AlarmAverage < fileValuesArray[n].AlarmAverage)
newFileValues[cnt].AlarmAverage = fileValuesArray[n].AlarmAverage;
}
}
Console.WriteLine(sw.ElapsedMilliseconds);
}
}
}
Aha! Le seuil pour mon système est entre 71924 (prend ~ 20ms) et 71925 (prend ~ 30ms) –
On dirait que c'est la collecte des ordures qui fait la différence. Essayez d'ajouter 'GC.Collect();' juste avant de lancer le chronomètre.Quand je fais cela, la différence disparaît - il semble donc que vous chronométrez le garbage collection ainsi que votre propre code. –
Pour soutenir davantage mon affirmation selon laquelle il s'agit du garbage collector, essayez de changer l'initialisation de 'fileValues' en:' fileValues = new List (TEST); '. Cela l'empêchera de réallouer le tableau à l'intérieur de la liste (puisqu'il sera assez grand pour contenir toute la liste finale) et donc il n'y aura pas autant de poubelles à collecter. Pour moi, cela réduit le temps sans le 'GC.Collect()'. –