Talk about the optimization of localobject memory allocation in Delphi

xiaoxiao2021-04-10  375

Talk about the optimization of localobject memory allocation in Delphi

As we all know, all Object is open inside the heap. In fact, Delphi is the concept of LocalObject in C . However, the efficientness of localobject memory allocation is very obvious. This article is mainly how to apply Delphi's Object to STACK. Mechanism is actually very simple, most of which can be optimized in this way (if an optimization is necessary). The younger brother throws jade here, is willing to communicate with the people.

For the use of LocalObject, it is basically a temporary object, and the writing in Delphi and C is this look.

Sample.cpp

/// ......

Int test ()

{

SampleClass A;

Return a.execute (1, 2);

}

/// sample.pas

Function test (): integer;

VAR

A: tsampleclass;

Begin

A: = tsampleclass.create ();

Try

Result: = a.execute (1, 2);

Finally

a.free ();

END;

END;

In the form of a form, the two functions are almost exactly the same. However, their efficiency has a lot of differences, and some people can test themselves. The efficiency of Stack Object is significantly higher than that of Heap Object. In particular, such a small function is called very much, such as the intersection of the geometric function I encountered in the actual engineering, the number of times the number of times is 10 million, and some places directly affect the interface. Refresh, efficiency requirements are relatively high. It is no longer able to understand that the memory allocation here is not so bad, so you will be able to understand some hands and feet.

My method is very simple, that is, assigns memory to the system stack. We know, there is a Record structure in Delphi (Struct in C / C ), although the Record is not as easy as C, but his memory is allocated on the stack. This is the same. (In fact, you can also use static arrays, you can do this, essentially the same. I still use a RECORD here, which should be understood.)

I first put the modified code, and then explain the principle.

/// sample.pas

Type

TsampleClass = Class (TOBJECT)

public

Procedure firmize (); responsible for release of a member variable of reference type

END;

TsampleClassRec = Record

Fcontent: array [0..const_tsampleclass_size-1] of byte;

END;

/// ....

Function test (): integer;

VAR

Arec: tsampleclassrec; /// A size and TSAMPLECLASS RECORD

A: tsampleclass;

Begin

A: = TsampleClass (Tsampleclass.initInstance (@arec)); note that this is different

A.CREATE ();

Try

Result: = a.execute (1, 2);

Finally

Finalize ();

A.cleanupinstance (); note, here is different

END;

END;

Below, let's explain the reason.

In Delphi, the creation of Object is done by TOBJECT.CREATE. Let's take a look at what is done in TOBJECT.CREATE.

We can go to the System.Pas unit to see this function, which will be surprised, empty, nothing! ! ! did nothing! ! ! Yes, it doesn't have anything, but it is definitely not done. We said that there are still a lot of things. People are the style of Tianlu girl, like to sneak, do not want to be seen by others. So how do you finish it? Here you can suggest a book, Mr. Li Wei's "Inside VCL" is more detailed in detail. However, the brothers who don't like reading books are not related. I can see what I wrote, about 100 words, I can also understand the process (like a deep brother or suggestion, actually calling SYSTEM.PAS _Classcreate function, a bunch of compilation, temporarily supplying first).

We can see an instance provided by Delphi, and he cut the whole process, divided, called template type code. Please see the VCL source code for forms.pas Procedure Tapplication.createform (InstanceClass: TcomponentClass; VAR Reference); function.

Procedure Tapplication.createform (InstanceClass: TcomponentClass; Var Reference);

VAR

Tcomponent;

Begin

Instance: = tComponent (InstanceClass.newinstance); First, create an instance, allocate memory, VMT, etc.

Tcomponent (Reference): = Instance;

Try

Instance.create (Self); now, call the initialization function, initialize USERDATA

Except

Tcomponent: = NIL;

Raise;

END;

IF (Fmainform = NIL) and (instance is tform) THEN

Begin

TFORM (Instance) .handleneed;

Fmainform: = TFORM (Instance);

END;

END;

Good, TOBJECT.CREATE is actually completed by these two steps. First, NewInstance, then create. Here, everyone will find a question, here is called CREATE through an example instead of normal passing Class. The difference between these two ways is whether or not the first NewInstance process is required to open a memory. This can be in Function_ClassCreate (ACLASS: TCLASS; to Boolean): TOBJECT; see, parameter alloc: boolean and jl @@ noalloc This line is depending on the way the memory is required.

Here, our task is to see what newinstance has made. Discover the following results

Class function TOBJECT.NEWINSTANCE: TOBJECT; Begin

Result: = INITINSTANCE (_getMem (instancesize));

END;

It turned out that the _getmem is implemented, then the problem is simple, what is the mysterious object-oriented a big set of complex moves, under the orchic nine sword, look at his essence, or a memory, and it is no two, ordinary can't Ordinary memory blocks.

So far, our LocalObject mechanism has nothing to cover, complete, completely, naked out in front of it. My mission is also completed, acting as a surgical doctor's role, should say "report captain, I have already cut it, please accept" "Well, you can go back" "Receive".

To be sewing, you need to pay attention to a few places, or seash the wrong, get a five senses, it is too sorry for the title of the doctor, Lu Xun's deep and evil, we will follow the sages and can not open.

