Joh*_*ica 10 delphi class object delphi-7 delphi-2007
在Delphi中,理智的人使用a class来定义对象.
在我们使用的Turbo Pascal for Windows中object,今天您仍然可以使用它object来创建对象.
不同之处在于object堆栈上的class生命和堆上的生命.
当然,这object是折旧的.
Putting all that aside:
通过使用object而不是上课来提高速度是否有益处?
我知道object在Delphi 2009 中已经破解了,但是我有一个特殊的用例1)速度很重要我试图找到是否使用object会使我的东西更快而不会让它变得越来越多
这个代码库是在Delphi 7中,但是我可能把它移植到Delphi 2007,尚未决定.
1)康威的生命游戏
长期评论
感谢所有人指出我正确的方向.
让我解释一下.我正在尝试更快地实现hashlife,另请参阅此处或此处的简单源代码
目前的记录保持者很高兴,但是他使用Bill Gospher原始的lisp代码的直接翻译(这是一种很好的算法,但根本没有在微观层面进行优化).Hashlife使您能够在O(log(n))时间内计算生成.
它通过使用空格/时间权衡来实现.由于这个原因,hashlife需要大量内存,千兆字节并非闻所未闻.作为回报,您可以在o(1)时间内使用第2代^ 127(170141183460469231731687303715880000000)计算第2代128(340282366920938463463374607431770000000).
因为hashlife需要为更大模式中出现的所有子模式计算哈希值,所以对象的分配需要很快.
这是我已经解决的解决方案:
分配优化
我分配一大块物理内存(用户可设置)让我们说512MB.在这个blob中我分配了我称之为奶酪堆栈的东西.这是我推送和弹出的普通堆栈,但是pop也可以来自堆栈的中间.如果发生这种情况,我会在free列表上标记它(这是一个正常的堆栈).当free我按下时,如果没有任何空闲,我先检查列表,我正常推动.我将使用记录,因为它看起来像具有最少开销的解决方案.
由于hashlife的工作方式,很少发生popping和很多pushes.我为不同大小的结构保留单独的堆栈,确保在4/8/16字节边界上保持内存访问对齐.
其他优化
inlineArn*_*hez 13
对于使用普通的OOP编程,您应该始终使用该class类型.你将拥有Delphi中最强大的对象模型,包括接口和泛型(在后来的Delphi版本中).
1.记录,指针和对象
记录可能是邪恶的(慢速隐藏副本,如果你忘记声明一个参数const,记录隐藏的慢速清理代码,一个fillchar会使记录中的任何字符串成为内存泄漏...),但它们有时非常方便访问二进制结构(例如一些"小值"),通过指针.
的微小的记录A的动态阵列(例如与一个整数和一个双场)将多比较快的TList小的类; 使用我们的TDynArray包装器,您将拥有对记录的高级访问权限,包括序列化,排序,散列等.
如果使用指针,您必须知道自己在做什么.最好坚持使用类,TPersistent如果你想使用神奇的"VCL组件所有权模型".
记录不允许继承.您需要使用"变体记录"(case在其类型定义中使用关键字),要么使用嵌套记录.使用类C语言API时,有时您必须使用面向对象的结构.使用嵌套记录或变体记录是恕我直言,而不是好旧的"对象"继承模型.
2.何时使用对象
但是在某些地方,对象是访问现有数据的好方法.
甚至对象模型也比新记录模型更好,因为它处理简单的继承.
在去年夏天的博客文章中,我发布了一些仍然使用对象的可能性:
一个内存映射文件,我想非常快速地解析:指向这样一个对象的指针很棒,你手边还有方法; 我将它用于TFileHeader或TFileInfo,它映射了.zip标题,在SynZip.pas中;
一个Win32结构,由API调用定义,我在其中放置了方便的方法来轻松访问数据(为此你可以使用记录,但如果结构中有一些面向对象 - 这很常见 - 你会有嵌套记录,这不是很方便);
在堆栈中定义的临时结构,仅在过程中使用:我将它用于SynZip.pas中的TZStream,或者用于我们的RTTI相关类,它们以面向对象的方式映射Delphi生成的RTTI而不是作为函数的TypeInfo /程序导向.通过直接映射RTTI内存内容,我们的代码比使用在堆上创建的新RTTI类更快.我们没有实现任何内存,对于像我们这样的ORM框架来说,它的速度是有益的.我们需要大量的RTTI信息,但我们需要它很快,我们需要它直接.
3.现代Delphi中如何破坏对象实现
在现代德尔福中,对象被破坏的事实是一种耻辱,恕我直言.
通常,如果您在堆栈上定义一个记录,包含一些引用计数变量(如字符串),它将在方法/函数的开始级别由一些编译器魔术代码初始化:
type TObj = object Int: integer; Str: string; end;
procedure Test;
var O: TObj
begin // here, an _InitializeRecord(@O,TypeInfo(TObj)) call is made
O.Str := 'test';
(...)
end; // here, a _FinalizeRecord(@O,TypeInfo(TObj)) call is made
Run Code Online (Sandbox Code Playgroud)
那些_InitializeRecord和_FinalizeRecord将"准备",然后"释放" O.Str变量.
在Delphi 2010中,我发现有时候,这个_InitializeRecord()并不总是如此.如果记录只有一些没有公共字段,则隐藏调用有时不会由编译器生成.
只需再次构建源代码,就会......
我发现的唯一解决方案是使用record关键字而不是object.
所以这里是结果代码的样子:
/// used to store and retrieve Words in a sorted array
// - is defined either as an object either as a record, due to a bug
// in Delphi 2010 compiler (at least): this structure is not initialized
// if defined as a record on the stack, but will be as an object
TSortedWordArray = {$ifdef UNICODE}record{$else}object{$endif}
public
Values: TWordDynArray;
Count: integer;
/// add a value into the sorted array
// - return the index of the new inserted value into the Values[] array
// - return -(foundindex+1) if this value is already in the Values[] array
function Add(aValue: Word): PtrInt;
/// return the index if the supplied value in the Values[] array
// - return -1 if not found
function IndexOf(aValue: Word): PtrInt; {$ifdef HASINLINE}inline;{$endif}
end;
Run Code Online (Sandbox Code Playgroud)
这{$ifdef UNICODE}record{$else}object{$endif}很糟糕......但是自从..代码生成错误没有发生..
由此产生的源代码修改并不大,但有点令人失望.我发现旧版本的IDE(例如Delphi 6/7)无法解析此类声明,因此类层次结构将在编辑器中被破坏...... :(
向后兼容性应包括回归测试.由于现有代码,许多Delphi用户都会使用此产品.对于Delphi未来来说,破解功能是非常有问题的,恕我直言:如果你必须重写很多代码,那你不应该只将项目切换到C#或Java?
Object不是Delphi 1设置对象的方法; 这是设置对象的短命Turbo Pascal方法,它被Delphi 1中的Delphi TObject模型取代.它被保留以便向后兼容,但应该避免它,原因如下:
object所以你不会在这里丢失任何东西.至于问题的其余部分,并没有那么多的速度优势.TObject模型速度非常快,特别是如果你使用FastMM内存管理器加速对象的创建和销毁,如果你的对象包含很多字段,它们甚至比很多情况下的记录更快,因为它们'通过引用传递,不必为每个函数调用复制.
当在"快速和可能破碎"和"快速和正确"之间做出选择时,总是选择后者.
旧式对象不提供普通旧记录的速度激励,因此无论您何时想要使用旧式对象,都可以使用记录而不会有未初始化的编译器管理类型或破坏的虚拟方法.如果您的Delphi版本不支持带方法的记录,那么只需使用独立程序.
| 归档时间: |
|
| 查看次数: |
2985 次 |
| 最近记录: |