我可以在不调用隐藏的try-finally的

时间:2015-04-26 19:11:35

标签: delphi interface refcounting

我希望refactor DelphiAST使用接口来处理不同类型,而不是笨重 它现在使用的TDirectionary。

Some research表明70%以上的运行时间都花费在字典中。

所以我会创建像:

这样的接口
TSyntaxNode = class
  ...
end;

IIdentifier = interface
  ['{D72E945D-E397-4E02-B702-8AE6C235F9C6}']
  function GetIdentifier: string;
  property Identifier: string read GetIdentifier;
end;

IMethod = interface(Identifier)
  ['{8E6119DC-E0F3-42BD-A5BF-FB658E34499E}']
  .....
end;

TMethodNode = class(TSyntaxNode, IMethod, IIdentifier,...)
 ...
end;

根据罗马的问题是:

  

引用计数可能会导致性能问题。 DelphiAST创建了数千个类来生成语法树(当输入文件足够大时,超过100,000个TSyntaxNode实例)。调用参考计数器多少次?

每次发生这种情况时,都会调用隐藏的try finally,这会减慢速度。

  

在方法参数中严格使用const会阻止调用该方法的引用代码,但是每当你执行类似MyRef = List[0]之类的事情时它仍会发生 - 它会增加指定给{的引用计数{1}},即使该项目仍然存在于列表中。

如何使用界面而不必担心引用计数和try-finally阻止?
我非常乐意手动管理类的破坏。

更多信息
我猜我需要用MyRef作为基础祖先 我读到某个地方,没有指定GUID禁止引用计数,但必须提供来支持这一点 但是丢失GUID会导致获取子接口的问题,所以我不得不设计一个解决方案....

3 个答案:

答案 0 :(得分:2)

  

我可以在不调用隐藏的try-finally的情况下使用接口吗?

没有。无论如何,编译器都会使用接口发出引用计数代码。你无法避免它。

您可以使用函数指针的记录实现自己的接口版本。它会更笨重,但会避免堆分配和引用计数。

答案 1 :(得分:0)

"成千上万的物品"总是让我颤抖。内存中的对象存在很大的开销。你忘了它,但是当你试图管理成千上万,或者注意到你的表现松散,或者开始尝试写文件或从文件中读取时,它会再次弹出......

使用接口不会发生太大的变化,因为你仍然使用下面的对象(类实例)。

如此规模的努力需要特定使用良好的直接存储器数据结构。例如,我一直在使用存储在记录数组中的AST:https://github.com/stijnsanders/strato

答案 2 :(得分:0)

如果没有调用try-finally并重新计数, 就不能使用界面。
但是,您可以大大减少隐藏的异常处理程序的数量 你必须非常小心做两件事。

  1. 传递接口时始终使用const参数。

  2. 永远不要将接口存储在接口类型变量中,而是使用自制记录来封装接口,以便不会触及其引用计数。

  3. 这是封装记录的一个示例:

    type
      TInterface<Intf: IInterface> = record
      private
        P: Pointer;
      public
        function I: Intf; inline;
        class operator Implicit(const A: Intf): TInterface<Intf>; inline;
      end;
    
    function TInterface<Intf>.I: Intf;
    begin
      pointer(IInterface(Result)):= P;
    end;
    
    class operator TInterface<Intf>.Implicit(const A: Intf): TInterface<Intf>;
    begin
      Result.P:= pointer(IInterface(A));
    end;
    

    这是一个展示概念的示例程序。

    program Project32;
    {$APPTYPE CONSOLE}
    
    {$R *.res}
    uses
      System.SysUtils;
    
    type
      TInterface<Intf: IInterface> = record
      private
        P: Pointer;
      public
        function I: Intf; inline;
        class operator Implicit(const A: Intf): TInterface<Intf>; inline;
      end;
    
      ITest1 = interface
        function Test1: integer;
      end;
    
      ITest2 = interface
        function Test2: integer;
      end;
    
      TTest = class(TAggregatedObject, ITest1, ITest2)
        function Test1: integer;
        function Test2: integer;
      end;
    
    { TTest }
    
    function TTest.Test1: integer;
    begin
      Result:= 1;
    end;
    
    function TTest.Test2: integer;
    begin
      Result:= 2;
    end;
    
    { TInterface<Intf> }
    
    function TInterface<Intf>.I: Intf;
    begin
      pointer(IInterface(Result)):= P;
    end;
    
    class operator TInterface<Intf>.Implicit(const A: Intf): TInterface<Intf>;
    begin
      Result.P:= pointer(IInterface(A));
    end;
    
    var
      I1: TInterface<ITest1>;
      I2: TInterface<ITest2>;
      Test: TTest;
    
    begin
      Test:= TTest.Create(nil);  //Force AV on _AddRef, _Release
      If (Test.Test1 = 1) then WriteLn(S);
      I1:= Test;
      If (I1.I.Test1 =1) then WriteLn(S);
      I2:= Test;
      If (I2.I.Test2 = 2) then WriteLn(S);
      ReadLn(s);
      Test.Free;
    end.
    

    TAggregatedObject没有用于处理_AddRef / _Release来电的界面。
    在程序的生命周期中,不会出现任何问题,但是Delphi确实将TTest的创建包装在try-finally中,这将在退出函数时生成异常。

    在实际使用中,您必须使用TInterfacedObject。如果您通过大量的接口引用,它可能会有所帮助。