본문 바로가기
엑셈 기업문화/엑셈 사람들

[정동기]모든것은 적재적소가 있다

by EXEM 2008. 9. 26.

사용자 삽입 이미지
생활을 하다 보면 가끔 주위 사람들에게서  질문에 대한 답변으로 이러한 대답을 듣는다.

응 이게 좋아 이거 써!!” 그럼 보통 사람들은 이유도 모른 체 사용하게 된다.

그런데 가끔은 이것이 잘못 되거나 혹은 굉장히 위험 할 수도 있다.

 

처음 프로그램을 접하였을 때 사람들이 TList가 배열보다 많은 장점 들을 가지고 있다고 해서

! 이거 좋은 거구나!”

아싸! 나도 이거 써야지 하고 무분별하게 TList를 남발한 적이 있다.

그게 어떠한 구조를 가지고 있고 용도가 무엇이고 어떠한 단점이 있는지도 모른 체 단순히 그냥 좋다고 하니까….^^;

참 위험한 생각이고 책임감 없는 행동 이었던 것 같다.

 

물론 TList는 잘만 사용하면 정말 기가 막힌 놈이다. 이 녀석은 '포인터'를 배열로 잡아 인덱스를 관리하는 방식으로 동작을 하게 된다.. 때문에 실제 데이터가 어디에 있건 데이터의 인덱스를 바꾸거나 정렬하는 등의 기능 구현이 무지하게 편하다. 배열이 이런 기능이 되다니 놀라웠다.. 사실 TList가 링크드리스트라고 생각했는대 아니었던 것이 더 놀랐고 의외였다..

 

하지만 그 이면에 이놈은 대량의 데이터가 들어가면 엄청난 성능저하를 겪게 됩니다..

 

이는 내부적으로 가지고 있는 배열의 크기를 조정하면서 나타나는 문제인데..

TList는 배열이 꽉 찰 때 마다, 대략 25% 정도씩 크기를 자동으로 늘입니다..

하지만 배열 크기 변경이 새로 할당 -> 데이터 복사 -> 이전에 할당한 것 해제 라는 작업이 필요합니다.

 

좀더 전문 단어를 쓰자면 임의의 인덱스를 지우거나 임의 위치에 어떤 값을 추가하려고 할 때 전체 메모리가 이동한다는 문제가 있게 됩니다.. 또한 그 특성상 '연결된 블럭'을 할당 받아야 하기 때문에 Add Insert등의 메소드가 불려질 때 마다 ReallocMem()을 호출하게 되구요

 

결론 적으로 하나의 데이터를 Insert 또는 Add할 때마다 이 것을 반복한다고 생각하면 대용량의 데이터를 다루는 대에는 정말 끔찍할 수 밖에 없다는 것을 알 수 있습니다. 그래서 대량의 데이터를 넣다 보면 잦은 재할당으로 속도가 엄청나게 느려질 수 있습니다. 

 

실제 적으로 TList VCL을 살펴보게 되면


procedure TList.Insert(Index: Integer; Item: Pointer);

begin

  if (Index < 0) or (Index > FCount) then

    Error(@SListIndexError, Index);

  if FCount = FCapacity then

    Grow;

  if Index < FCount then

    System.Move(FList^[Index], FList^[Index + 1],

      (FCount - Index) * SizeOf(Pointer));

  FList^[Index] := Item;

  Inc(FCount);

  if Item <> nil then

    Notify(Item, lnAdded);

end;

 

 

function TList.Add(Item: Pointer): Integer;

begin

  Result := FCount;

  if Result = FCapacity then

    Grow;

  FList^[Result] := Item;

  Inc(FCount);

  if Item <> nil then

    Notify(Item, lnAdded);

end;

 

procedure TList.Grow;

var

  Delta: Integer;

begin

  if FCapacity > 64 then

    Delta := FCapacity div 4

  else

    if FCapacity > 8 then

      Delta := 16

    else

      Delta := 4;

  SetCapacity(FCapacity + Delta);

end;

 

 

procedure TList.SetCapacity(NewCapacity: Integer);

begin

  if (NewCapacity < FCount) or (NewCapacity > MaxListSize) then

    Error(@SListCapacityError, NewCapacity);

  if NewCapacity <> FCapacity then

  begin

    ReallocMem(FList, NewCapacity * SizeOf(Pointer));

    FCapacity := NewCapacity;

  end;

end;

 


Insert
를 하거나 Add를 하게 될 때 함 수를 호출하면서 실질적으로 ReallocMem(FList, NewCapacity * SizeOf(Pointer));을 실행 시키는 것을 볼 수 있습니다.

(Insert or Add 실행 à Grow 프로시저 실행 à SetCapacity 프로시저 실행 à ReallocMem(FList, NewCapacity * SizeOf(Pointer)) 실행 )

 

물론 컴퓨터가 많이 좋아진 관계로 어느 정도까지는 잘 못 느끼겠지만 대량의 데이터를 다루거나

이러한 것들을 인식하지 않고 무분별하게 사용 하다 보면 쓰지 않은 것만 못 할 것입니다.

 

이런 것들을 하나하나 경험에 나가면서


! 모든 것들은 만들어진 이유와 쓰여지는 용도가 따로 있구나!”  

그 것을 정확히 알고 사용하는 것이 프로그램어로서 책임감 있는 행동이구나!” 라는 것을 많이 느꼈던 것 같다.

댓글