본문 바로가기
Delphi/클래스

델파이 레코드 클래스 성능 비교

by MonoSoft 2021. 8. 5.
728x90
반응형

델파이 레코드 클래스 성능 비교

 

전통적으로 구조체는,

그러니까 c++의 스트럭트는 변수들만

담을 수 있어 왔다.

그러나 최근의 델파이나 .넷은 구조체에

프라퍼티와 메떠드도 담을 수 있게 되어서

구조적으로 클래스와 차이가 없어졌다.

그러나 기능적으로는 이들 사이에

큰 차이가 있는데 구조체는

밸류 타입이고 클래스는 레퍼런스 타입이다.

 

일반적으로 구조체보다는 클래스를 쓰는 게 좋다.

구조체를 메떠드의 매개변수로 설정하거나

아래 예제처럼 List<t>.Items로 접근하면

이 값이 내부적으로 복사되므로 부하가 커진다.

주소로 직접 접근할 수 없으니 일단 가져다 놓고 쓰는 거다.

물론 매개변수 앞에 var를 붙이거나

리스트를 아래 예제처럼 이용하면 이런 문제를 피할 수 있기는 하다.

 

클래스는 레퍼런스 타입이므로 포인터와 같이

32비트 플랫폼 빌드의 경우 4바이트, 64비트의 경우

8바이트의 레퍼런스를 위한 주소값을 가지며

이게 클래스의 크기 전부다.

따라서 위 경우들처럼 이용을 하면 클래스도

어쩔 수 없이 4바이트의 복사가 일어난다.

기술적으로 엄격하게 말하면 구조체가 4바이트보다

작으면 이걸 쓰는 게 좋지만 구조체를

이렇게 조그맣게 만들어 쓰는 경우는 없을 거다.

 

델파이의 구조체는 record인데

이게 마냥 쓸모없는 건 아니다.

예를 들어 포인터를 클래스로 타입캐스팅하는 거보단

레코드로 하는 게 한결 편하다.

레코드는 그 시작과 크기가 쉽게 예상 가능하지만

클래스의 경우에는 그렇지 않기 때문이다.

 

tList<t>.Items는 기본 프라퍼티이므로 tList<t>[n]과 같다.

tList<t>.List는 아이템의 밸류를 이용하지 않고

레퍼런스를 이용한다.

아래 예제 결과를 정리하면 이렇다.

 

- 클래스의 인스턴스를 만들고 리스트에 넣어도

그냥 구조체를 리스트에 담는 거보다 빠르다.

 

- tList<t>에 레코드를 넣고 tList<t>.Items로

접근하면 tList<t>.List로 접근하는 거보다 많이

느리며 그 정도는 아이템의 크기에 비례한다.

 

- tList<t>에 클래스를 넣고 위와 같이 하면 차이가 없다.

 

user System.Diagnostics, Vcl.StdCtrls,System.Generics.Collections 추가 

type

tRecord = record

Field1: array[0..19999] of Char;

Field2: Integer;

Field3: Extended;

end;

 

tClass = class

Field1: array[0..19999] of Char;

Field2: Integer;

Field3: Extended;

end;

 

procedure TForm1.Button1Click(Sender: TObject);

var

_Stopwatch: TStopwatch;

_List1: TList<tRecord>;

_List2: TList<tClass>;

_Record: tRecord;

_Class: tClass;

i: Integer;

begin

_List1 := TList<tRecord>.Create;

_List2 := TList<tClass>.Create;

_Stopwatch := TStopwatch.StartNew;

 

for i := 0 to 19999 do

begin

_Record.Field1 := 'abcde';

_Record.Field2 := 12345;

_Record.Field3 := 0.12345;

_List1.Add(_Record);

end;

 

Memo1.Lines.Add('_List1.Add(_Record);');

Memo1.Lines.Add(IntToStr(_Stopwatch.ElapsedMilliseconds)); // 1,012

_Stopwatch := TStopwatch.StartNew;

 

for i := 0 to 19999 do

begin

_Class := tClass.Create;

_Class.Field1 := 'abcde';

_Class.Field2 := 12345;

_Class.Field3 := 0.12345;

_List2.Add(_Class);

end;

 

Memo1.Lines.Add('_List2.Add(_Record);');

Memo1.Lines.Add(IntToStr(_Stopwatch.ElapsedMilliseconds)); // 384

 

_Stopwatch := TStopwatch.StartNew;

 

for i := 0 to _List1.Count - 1 do

begin

if _List1[i].Field1 = '12345' then

begin

Break;

end;

end;

 

Memo1.Lines.Add('for i := 0 to _List1.Count - 1 do');

Memo1.Lines.Add(IntToStr(_Stopwatch.ElapsedMilliseconds)); // 87

_Stopwatch := TStopwatch.StartNew;

 

for i := 0 to _List1.Count - 1 do

begin

if _List1.List[i].Field1 = '12345' then

begin

Break;

end;

end;

 

Memo1.Lines.Add('for i := 0 to _List1.Count - 1 do');

Memo1.Lines.Add(IntToStr(_Stopwatch.ElapsedMilliseconds)); // 3

 

_Stopwatch := TStopwatch.StartNew;

 

for i := 0 to _List2.Count - 1 do

begin

if _List2[i].Field1 = '12345' then

begin

Break;

end;

end;

 

Memo1.Lines.Add('for i := 0 to _List2.Count - 1 do');

Memo1.Lines.Add(IntToStr(_Stopwatch.ElapsedMilliseconds)); // 4

 

_Stopwatch := TStopwatch.StartNew;

 

for i := 0 to _List2.Count - 1 do

begin

if _List2.List[i].Field1 = '12345' then

begin

Break;

end;

end;

Memo1.Lines.Add('for i := 0 to _List2.Count - 1 do');

Memo1.Lines.Add(IntToStr(_Stopwatch.ElapsedMilliseconds)); // 4

end;

728x90
반응형

댓글