The first question, how is the actual size of TsampleClass. This is very unfortunate, we can't get the size of an Object with SIZEOF. There are two ways, one is a way forward, but it is very painful and troubles; one is a little evil, but it is very effective. It is very simple. The way the right path is naturally analyzed. It is directly analyzed, how much memory does actually, but this requires a deep skill to do, and even if the skill is really deep, it is inevitable to make mistakes, it is easy to walk into the magic, not paying attention to it. Another way, I recommend the recommended way, run the program below, invoke the trial interface, or write Message, call the instancesize function, see that the number is OK. mission completed. However, as a conscience doctor, although it cannot cure the problem, it is best to give a defense measures. Strongly recommended, assert (tsampleclass.instancesize () = sizeof (tsampleclassrec);

The second question, don't pay attention to it, you can't call free, or what is not required for this STACK OBJECT. However, this will also bring a problem. There is a problem with the release of members. It doesn't matter if INTEGER, CHAR, DOUBLE, etc. can be ignored, but for String, dynamic arrays, etc., it is best to release first. Thus, a finalize () function is responsible for the release of a member of the reference type, remember to call in the FinalLy section.

The third question, Cleanupinstance. Still needs to be called, this is called the broken. The big husband is bright, and it has been clear and understand.

In addition, the optimization of this approach is that when the number of call times, and the time and algorithm that allocate memory, the effect is not significant. The reason why this effect is that the STACK is allocated only to modify the ESP value, no access is available. GetMem, we said at least one system call. How did Windows achieve, it is unclear. When he studied operating system, the teacher told a buddy memory allocation algorithm, quite clever, and quite comparable. The time to distribute the flower on the stack is not synonymous. Because he is a common memory allocation algorithm, it is necessary to consider the release of memory, recycled, and consider the processing of a memory fragment. So, based on this idea, we actually except for the mechanism to use the mechanism described in this document, for the Object, which does need to create in the stack, is specifically written in a memory pool. For example, open a large number of TsampleClassRec, every time you create, throw it out, and release it back. This mechanism is more efficient than the general-purpose distribution mechanism. We can also explore it. (In this, the Effectice C book is a little mention, but it is also enough. The master is a master, and a few words can make it clear, so that I have a lot of feelings, I have this text. Ties respectfully.) Finally, the doctor needs to take a break. I hope this little skill can solve some problems for you, and it is a good fortune. Throwing jade, more sharing.

The efficiency optimization of the temporary objects of some instances, pay attention to the concept of temporary objects.

For example: Some of this form of functions in our CalcFunc, I have an example

Class function tcseg2d.intersectwith (const line1, line2: twxline; var pt1,

Pt2: twpoint; atol: double): TintResult;

VAR

SEG1, SEG2: TCSEG2D;

Begin

Seg1: = tcseg2d.create (line1);

Try

Seg2: = tcseg2d.create (line2);

Try

Result: = seg1.intersectwith (seg2, pt1, pt2, atol);

Finally

Seg2.free;

END;

Finally

Seg1.free;

END;

END;

In our implementation logic, you can see seg1, seg2, in addition to participating in small calculations, there is no other role, local creation,

Locally released. We call these things temporary objects. (He actually plays the role is also a temporary store)

However, there is actually a small overhead here, he must create two instances on the heap, need to pass _getmem to get the memory he needs.

This overhead is actually large, especially for frequently called, is simply rising as the number of calls of calls.

So, my idea is to open these memory, put it on the stack. I tried it, such optimization efficiency is still more considerable.

For example, there is such a class

Tsmall = Class (TOBJECT)

Private

FCONText: integer;

public

Constructor crete;

DESTRUCTOR DESTROY; OVERRIDE;

Procedure.

Function SUM (I, J: Integer): Integer;

END;

Inside an Integer, simulate data, in fact, even if the required function table is theoretically, there is no problem. The Sum function is our business function.

Seeing the actual size according to his instancesize is 8 bytes.

So I also defined an 8-byte replard. Apply for memory as a stack.

TsmallRec = Record

FContext: array [0..1] of integer;

END;

If you need to pay attention, we can't call the Destroy function, because it will do the operation of the release of the memory, but our memory will create on the stack.

So the function will get stake yourself. So there is still a need to release other resources other than memory, so define a Release function.

///

Now look at the actual use method

This is the original usage

Procedure ttestsmall.dool;

Begin

WITH TSMALL.CREATE () DO

Try

Sum (1, 3)

Finally

FREE ();

END;

END;

New usage

Procedure ttestsmall.donew;

VAR

RSMall: TsmallRec;

Osmall: Tsmall;

Begin

Osmall: = Tsmall (Tsmall.initInstance (@rsmall));

With osmall.create () DO

Try

SUM (1, 3);

Finally

Cleanupinstance ();

END;

END;

I made 1000 calls for them, the result is

Old: Time Used (Run 1000 Times): 1092

New: Time Used (Run 1000 Times): 454

/

I made a test, I got a conclusion:

The larger Object, the better the effect.

The more simple business, the better the effect.

You can refer to memory management, I will basically consider the Mechanism of the Buddy algorithm.

转载请注明原文地址:https://www.9cbs.com/read-133314.html

New Post(0)