그래프로 표시되는 거대한 데이터 세트 (매 초마다 35 개의 새로운 값)를 사용해야하는 UI 프로젝트에서 작업하고 있습니다. 사용자는보기를 10 분에서 최대 월보기로 변경할 수 있습니다. 이것을 보관하기 위해 필자는 많은 데이터를 600 바이트 배열로 잘라내어 LiveView 차트에 표시해야하는 도우미 함수를 작성했습니다.C# 성능은 루핑을 통해 빠르게 0,001 %로 빠르게 변경됩니다.
처음에는 소프트웨어가 잘 작동하고 빠르지 만 소프트웨어가 오래 실행되고 (예를 들어 한 달 동안) 메모리 사용량이 올라 갔을 때 (약 600Mb까지) 함수가 많이 발생했습니다. 느림 (최대 8 배)
그래서 나는 이것에 대한 근원을 찾기 위해 몇 가지 테스트를했습니다. 아주 놀라 나는 매직 넘버 같은이 있음을 발견 어디에 기능 GET의 2 배, 느린 그냥 내가 정말 혼란 스러워요 39MS 런타임
에 19ms에서 71,495에 71494 루프를 변경하여. 두 번째 for 루프 (배열이 잘리는 부분)를 주석 처리한다고해도 훨씬 느립니다. 가비지 수집기와 관련이있을 수 있습니까? 아니면 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);
}
}
}
아하! 내 시스템에 대한 임계 값은 71924 (~ 20ms 소요)와 71925 (~ 30ms 소요) 사이입니다. –
차이를 만드는 가비지 콜렉션 인 것처럼 보입니다. 스톱워치를 시작하기 직전에'GC.Collect(); '를 추가하십시오.그렇게하면 차이점이 사라집니다. 가비지 컬렉션과 자신의 코드를 타이밍을 맞추는 것처럼 보입니다. –
가비지 컬렉터라는 필자의 주장을 뒷받침하려면'fileValues'의 초기화를'fileValues = new List (TEST);'로 변경해보십시오. 이렇게하면 List 내부에서 배열을 재 할당 할 수 없으므로 (최종 전체 목록을 담을만큼 충분히 커지기 때문에) 수집 할 쓰레기는 그리 많지 않을 것입니다. 나에게 이것은 GC.Collect()가 없어도 시간을 단축시킨다. –