Is there any advantage to using the old `object` object instead of the` class` in Delphi?

In Delphi sane, people use a class to define objects.
In Turbo Pascal for Windows, we used object , and today you can use object to create an object.

The difference is that a object lives on the stack, and class lives on the heap.
And, of course, object depreciating.

Putting all that aside:

Is there any advantage that could be used, speed using object instead of class?

I know that object broken in Delphi 2009, but I have a special use case 1) where speed matters, and I'm trying to find if using object will make my thing faster without making it an error
This code base is in Delphi 7, but I can transfer it to Delphi 2007, have not decided yet.


1) Conway's game of life

Long comment
Thanks to everyone for pointing me in the right direction.

Let me explain a little more. I am trying to run a faster implementation of hashlife , see also here or here for simple source code

The current record holder is golly , but golly uses direct translation of Bill Gosfer's lisp source code (which is brilliant as an algorithm, but not optimized at the micro level at all). Hashlife allows you to calculate the generation in O (log (n)) time.

He does this using space / time commutation. And for this reason hashlife requires a lot of memory, gigabytes are not unheard of. In return, you can calculate the 2 ^ 128 generation (340282366920938463463374607431770000000) using the 2 ^ 127 generation (170141183460469231731687303715880000000) o (1) times.

Since hashlife needs to calculate hashes for all submatrices that appear in the larger template, the distribution of objects should be fast.

Here is the solution I decided on:

Distribution optimization
I allocate one large block of physical memory (user configurable), say 512 MB. Inside this blob, I highlight what I call cheese stacks . This is a normal stack where I press and pop, but pop can also be in the middle of the stack. If this happens, mark it in the free list (this is a normal stack). When I click, I check the free list first, if nothing is free, I click as usual. I will use the records, as was said, this seems like a solution with the least amount of overhead.

Due to how hashlife works, there is very little pop ping and lots of push es. I keep separate stacks for structures of different sizes, making sure that memory access is aligned at 4/8/16 byte boundaries.

Other optimizations

  • recursion removal
  • cache optimization
  • using inline
  • Hashes precomputed (like rainbow tables)
  • detection of pathological cases and the use of the recession algorithm
  • GPU usage
+8
object class delphi delphi-2007 delphi-7
source share
4 answers

To use normal OOP programming, you should always use the class view . You will have the most powerful object model in Delphi, including the interface and generics (in later versions of Delphi).

1. Entries, pointers and objects

Records can be evil (slow blind copy, if you forget to declare the parameter as const , write a hidden slow cleaning code, fillchar will make any line in the record a memory loss ...), but they are sometimes very convenient for accessing the binary structure (for example, some "small value") using a pointer.

A dynamic array of tiny records (for example, with one integer and one double field) will be much faster than a TList small classes; with our TDynArray wrapper , you will get access to records at a high level, with serialization, sorting, hashing, etc.

If you use pointers, you should know what you are doing. It is definitely preferable to stick with classes and TPersistent if you want to use the magic "VCL component ownership model".

Inheritance is not allowed for records. You need to either use the "variant entry" (using the case keyword in the type definition) or use nested entries. When using the C-like API, you sometimes have to use object-oriented structures. The use of nested records or variant records IMHO is much less clear than a good old model of object inheritance.

2. When to use the object

But there are places where objects are a good way to access existing data.

Even the object model is better than the new recording model, because it handles simple inheritance.

In a blog post last summer , I added some features for using objects:

  • A memory mapping file that I want to analyze very quickly: a pointer to such an object is simply large, and you still have methods; I use this for TFileHeader or TFileInfo that display the .zip header in SynZip.pas; A.

  • The Win32 structure, defined by an API call, in which I put convenient methods for easy access to data (for this you can use a record, but if the structure has some orientation of objects, which is very common, you will have to write records, which is not very convenient );

  • The temporary structure defined on the stack is just used during the procedure: I use this for TZStream in SynZip.pas or for our RTTI-related classes that display the Delphi generated RTTI in an object-oriented way, and not like TypeInfo, which is a function / procedure . By directly matching the contents of the RTTI memory, our code is faster than using the new RTTI classes created on the heap. We are not creating any memory that is good for its speed for an ORM platform such as ours. We need a lot of RTTI information, but we need it quickly, we need it directly.

3. How is the implementation of the object implemented in modern Delphi

The fact that the object is broken in modern Delphi is a shame, IMHO.

Usually, if you define an entry on the stack containing some variables counted by reference (for example, a string), it will be initialized with some magic compiler code at the initial level of the method / function:

 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 

Those _InitializeRecord and _FinalizeRecord will "cook", then "free" the O.Str variable.

Since Delphi 2010, I found out that sometimes this _InitializeRecord () was not always executed. If the record does not contain only any open fields, hidden calls are sometimes not generated by the compiler.

Just create the source again and it will ...

The only solution I found out was to use the record keyword instead of the object.

So, what does the resulting code look like:

 /// 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; 

{$ifdef UNICODE}record{$else}object{$endif} terrible ... but the code generation error did not occur, because ..

The resulting changes in the source code are not huge, but a little disappointing. I found out that an older version of the IDE (for example, Delphi 6/7) cannot parse such a declaration, so the class hierarchy will be broken in the editor ...: (

Backward compatibility should include regression tests. Many Delphi users remain in this product due to existing code. Function disruption is very problematic for the future of Delphi, IMHO: if you need to rewrite a lot of code, should you just switch the project to C # or Java?

+12
source share

Object not a Delphi 1 method for setting objects; it was the short-lived Turbo Pascal object creation method, which was replaced by the Delphi TObject model in Delphi 1. It was supported for backward compatibility, but it should be avoided for several reasons:

  • As you already noted, it is broken in later versions. And AFAIK has no plans to fix it.
  • This is a conceptual incorrect object model. The whole point of object-oriented programming, the only thing that really distinguishes it from procedural programming, is the Liskov substitution (inheritance and polymorphism), and inheritance types and values ​​do not mix.
  • You are losing support for many features that require TObject descendants.
  • If you really need value types that don't require dynamic allocation and initialization, you can use records instead. You cannot inherit from them, but you cannot do it very well with Object , so as not to lose anything here.

As for the rest of the question, there are not many speed advantages. The TObject model is pretty fast, especially if you use FastMM memory manager to speed up the creation and destruction of objects, and if your objects contain many fields, they can be even faster than records in many cases, re passed by reference and do not need to be copied for each call functions.

+7
source share

When choosing between “fast and possibly broken” and “fast and right” always choose the latter.

Old-style objects do not offer speed incentives compared to regular old records, so wherever you want to use old-style objects, you can use records instead, without risking having uninitialized types controlled by compilers or broken virtual methods. If your version of Delphi does not support recording using methods, just use stand-alone procedures.

+5
source share

Returning to older versions of Delphi that did not support writing using methods, and then using object , was a way to get your objects allocated on the stack. Very rarely, this could benefit performance. Currently, the record better. The only function missing from the record is the ability to inherit from another record .

You give back a lot when you change from class to record , so only consider this if the performance benefits are stunning.

+1
source share

All Articles