我希望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会导致获取子接口的问题,所以我不得不设计一个解决方案....
答案 0 :(得分:2)
我可以在不调用隐藏的try-finally的情况下使用接口吗?
没有。无论如何,编译器都会使用接口发出引用计数代码。你无法避免它。
您可以使用函数指针的记录实现自己的接口版本。它会更笨重,但会避免堆分配和引用计数。
答案 1 :(得分:0)
"成千上万的物品"总是让我颤抖。内存中的对象存在很大的开销。你忘了它,但是当你试图管理成千上万,或者注意到你的表现松散,或者开始尝试写文件或从文件中读取时,它会再次弹出......
使用接口不会发生太大的变化,因为你仍然使用下面的对象(类实例)。
如此规模的努力需要特定使用良好的直接存储器数据结构。例如,我一直在使用存储在记录数组中的AST:https://github.com/stijnsanders/strato
答案 2 :(得分:0)
是如果没有调用try-finally
并重新计数, 就不能使用界面。
但是,您可以大大减少隐藏的异常处理程序的数量
你必须非常小心做两件事。
传递接口时始终使用const
参数。
永远不要将接口存储在接口类型变量中,而是使用自制记录来封装接口,以便不会触及其引用计数。
这是封装记录的一个示例:
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。如果您通过大量的接口引用,它可能会有所帮助。