foreach 구문 처리 속도가 점점 느려집니다.

안녕하세요 C# 에서 .csv 파일의 컬럼들을 array로 받아서 foreach 구문을 사용하여 DataTable에 column.Add를 하는 과정에서 처음에는 문제가 없었으나 시간이 지나면서 점점 속도가 느려지는 현상이 있습니다.

string firstLine = lines[0];
string headerLabels = firstLine.Split(‘,’);
foreach (string headerWord in headerLabels)
{
dt.Columns.Add(new DataColumn(headerWord));
}

lines에는 csv에서 가져온 모든 데이터들이 들어갑니다.(column명부터 안에 있는 데이터들이 들어갑니다.

image

혹시 어떤 이유 때문에 진단 도구에서 확인 시 점점 걸리는 시간이 늘어나는지 그 이유와 어떻게 하면 이 느려지는 현상을 해소 할 수 있는지 궁금합니다.

감사합니다.

2개의 좋아요

580번째 줄이 dt.Columns.Add(new DataColumn(headerWord)); 인가요?

컬럼이 혹시 계속에서 누적되는 것은 아닐까요?

3개의 좋아요

dt.columns.clear(); 와 dt.rows.clear();를 사용해서 비워주긴하는데…

제가 입력하고자 하는 column 개수가 305개 정도 인데 100개 까지는 그렇게 느리지 않다가 120개 언저리부터 점점 속도가 느려집니다 ㅠㅠ…

580번째 줄이 dt.Columns.Add(new DataColumn(headerWord));가 맞습니다.

3개의 좋아요

음 저도 정확하게 이거다 라고 확답은 못드리겠지만 가비지 컬렉팅과 연관이 있을 것 같습니다.
시간이 지나면서 데이터를 많이 추가할수록 ← 에 착안했기 때문인데요.

닷넷에서 foreach를 통해 뭔가 동적으로 처리를 한다는 것은 눈으로 보이기엔 마법이지만 사실 내부적으로는 GC가 그것을 다 받아주고 있기 때문입니다.

아래 링크를 한번 확인하시고 이해하시면 도움이 되실 것도 같습니다.

결국 배열이고, 재할당이고 많이 쌓일수록 그만큼 더 제거해야하거든요… 또한 일반적으로 GC 스레드가 동작할 때는 모든 스레드가 일시 중단되므로 아마도…이게 맞지않을까 예상이 됩니다.

new int[0]은 카운트가 있으니 가비지는 아니고 , 처음 Add하면서 할당된 new int[4]가 에 1~4까지 숫자가 차있는 상태로 가비지가 되고, new int[8] 인 배열이 할당되고 거기에 1~5까지 들어가는 것입니다. 이렇게 재할당을 계속 발생시키는 행위는 GC가 제거할 레퍼런스 카운트가 0인 쓰레기 메모리가 계속 늘어난다는 의미 와 같은 의미입니다. 4, 8, 16, 32 …순서로 할당이 되니 만약 추가해야할 데이터가 10000개라면 4 + 8 + 16 + 32 + 64 + 128 + 256 + 512 + 1024 + 2048 + 4096 + 8192 + 16384 = 32764개의 메모리 영역이 필요합니다. 물론 16384개 이전의 4부터 8192까지 더한 16380개의 메모리는 쓰레기가 됩니다.

여기에 집중하시면 될 것 같습니다.

5개의 좋아요

음… 심증은 있지만 물증이 없어 확신은 못하고 있었는데 감안해서 한번 다시 해봐야 될 거 같네요

조언 감사합니다.

3개의 좋아요

저도 관련해서 Datatable에 Column을 Add했을 때 제가 언급한 List에 Add하는 것과 정말 동일한지 소스코드 한번 살펴보고 오겠습니다.

https://referencesource.microsoft.com/#System.Data/fx/src/data/System/Data/DataColumnCollection.cs,143

아 보니까 정말 List에 Add 했을 때와 똑같이 하고 있네요

https://referencesource.microsoft.com/#System.Data/fx/src/data/System/Data/DataColumnCollection.cs,269

아마…심증상으로는 GC가 맞을 것 같습니다.

제 의견은 여기에 한 표…


아니다…생각해보니 짝수배로 컬럼이 늘어나야 재할당이 이뤄지는데

보여주신 사진을 보면 짝수배로 할당했을 때는 아니군요…

그럼 또 이게 아닐 수도 있는건데…

2개의 좋아요

혹시 OnCollectionChanging() 또는 OnCollectionChanged() 등의 컬럼 관련 이벤트가 걸려있나요?

3개의 좋아요

@dimohy 아뇨 OnCollectionChange 관련해서 이벤트 사용하지 않습니다.

2개의 좋아요

@Vincent dipose라는 메서드가 관리되지 않는 리소스 정리 하는 게 있다는데… 일단 그거 한번 적용 시켜보려고 합니다.

2개의 좋아요

아…그거는 아마 연관이 없을 것 같습니다.
Dispose를 한다고해도 바로 제거되는 게 아니라서요.
게다가 IDisposable 인터페이스를 구현하지 않았다면 Dispose에 기본코드만 들어있을 수도 있습니다.

2개의 좋아요

아 그런가요… 안타깝네요… 허허…

2개의 좋아요

작업 앞뒤로 table.BeginLoadData();table.EndLoadData();를 넣어서 불필요한 추가 작업이 발생되지 않도록 구성해도 동일한걸까요?

var table = new DataTable();
table.BeginLoadData();
Enumerable.Range(1, 500)
    .ToList().ForEach(x =>
    {
        table.Columns.Add(new DataColumn("Column" + x));
    });
table.EndLoadData();
3개의 좋아요

참고해서 한번 진행해보겠습니다.

조언 감사합니다.

@nyjin 시도해봤는데 현상 동일하게 발생하네요… ㅠㅠ

2개의 좋아요

좋은 글이 있었네요~

4개의 좋아요

DataTable에 데이터를 담는 부분의 전체 소스가 있었다면… 하는 아쉬움이 드네요

4개의 좋아요
string[] lines = System.IO.File.ReadAllLines(opf.FileName);
            if (lines.Length > 0)
            {
                dt.Clear();

                //first line to create header
                string firstLine = lines[0];
                string[] headerLabels = firstLine.Split(',');
                
                foreach (string headerWord in headerLabels)
                {
                    dt.Columns.Add(new DataColumn(headerWord));


                }

이렇게 들어가고 .csv 파일에 있는 데이터를 가져오는 소스 입니다.

이 이외에 따로 데이터를 담기 위한 소스는 없네요.

2개의 좋아요

DataTable 로 사용하는 이유가 따로 있으신가요?

직접 순수 VO 클래스를 만들어서 사용하는걸 권장 드립니다.

데이터그리드 등으로 UI에 데이터 표시 목적으로 DataTable을 사용하시는 거라면

VO 모델 클래스로 바인딩도 가능합니다.

DataTable은 무겁고 또한 많은 컬럼을 추가 하기 위해 new DataColumn 처리는
내부적으로 해당 데이터타입을 위해 리플렉션을 동반합니다.
때문에 속도가 느리고 메모리 효율성도 딱히 좋다고 볼 순 없으며 느릴수 밖에 없습니다.

왠만하면 클래스 바인딩 방식으로 추천 드립니다.

3개의 좋아요

공유해주신 코드는 런타임 상에서 여러번 불리는걸까요?

string[] lines = System.IO.File.ReadAllLines(opf.FileName);
if (lines.Length > 0)
{
    dt.Clear();

    //first line to create header
    string firstLine = lines[0];
    string[] headerLabels = firstLine.Split(',');
    
    foreach (string headerWord in headerLabels)
    {
        dt.Columns.Add(new DataColumn(headerWord));


    }
}
2개의 좋아요

아직도 문제가 해결되지 않으셨으면, 깃허브 등을 통해 재현 가능한 소스코드를 공유해주시면 아마도 이곳 여러 분들이 도움을 드릴 수 있을 것 같습니다.

2개의 좋아요

사용하는 이유는 따로 없습니다.

제가 지식이 얕다보니 구글링하면서 사용할 수 있는 부분을 참고해서 코드를 짜다 보니 사용하게되었습니다.
VO 모델 클래스… 한번 구글링 해보겠습니다.

조언 감사합니다.

2개의 좋아요