자유롭게 질의 및 응답을 할 수 있는 게시판입니다. 개발자 여러분의 답변이 큰 도움이 됩니다.
- 제품설치/등록 오류 문의: 설치/등록 Q&A 이용 (제품 구매 고객 한정)
Delphi 디버깅 시 tList<t>.List w.a.t.c.h. 이상 작동
2020.02.25 10:21
안녕하세요? 2009 라이선스 가지고 있으며 아래 문제는 10.3.3. 커뮤니티 에디션에서 확인됩니다.
두 개 이상 유닛의 인터페이스 영역에 같은 tList<t>가 선언되어 있으면 디버깅을 할 때 tList<t>.List에 대한 w.a.t.c.h.(이용 금지 단어라네요)가 작동되지 않는 거 같습니다.
아시다시피 tList<t>는 10.3.x 이후 많이 달라졌으며 그 과정에서 오류들이 보고되고 있습니다. 간단한 코드이니 한번 확인해 보시는 게 좋을 듯합니다.
unit Unit1;
interface
uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Generics.Collections, Unit2;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
List1: TList<Extended>;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
List1 := TList<Extended>.Create;
List1.Add(1);
Caption := FloatToStr(List1.List[0]); // break and w.a.t.c.h. here
end;
unit Unit2;
interface
uses System.Generics.Collections;
type
tClass1 = class
List2: TList<Extended>; // makes the problem
end;
댓글 9
-
험프리
2020.02.27 10:46
-
림병기
2020.02.27 15:53
답변 고맙습니다.
패치가 배포된 날 뒤에 다운로드/설치를 하여 버전은 님 꺼랑 같습니다. tList<t>.Items로 하면 더 이상한 작동을 합니다. 그치만 커뮤니티 에디션에서만 일어나는 이상이라면 특별히 문제될 건 없을 듯합니다.
그건 그렇고 리스트 아이템을 호출하는 방법에 대해 제가 틀리게 알고 있나 싶어 하나 여쭤 보려 합니다. 님 설명과 레퍼런스에서 확인되는 거처럼 tList<t>.List는 비교적 최근 만들어진 프라퍼티로서 배열의 형태입니다. 그러나 이 차이에 더해 tList<t>.Items의 경우 이걸 호출한 때 호출을 한 루틴에서 내부적으로 해당 아이템에 대한 변수 복사가 일어나지만 tList<t>.List의 경우에는 포인터로 접근한다고 알고 있습니다.
전자의 경우 긴 리스트를 루핑하면 이런 복사 오버헤드로 인해 상당한 부하가 발생하지만 후자의 경우에는 산뜻하게 처리됩니다. 또한 전자의 경우 리스트의 아이템이 레코드라면 tList<record>.Items[n].Field1 := 'abcde'; ... 의 형태로 이용이 가능하지 않습니다. left side로 assign 할 수 없다는 오류가 생깁니다. 그러나 후자의 경우에는 같은 이유로 부드럽게 처리됩니다.
이런 이유들 때문에 저는 tList<t>.List가 생긴 뒤로는 tList<t>.Items나 tList[n]의 형태는 전혀 이용하지 않고 있습니다만 제가 혹시 잘못 이해하고 있는 것인지요?
-
험프리
2020.02.27 16:57
디버깅 시 Watch List에 나오는 목록이 안나온다는 이야기였네요. 제가 엉뚱한 내용으로 알아들었습니다.
(저는 실행 시 오류가 발생하는 것으로 잘못 알아들었습니다.)
저도 동일한 이슈가 발생하네요.
Function to be called, {System.Generics.Collections}TList<System.Extended>.GetList, was eliminated by linker
이 부분은 디버거의 기능으로 저도 원인은 잘 모르겠습니다.
TList<T>.Items와 TList<T>.List에 대해 저도 다시한번 살펴봤습니다.
말씀하신대로 Items에서 List를 호출하는 방식으로 구현되어있습니다.
function TList<T>.GetItem(Index: Integer): T;
begin
// CheckItemRange(Index) is expanded here manually to improve codegen
if Cardinal(Index) >= Cardinal(FCount) then
ErrorArgumentOutOfRange;
Result := List[Index];
end;
하지만, 댓글에 작성하신 "긴 리스트를 루핑하면 이런 복사 오버헤드로 인해 상당한 부하가 발생"은 잘 이해가되지 않습니다.
위 코드와 같이 배열에 대해 인덱스로 찾아가기 때문에 형변환에 대한 오버해드가 많을것이라는 것은 잘 이해되지 않네요^^
(제가 직접 테스트를 진행해 보지는 못했습니다.)
그리고, 자주 애용하셨던 방식대로 List 속성을 이용해도 무방할것으로 보입니다.
그래도 Items 속성이 말씀하신대로 사용 못할정도로 오버해드가 많지 않으니 적절하게 사용하시길 권장드립니다.
저도 성능 테스트 진행해보도록 하겠습니다.
-
림병기
2020.02.27 22:11
친절하고 빠른 답변에 다시 한번 감사 드립니다.
https://stackoverflow.com/questions/38990682/long-delay-when-looping-through-a-tlist-of-big-records
위 링크는 전에 제가 했던 질문인데 그 내용을 요약해 보면
tTestRecord1 = record
Field1: array[0..4] of Integer;
Field2: array[0..4] of Extended;
Field3: string;
end;
tTestRecord2 = record
Field1: array[0..4999] of Integer;
Field2: array[0..4999] of Extended;
Field3: string;
위와 같이 구조는 같고 크기는 다른 레코드의 리스트를 만들어서 tList<t>.Items[n]으로 루핑을 하면 전자에 비해 후자의 경우 시간이 많이 걸리지만 tList<t>.List[n]으로 같은 루핑을 하면 의미 있는 시간의 차이가 없다는 것입니다. 이는 인덱스로 찾아가는 과정이나 타입캐스팅에서 시간이 소요되는 것이 아니라 전자로 루핑을 하면 아이템 하나 하나를 임시 변수에 담기 때문이라는 게 답변입니다. 실제로 질문을 위해 최대로 간략하게 만들었던 예제와 달리 더 크고 복잡한 구조의 레코드를 리스트에 담아 Items[n]으로 루핑을 하면 감당하기 어려울 정도로 긴 시간이 걸렸습니다.
혹시나 시간이 오래 지나 님 설명과 같이 수정이 되었나 확인을 해 보았으나 위의 차이는 여전히 있었습니다.
-
happy
2020.02.28 13:01
엠바 디버거는 원래 부터 버그가 많고...
결론 부터 말하면...
TList.Items 보다 TList.LIst 가 훨씬 유리 한 것임.
험프리 군은...
TList<T>.Items와 TList<T>.List에 대해 저도 다시한번 살펴봤습니다.
말씀하신대로 Items에서 List를 호출하는 방식으로 구현되어있습니다.
function TList<T>.GetItem(Index: Integer): T;
begin
// CheckItemRange(Index) is expanded here manually to improve codegen
if Cardinal(Index) >= Cardinal(FCount) then
ErrorArgumentOutOfRange;
Result := List[Index];
end;
하지만, 댓글에 작성하신 "긴 리스트를 루핑하면 이런 복사 오버헤드로 인해 상당한 부하가 발생"은 잘 이해가되지 않습니다.
ITems에서 List를 호출하는 구조로 되어 있어서 이해가 안간다고 답변하고 있는데...
-
happy
2020.02.28 13:06
GetItem 펑션에서 리턴되는 Generic의 T 타입이 value type semantic이 적용되어 리턴 되므로
T element 의 크기가 클 경우, 런타임 복사비용이 비례해서 크게 들지만...
TList.List를 통해서 액세스 할 경우에는
List가 arryofT로 정의되어 있고
arrayofT는 'array of T'로 ` value type이 아닌 reference type으로 정의되어 있으므로
복사가 필요한 value semantic이 적용되지 않으므로 훨씬 유리한 것임.
-
happy
2020.02.28 13:10
컴파일러의 value semantic rule에 대한 사전 지식이 없으면 이해하기 힘든 부분이고
C++ 언어는 value type이 적용될 때도... r-value move semantic rule을 지원해서 불필요한 복사가 일어나지 않도록 할 수 있으나
파스칼 언어는 코드 옵티마이징을 위한 그런 개념 조차 갖고있지 않음. 파스칼은 초보자들이 쉽게 쓸 수 있는 간단한 언어에 불과 함.
-
험프리
2020.02.28 13:50
이번 기회에 List와 Items에 대한 차이를 알게되었습니다.
답변 주신 2분께 감사드립니다.
아래 구조체로 테스트하니 극명한 차이가 나네요. 저도 성능이 필요한 경우 List 속성을 이용하도록 해야겠습니다.
TTestRecord2 = record
Field1: array[0..4999] of Integer;
Field2: array[0..4999] of Extended;
Field3: string;
end;
-
림병기
2020.02.28 15:11
고맙습니다. 잘 이해했습니다.
Delphi 디버깅 시 tList<t>.List w.a.t.c.h. 이상 작동
2020.02.25 10:21
안녕하세요? 2009 라이선스 가지고 있으며 아래 문제는 10.3.3. 커뮤니티 에디션에서 확인됩니다.
두 개 이상 유닛의 인터페이스 영역에 같은 tList<t>가 선언되어 있으면 디버깅을 할 때 tList<t>.List에 대한 w.a.t.c.h.(이용 금지 단어라네요)가 작동되지 않는 거 같습니다.
아시다시피 tList<t>는 10.3.x 이후 많이 달라졌으며 그 과정에서 오류들이 보고되고 있습니다. 간단한 코드이니 한번 확인해 보시는 게 좋을 듯합니다.
unit Unit1;
interface
uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Generics.Collections, Unit2;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
List1: TList<Extended>;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
List1 := TList<Extended>.Create;
List1.Add(1);
Caption := FloatToStr(List1.List[0]); // break and w.a.t.c.h. here
end;
unit Unit2;
interface
uses System.Generics.Collections;
type
tClass1 = class
List2: TList<Extended>; // makes the problem
end;
댓글 9
-
험프리
2020.02.27 10:46
-
림병기
2020.02.27 15:53
답변 고맙습니다.
패치가 배포된 날 뒤에 다운로드/설치를 하여 버전은 님 꺼랑 같습니다. tList<t>.Items로 하면 더 이상한 작동을 합니다. 그치만 커뮤니티 에디션에서만 일어나는 이상이라면 특별히 문제될 건 없을 듯합니다.
그건 그렇고 리스트 아이템을 호출하는 방법에 대해 제가 틀리게 알고 있나 싶어 하나 여쭤 보려 합니다. 님 설명과 레퍼런스에서 확인되는 거처럼 tList<t>.List는 비교적 최근 만들어진 프라퍼티로서 배열의 형태입니다. 그러나 이 차이에 더해 tList<t>.Items의 경우 이걸 호출한 때 호출을 한 루틴에서 내부적으로 해당 아이템에 대한 변수 복사가 일어나지만 tList<t>.List의 경우에는 포인터로 접근한다고 알고 있습니다.
전자의 경우 긴 리스트를 루핑하면 이런 복사 오버헤드로 인해 상당한 부하가 발생하지만 후자의 경우에는 산뜻하게 처리됩니다. 또한 전자의 경우 리스트의 아이템이 레코드라면 tList<record>.Items[n].Field1 := 'abcde'; ... 의 형태로 이용이 가능하지 않습니다. left side로 assign 할 수 없다는 오류가 생깁니다. 그러나 후자의 경우에는 같은 이유로 부드럽게 처리됩니다.
이런 이유들 때문에 저는 tList<t>.List가 생긴 뒤로는 tList<t>.Items나 tList[n]의 형태는 전혀 이용하지 않고 있습니다만 제가 혹시 잘못 이해하고 있는 것인지요?
-
험프리
2020.02.27 16:57
디버깅 시 Watch List에 나오는 목록이 안나온다는 이야기였네요. 제가 엉뚱한 내용으로 알아들었습니다.
(저는 실행 시 오류가 발생하는 것으로 잘못 알아들었습니다.)
저도 동일한 이슈가 발생하네요.
Function to be called, {System.Generics.Collections}TList<System.Extended>.GetList, was eliminated by linker
이 부분은 디버거의 기능으로 저도 원인은 잘 모르겠습니다.
TList<T>.Items와 TList<T>.List에 대해 저도 다시한번 살펴봤습니다.
말씀하신대로 Items에서 List를 호출하는 방식으로 구현되어있습니다.
function TList<T>.GetItem(Index: Integer): T;
begin
// CheckItemRange(Index) is expanded here manually to improve codegen
if Cardinal(Index) >= Cardinal(FCount) then
ErrorArgumentOutOfRange;
Result := List[Index];
end;
하지만, 댓글에 작성하신 "긴 리스트를 루핑하면 이런 복사 오버헤드로 인해 상당한 부하가 발생"은 잘 이해가되지 않습니다.위 코드와 같이 배열에 대해 인덱스로 찾아가기 때문에 형변환에 대한 오버해드가 많을것이라는 것은 잘 이해되지 않네요^^(제가 직접 테스트를 진행해 보지는 못했습니다.)그리고, 자주 애용하셨던 방식대로 List 속성을 이용해도 무방할것으로 보입니다.그래도 Items 속성이 말씀하신대로 사용 못할정도로 오버해드가 많지 않으니 적절하게 사용하시길 권장드립니다.저도 성능 테스트 진행해보도록 하겠습니다. -
림병기
2020.02.27 22:11
친절하고 빠른 답변에 다시 한번 감사 드립니다.
https://stackoverflow.com/questions/38990682/long-delay-when-looping-through-a-tlist-of-big-records
위 링크는 전에 제가 했던 질문인데 그 내용을 요약해 보면
tTestRecord1 = record Field1: array[0..4] of Integer; Field2: array[0..4] of Extended; Field3: string; end; tTestRecord2 = record Field1: array[0..4999] of Integer; Field2: array[0..4999] of Extended; Field3: string;
위와 같이 구조는 같고 크기는 다른 레코드의 리스트를 만들어서 tList<t>.Items[n]으로 루핑을 하면 전자에 비해 후자의 경우 시간이 많이 걸리지만 tList<t>.List[n]으로 같은 루핑을 하면 의미 있는 시간의 차이가 없다는 것입니다. 이는 인덱스로 찾아가는 과정이나 타입캐스팅에서 시간이 소요되는 것이 아니라 전자로 루핑을 하면 아이템 하나 하나를 임시 변수에 담기 때문이라는 게 답변입니다. 실제로 질문을 위해 최대로 간략하게 만들었던 예제와 달리 더 크고 복잡한 구조의 레코드를 리스트에 담아 Items[n]으로 루핑을 하면 감당하기 어려울 정도로 긴 시간이 걸렸습니다.
혹시나 시간이 오래 지나 님 설명과 같이 수정이 되었나 확인을 해 보았으나 위의 차이는 여전히 있었습니다.
-
happy
2020.02.28 13:01
엠바 디버거는 원래 부터 버그가 많고...
결론 부터 말하면...
TList.Items 보다 TList.LIst 가 훨씬 유리 한 것임.
험프리 군은...
TList<T>.Items와 TList<T>.List에 대해 저도 다시한번 살펴봤습니다.
말씀하신대로 Items에서 List를 호출하는 방식으로 구현되어있습니다.
function TList<T>.GetItem(Index: Integer): T;
begin
// CheckItemRange(Index) is expanded here manually to improve codegen
if Cardinal(Index) >= Cardinal(FCount) then
ErrorArgumentOutOfRange;
Result := List[Index];
end;
하지만, 댓글에 작성하신 "긴 리스트를 루핑하면 이런 복사 오버헤드로 인해 상당한 부하가 발생"은 잘 이해가되지 않습니다.
ITems에서 List를 호출하는 구조로 되어 있어서 이해가 안간다고 답변하고 있는데...
-
happy
2020.02.28 13:06
GetItem 펑션에서 리턴되는 Generic의 T 타입이 value type semantic이 적용되어 리턴 되므로
T element 의 크기가 클 경우, 런타임 복사비용이 비례해서 크게 들지만...
TList.List를 통해서 액세스 할 경우에는
List가 arryofT로 정의되어 있고
arrayofT는 'array of T'로 ` value type이 아닌 reference type으로 정의되어 있으므로
복사가 필요한 value semantic이 적용되지 않으므로 훨씬 유리한 것임.
-
happy
2020.02.28 13:10
컴파일러의 value semantic rule에 대한 사전 지식이 없으면 이해하기 힘든 부분이고
C++ 언어는 value type이 적용될 때도... r-value move semantic rule을 지원해서 불필요한 복사가 일어나지 않도록 할 수 있으나
파스칼 언어는 코드 옵티마이징을 위한 그런 개념 조차 갖고있지 않음. 파스칼은 초보자들이 쉽게 쓸 수 있는 간단한 언어에 불과 함.
-
험프리
2020.02.28 13:50
이번 기회에 List와 Items에 대한 차이를 알게되었습니다.
답변 주신 2분께 감사드립니다.
아래 구조체로 테스트하니 극명한 차이가 나네요. 저도 성능이 필요한 경우 List 속성을 이용하도록 해야겠습니다.
TTestRecord2 = record
Field1: array[0..4999] of Integer;
Field2: array[0..4999] of Extended;
Field3: string;
end;
-
림병기
2020.02.28 15:11
고맙습니다. 잘 이해했습니다.
저의 경우 동일한 코드로 이슈가 재현되지 않습니다.
저는 10.3.3 버전( RAD Studio 10.3 Version 26.0.36039.7899) 사용 중입니다.
List1 := TList<Extended>.Create;
List1.Add(1);
Caption := FloatToStr(List1.Items[0]);
Caption := FloatToStr(List1.List[0]);
하나 안내드릴 부분은
List.List[0] 대신 List1.Items[0] 또는 List1[0] 등으로 사용하는 것을 권장합니다.
List 속성은 Items의 내용을 배열로 변환해 반환하는 함수로 배열형태가 필요하지 않다면 바로 데이터에 접근하는 Items 속성을 이용하는것을 추천드립니다.
(작성해주신 코드 상으로는 문법상 문제가 없을 것 같은데요^^ 제가 진행한 패치 중에 해당 이슈가 해결된것일수도 있을것 같습니다.)