Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/VCL/Core VCL Classes    [ Add a report in this area ]  
Report #:  53738   Status: Closed
Change all RTL and VCL enumerators from class to record
Project:  Delphi Build #:  11.0.2799.9201
Version:    11.1 Submitted By:   Hallvard Vassbotn
Report Type:  Suggestion / Enhancement Request Date Reported:  10/20/2007 12:51:11 AM
Severity:    Commonly encountered problem Last Updated: 3/20/2012 2:24:39 AM
Platform:    All versions Internal Tracking #:   256282
Resolution: Won't Do (Resolution Comments) Resolved in Build: : 12.0.3170.16989
Duplicate of:  None
Voting and Rating
Overall Rating: No Ratings Yet
0.00 out of 5
Total Votes: None
Description
As documented here:
http://hallvards.blogspot.com/2007/10/more-fun-with-enumerators.html

the compiler generates more efficient code for record enumerators than class enumerators. Class enumerators require heap allocations and auto-generated try-finally sections to free the enumerator instance after a for-in loop. Record enumerators removes these two steps, producing smaller and faster code.
Steps to Reproduce:
Change all RTL and VCL enumerators like:

  TListEnumerator = class
  private
    FIndex: Integer;
    FList: TList;
  public
    constructor Create(AList: TList);
    function GetCurrent: Pointer;
    function MoveNext: Boolean;
    property Current: Pointer read GetCurrent;
  end;

to:

  TListEnumerator = record
  private
    FIndex: Integer;
    FList: TList;
  public
    constructor Create(AList: TList);
    function GetCurrent: Pointer;
    function MoveNext: Boolean;
    property Current: Pointer read GetCurrent;
  end;

This applies to a number of enumerator classes:

TActionListEnumerator = class
TTreeNodesEnumerator = class
TListItemsEnumerator = class
TToolBarEnumerator = class
TFavoriteLinkItemsEnumerator = class
TTaskDialogButtonsEnumerator = class
TMenuItemEnumerator = class
TFieldsEnumerator = class
TListEnumerator = class
TInterfaceListEnumerator = class
TCollectionEnumerator = class
TStringsEnumerator = class
TComponentEnumerator = class
TWideStringsEnumerator = class
Workarounds
None
Attachment
None
Comments

Sebastian Zierer at 11/13/2007 12:36:06 PM -
ATTENTION....

This change breaks the following code, because it it impossible to derive from a record enumerator.


unit Unit2;

interface

uses
  Classes, Contnrs;

type
  TShape = class(TObject)

  end;

  TShapeEnumerator = class(TListEnumerator)
  public
    function GetCurrent: TShape;
    property Current: TShape read GetCurrent;
  end;

  TShapeList = class(TObjectList)
  private
    function GetItem(Index: Integer): TShape;
    procedure SetItem(Index: Integer; const Value: TShape);
  public
    function GetEnumerator: TShapeEnumerator;
    function Add(AObject: TShape): Integer;
    property Items[Index: Integer]: TShape read GetItem write SetItem; default;
  end;

implementation

{ TShapeList }

function TShapeList.Add(AObject: TShape): Integer;
begin
  Result := inherited Add(AObject);
end;

function TShapeList.GetEnumerator: TShapeEnumerator;
begin
  Result := TShapeEnumerator.Create(Self);
end;

function TShapeList.GetItem(Index: Integer): TShape;
begin
  Result := TShape(inherited Items[Index]);
end;

procedure TShapeList.SetItem(Index: Integer; const Value: TShape);
begin
  inherited Items[Index] := Value;
end;

procedure Test;
var
  ShapeList: TShapeList;
  Shape: TShape;
begin
  ShapeList := TShapeList.Create;
  try
    for Shape in ShapeList do
    begin
      
    end;
  finally
    ShapeList.Free;
  end;
end;

{ TShapeEnumerator }

function TShapeEnumerator.GetCurrent: TShape;
begin
  Result := TShape(inherited GetCurrent);
end;

initialization
  Test;
end.

Rolf Einar Eriksen at 11/14/2007 5:28:40 AM -
Why would you need that for any of the VCL and RTL enumerators? They are only using the current simple structure, and nothing else. So there is no big use for inheritance. You save a few lines, but you miss much on the performance side. This means that the gain would be bigger if they use records instead.

If you want to use class style in your collection classes, that is your choice..

Sebastian Zierer at 11/15/2007 1:46:31 PM -
Did you run any speed tests?

Enumerating through 20,000,000 record gives me the following results:

class: 421ms
class + inline: 433ms
record: 439ms
record + inline: 390ms
for..to loop: 218ms

I was a little surprised that using inline makes the class enumerator slower. On the other hand a record only makes sense if you use the inline keyword. This makes me wonder if a record enumerator + inline really beats the class enumerator in a real world program.

If speed is an issue, use a simple for .. to loop instead.

Server Response from: ETNACODE01