私は巨大なデータセット(毎秒35の新しい値)で動作するUIプロジェクトに取り組んでいます。ユーザーはビューを10分から月ビューに変更することができます。これをアーカイブするために、私は自分自身にヘルパー関数を書いていました。この関数は大量のデータを600バイトの配列に切り捨てて、LiveView Chartに表示する必要があります。C#のパフォーマンスは急激に変化し、ちょうど0,001%増加します。
ソフトウェアは非常によく高速で動作することがわかりましたが、ソフトウェアの実行時間が長く(たとえば1か月間)、メモリ使用量が(約600Mbに)増加すると、の機能がかなり向上します遅い(最大8倍)。
私はこれの原因を見つけるためにいくつかのテストを行いました。かなり驚い私は39msランタイム
に19msから71495に71494ループを変更することで、私は本当に困惑している、機能取得の2倍遅くマジックナンバーのようなものがあることが分かりました。あなたが2番目のforループ(配列が切り捨てられているところ)をコメントアウトしても、それはずっと遅くなります。 これはGarbage Collectorと関係がありますか?または、C#はメモリを自動的に圧縮しますか?
最新のアップデートでVisual Studio 2017を使用する。
コード
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!私のシステムのしきい値は71924(〜20ms)と71925(〜30ms)の間です。 –
それは違いを生むガベージコレクションのようです。ストップウォッチを開始する直前に 'GC.Collect();'を追加してみてください。私がそうすると、その違いは消えてしまいます。ガベージコレクションとあなた自身のコードを計時しているように見えます。 –
ガベージコレクタであることをさらにサポートするには、 'fileValues'の初期化を' fileValues = new List(TEST); 'に変更してみてください。これによりリスト内の配列の再配置が妨げられ(最終リスト全体を保持するのに十分な大きさになるので)、収集するゴミはそれほど多くありません。私にとって、これは 'GC.Collect()'がなくても時間を短縮します。 –