델파이 레코드 클래스 성능 비교
전통적으로 구조체는,
그러니까 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;
'Delphi > 클래스' 카테고리의 다른 글
멀티스레드 GUI 동기화 (0) | 2022.01.14 |
---|---|
델파이 쓰레드(Thread)의 기초 (0) | 2021.08.07 |
델파이 TDictionary / TObjectDictionary (0) | 2021.08.04 |
델파이 클래스 메소드 사용방법 (0) | 2021.08.03 |
델파이의 클래스 헬퍼(helper) (0) | 2021.08.02 |
댓글