Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2012-02

Faster InitObject

25 messages · 9 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Amir

Hi,

I have a code, developed in object pascal, with many classes. The project is working fine.
Today, I used callgrind (valgrind --tool=calgrind) to see which function consumes the most execution time and I noticed that the most of time in my project is consumed by fillchar function.

Incl. Self. Called Function
29.78 29.72 26M SYSTEM_FILLCHAR$formal$INT64$BYTE 19.07 19.07 123M SYSTEM_MOVE$formal$formal$INT64 3.63 3.63 70M SYSTEM_SYSGETMEM_FIXED$QWORD$$POINTER ....

The main caller of FillChar function is
system_TObject_$__NewInstance$$TObject which called Fillchar 26 Million times.

NewInstance method in TObject calls InitInstance which is an inline function and defined as follows:

        class function TObject.InitInstance(instance : pointer) :
tobject; {$ifdef SYSTEMINLINE} inline; {$ENDIF}
          begin
             { the size is saved at offset 0 }
             fillchar(instance^, InstanceSize, 0);
             { insert VMT pointer into the new created memory area }
             { (in class methods self contains the VMT!)           }
             ppointer(instance)^:=pointer(self);
             if PVmt(self)^.vIntfTable  @emptyintf then
               InitInterfacePointers(self,instance);
             InitInstance:=TObject(Instance);
          end;

My question is do we need to call fillchar in InitInstance? Or Is there any way to avoid calling InitInstance?

Amir

P.S. I can send you the output of callgrind.

Reply by email

Sven Barth
to Amir
"Amir" wrote 38 lines
Hi, I have a code, developed in object pascal, with many classes. The project is working fine. Today, I used callgrind (valgrind --tool=calgrind) to see which function consumes the most execution time and I noticed that the most of time in my project is consumed by fillchar function. Incl. Self. Called Function 29.78 29.72 26M SYSTEM_FILLCHAR$formal$INT64$BYTE 19.07 19.07 123M SYSTEM_MOVE$formal$formal$INT64 3.63 3.63 70M SYSTEM_SYSGETMEM_FIXED$QWORD$$POINTER .... The main caller of FillChar function is system_TObject_$__NewInstance$$TObject which called Fillchar 26 Million times. NewInstance method in TObject calls InitInstance which is an inline function and defined as follows: class function TObject.InitInstance(instance : pointer) : tobject; {$ifdef SYSTEMINLINE} inline; {$ENDIF} begin { the size is saved at offset 0 } fillchar(instance^, InstanceSize, 0); { insert VMT pointer into the new created memory area } { (in class methods self contains the VMT!) } ppointer(instance)^:=pointer(self); if PVmt(self)^.vIntfTable @emptyintf then InitInterfacePointers(self,instance); InitInstance:=TObject(Instance); end; My question is do we need to call fillchar in InitInstance? Or Is there any way to avoid calling InitInstance?

Yes, FillChar is needed, because a class instance is guaranteed to have Bennett initialized with zero before the first constructor is called (this way the fields are basically initialized by 0, Nil or '' depending on the field type).

Regards,
Sven

Reply by email

Sven Barth
to Sven Barth
"Sven Barth" wrote
Yes, FillChar is needed, because a class instance is guaranteed to have

Bennett initialized with zero before the first constructor is called (this way the fields are basically initialized by 0, Nil or '' depending on the field type).

Sometimes I hate my phone's autocompletion: it should have been "been" instead of "Bennett"...

Regards,
Sven

Reply by email

Tommi Prami
to Sven Barth: Yes, FillChar is needed, because a class instance is guaranteed to have Bennett…
Show quoted text 38 lines
> Incl. Self. Called Function > 29.78 29.72 26M SYSTEM_FILLCHAR$formal$INT64$BYTE > 19.07 19.07 123M SYSTEM_MOVE$formal$formal$INT64 > 3.63 3.63 70M SYSTEM_SYSGETMEM_FIXED$QWORD$$POINTER > .... > > The main caller of FillChar function is > system_TObject_$__NewInstance$$TObject which called Fillchar 26 Million > times. > > NewInstance method in TObject calls InitInstance which is an inline > function and defined as follows: > > class function TObject.InitInstance(instance : pointer) : > tobject; {$ifdef SYSTEMINLINE} inline; {$ENDIF} > > begin > { the size is saved at offset 0 } > fillchar(instance^, InstanceSize, 0); > { insert VMT pointer into the new created memory area } > { (in class methods self contains the VMT!) } > ppointer(instance)^:=pointer(self); > if PVmt(self)^.vIntfTable @emptyintf then > InitInterfacePointers(self,instance); > InitInstance:=TObject(Instance); > end; > > My question is do we need to call fillchar in InitInstance? Or Is there > any way to avoid calling InitInstance? Yes, FillChar is needed, because a class instance is guaranteed to have Bennett initialized with zero before the first constructor is called (this way the fields are basically initialized by 0, Nil or '' depending on the field type).

But still...

Any improvement done in the Creating and Freeing an Objects would be welcome :)

We need the FillChar there, but if the object creation could be optimized even a little bit, it would benefit almost all the code. Some more than others... But still...

-Tee-

Reply by email

Marco van de Voort
to Tommi Prami

In our previous episode, Tommi Prami said:

Show quoted text 8 lines
> depending on the field type). > > But still... Any improvement done in the Creating and Freeing an Objects would be welcome :)

Usually pooling the most used objecttype(s) is already enough.

Reply by email

Sven Barth
to Tommi Prami: But still... Any improvement done in the Creating and Freeing an Objects would be welcome…
Tommi Prami wrote 47 lines
> > > > Incl. Self. Called Function > > 29.78 29.72 26M SYSTEM_FILLCHAR$formal$INT64$BYTE > > 19.07 19.07 123M SYSTEM_MOVE$formal$formal$INT64 > > 3.63 3.63 70M SYSTEM_SYSGETMEM_FIXED$QWORD$$POINTER > > .... > > > > The main caller of FillChar function is > > system_TObject_$__NewInstance$$TObject which called Fillchar 26 Million > > times. > > > > NewInstance method in TObject calls InitInstance which is an inline > > function and defined as follows: > > > > class function TObject.InitInstance(instance : pointer) : > > tobject; {$ifdef SYSTEMINLINE} inline; {$ENDIF} > > > > begin > > { the size is saved at offset 0 } > > fillchar(instance^, InstanceSize, 0); > > { insert VMT pointer into the new created memory area } > > { (in class methods self contains the VMT!) } > > ppointer(instance)^:=pointer(self); > > if PVmt(self)^.vIntfTable @emptyintf then > > InitInterfacePointers(self,instance); > > InitInstance:=TObject(Instance); > > end; > > > > My question is do we need to call fillchar in InitInstance? Or Is there > > any way to avoid calling InitInstance? > > Yes, FillChar is needed, because a class instance is guaranteed to > have Bennett initialized with zero before the first constructor is > called (this way the fields are basically initialized by 0, Nil or '' > depending on the field type). > > But still... Any improvement done in the Creating and Freeing an Objects would be welcome :) We need the FillChar there, but if the object creation could be optimized even a little bit, it would benefit almost all the code. Some more than others... But still...

In August 2010 Florian already said that maybe FillChar should be optimized:
http://lists.freepascal.org/lists/fpc-devel/2010-July/021146.html (at the bottom)

Regards,
Sven

Reply by email

Alexander Shishkin
to Amir: I have a code, developed in object pascal, with many classes. The project is working fine.

23.02.2012 11:47, Amir пишет:

Show quoted text 37 lines
Hi, I have a code, developed in object pascal, with many classes. The project is working fine. Today, I used callgrind (valgrind --tool=calgrind) to see which function consumes the most execution time and I noticed that the most of time in my project is consumed by fillchar function. Incl. Self. Called Function 29.78 29.72 26M SYSTEM_FILLCHAR$formal$INT64$BYTE 19.07 19.07 123M SYSTEM_MOVE$formal$formal$INT64 3.63 3.63 70M SYSTEM_SYSGETMEM_FIXED$QWORD$$POINTER .... The main caller of FillChar function is system_TObject_$__NewInstance$$TObject which called Fillchar 26 Million times. NewInstance method in TObject calls InitInstance which is an inline function and defined as follows: class function TObject.InitInstance(instance : pointer) : tobject; {$ifdef SYSTEMINLINE} inline; {$ENDIF} begin { the size is saved at offset 0 } fillchar(instance^, InstanceSize, 0); { insert VMT pointer into the new created memory area } { (in class methods self contains the VMT!) } ppointer(instance)^:=pointer(self); if PVmt(self)^.vIntfTable @emptyintf then InitInterfacePointers(self,instance); InitInstance:=TObject(Instance); end; My question is do we need to call fillchar in InitInstance? Or Is there any way to avoid calling InitInstance?

1) You can override TObject.NewInstance, w|o calling inherited. (Init instance is called from NewInstance) and do whatever you want

2) You can use objects instead of classes if Delphi class model overhead is significant for you.

AVS

Reply by email

Hans-Peter Diettrich
to Amir: I have a code, developed in object pascal, with many classes. The project is working fine.
Show quoted text 13 lines
Hi, I have a code, developed in object pascal, with many classes. The project is working fine. Today, I used callgrind (valgrind --tool=calgrind) to see which function consumes the most execution time and I noticed that the most of time in my project is consumed by fillchar function. Incl. Self. Called Function 29.78 29.72 26M SYSTEM_FILLCHAR$formal$INT64$BYTE 19.07 19.07 123M SYSTEM_MOVE$formal$formal$INT64 3.63 3.63 70M SYSTEM_SYSGETMEM_FIXED$QWORD$$POINTER ....

I suspect that the procedure initialization time is added to the first explicit statement. The initialization can include try-except/finally blocks, memory allocation (in constructors) or more.

Please create another procedure or method, that uses FillChar not as the first statement, and test again.

DoDi

Reply by email

Amir
to Hans-Peter Diettrich
Hans-Peter Diettrich wrote 22 lines
Amir schrieb: > Hi, > > I have a code, developed in object pascal, with many classes. The > project is working fine. > Today, I used callgrind (valgrind --tool=calgrind) to see which > function consumes the most execution time and I noticed that the most of > time in my project is consumed by fillchar function. > > Incl. Self. Called Function > 29.78 29.72 26M SYSTEM_FILLCHAR$formal$INT64$BYTE > 19.07 19.07 123M SYSTEM_MOVE$formal$formal$INT64 > 3.63 3.63 70M SYSTEM_SYSGETMEM_FIXED$QWORD$$POINTER > .... I suspect that the procedure initialization time is added to the first explicit statement. The initialization can include try-except/finally blocks, memory allocation (in constructors) or more. Please create another procedure or method, that uses FillChar not as the first statement, and test again.

I am not sure what you are asking. The callgrind outputs the call tree and other information....

Amir

Reply by email

Amir
to Alexander Shishkin: 23.02.2012 11:47, Amir пишет: 1) You can override TObject.NewInstance, w|o calling…
Alexander Shishkin wrote
1) You can override TObject.NewInstance, w|o calling inherited. (Init instance is called from NewInstance) and do whatever you want

What does each of the follwoing lines do? It looks like they are setting the function table for the object. So, I need them in my new NewInstance Implementation, don't I?

ppointer(instance)^:=pointer(self);
if PVmt(self)^.vIntfTable  @emptyintf then
   InitInterfacePointers(self,instance);
InitInstance:=TObject(Instance);

Amir

Reply by email

Sergei Gorelkin
to Sven Barth: In August 2010 Florian already said that maybe FillChar should be optimized:…

23.02.2012 13:16, Sven Barth пишет:

In August 2010 Florian already said that maybe FillChar should be optimized: http://lists.freepascal.org/lists/fpc-devel/2010-July/021146.html (at the bottom)

In early 2011 I wrote an assembler implementation of FillChar for x86_64 (cannot be used for FreeBSD due to its toolchain problems), it executes approximately 4 times less instructions than generic version.

Regards,
Sergei

Reply by email

Marco van de Voort
to Sergei Gorelkin

In our previous episode, Sergei Gorelkin said:

Show quoted text 6 lines
> In August 2010 Florian already said that maybe FillChar should be optimized: > http://lists.freepascal.org/lists/fpc-devel/2010-July/021146.html (at the bottom) > In early 2011 I wrote an assembler implementation of FillChar for x86_64 (cannot be used for FreeBSD due to its toolchain problems), it executes approximately 4 times less instructions than generic version.

Meanwhile 2.6.1 and 2.7.1 should use internal assembler now. Is there something I can test?

Reply by email

Sergei Gorelkin
to Marco van de Voort

23.02.2012 22:57, Marco van de Voort пишет:

Show quoted text 10 lines
In our previous episode, Sergei Gorelkin said: >> In August 2010 Florian already said that maybe FillChar should be optimized: >> http://lists.freepascal.org/lists/fpc-devel/2010-July/021146.html (at the bottom) >> > In early 2011 I wrote an assembler implementation of FillChar for x86_64 (cannot be used for FreeBSD > due to its toolchain problems), it executes approximately 4 times less instructions than generic > version. Meanwhile 2.6.1 and 2.7.1 should use internal assembler now. Is there something I can test?

There is {$ifndef freebsd} in rtl/x86_64/x86_64.inc line 63. You can try to disable that ifdef and see what happens while recompiling the RTL.

Sergei

Reply by email

Hans-Peter Diettrich
to Amir: I am not sure what you are asking. The callgrind outputs the call tree and other…
Show quoted text 15 lines
>> Incl. Self. Called Function >> 29.78 29.72 26M SYSTEM_FILLCHAR$formal$INT64$BYTE >> 19.07 19.07 123M SYSTEM_MOVE$formal$formal$INT64 >> 3.63 3.63 70M SYSTEM_SYSGETMEM_FIXED$QWORD$$POINTER >> .... > > I suspect that the procedure initialization time is added to the first > explicit statement. The initialization can include try-except/finally > blocks, memory allocation (in constructors) or more. > > Please create another procedure or method, that uses FillChar not as the > first statement, and test again. > I am not sure what you are asking. The callgrind outputs the call tree and other information....

It's not the first time that somebody complains about slow FillChar. I simply doubt that it really as slow as reported. In above table I'd expect that SysGetMem should be slower than FillChar.

DoDi

Reply by email

Amir
to Hans-Peter Diettrich
Hans-Peter Diettrich wrote 24 lines
Amir schrieb: >>> Incl. Self. Called Function >>> 29.78 29.72 26M SYSTEM_FILLCHAR$formal$INT64$BYTE >>> 19.07 19.07 123M SYSTEM_MOVE$formal$formal$INT64 >>> 3.63 3.63 70M SYSTEM_SYSGETMEM_FIXED$QWORD$$POINTER >>> .... >> >> I suspect that the procedure initialization time is added to the first >> explicit statement. The initialization can include try-except/finally >> blocks, memory allocation (in constructors) or more. >> >> Please create another procedure or method, that uses FillChar not as the >> first statement, and test again. >> > I am not sure what you are asking. The callgrind outputs the call tree > and other information.... It's not the first time that somebody complains about slow FillChar. I simply doubt that it really as slow as reported. In above table I'd expect that SysGetMem should be slower than FillChar. DoDi

The performance of FillChar is not my problem, The main issue in my case is that my class has a big array defined inside it. I developed my code such that this array does not need to be initialized with zero initially (I have not assumed anything about the content of this array). But the InitInstance function, initialize the array with zero.

I can see a simple solution to resolve my problem. I can change the array, in my class, to a dynamic array or pointer or ... But what if I had, let's say, many variables inside my class? Or a big data structure?

Amir

Reply by email

waldo kitty
to Sven Barth: Bennett initialized with zero before the first constructor is called (this way the fields…
Sven Barth wrote 8 lines
Am 23.02.2012 09:27 schrieb "Sven Barth" < >: > Yes, FillChar is needed, because a class instance is guaranteed to have Bennett initialized with zero before the first constructor is called (this way the fields are basically initialized by 0, Nil or '' depending on the field type). Sometimes I hate my phone's autocompletion: it should have been "been" instead of "Bennett"...

is there a DYAC (Damn You AutoCorrect) for coders? ;)

Reply by email

Hans-Peter Diettrich
to Amir: The performance of FillChar is not my problem, The main issue in my case is that my class…
Show quoted text 10 lines
The performance of FillChar is not my problem, The main issue in my case is that my class has a big array defined inside it. I developed my code such that this array does not need to be initialized with zero initially (I have not assumed anything about the content of this array). But the InitInstance function, initialize the array with zero. I can see a simple solution to resolve my problem. I can change the array, in my class, to a dynamic array or pointer or ... But what if I had, let's say, many variables inside my class? Or a big data structure?

The initialization of every class instance is a major improvement over e.g. C++ classes. I'd say that you should accept it, instead of searching for workarounds, with hard to predict effects in the remaining code.

While it may be possible to move elements, which do not deserve an initialization, out of the instance, this solution adds time to *every* later usage, and may require methods (calls!) to address these values as properties.

Another solution could move all uninitialized members to the end of the instance, and to initialize only the first part of the instance. But such modified behaviour will also affect all derived classes, whose added members then either stay uninitialized (what's unexpected), or will revert your optimization of the base class.

DoDi

Reply by email

Alexander Shishkin
to Amir: What does each of the follwoing lines do? It looks like they are setting the function…

23.02.2012 22:25, Amir пишет:

Show quoted text 11 lines
On 02/23/2012 01:20 AM, Alexander Shishkin wrote: > 1) You can override TObject.NewInstance, w|o calling inherited. (Init > instance is called from NewInstance) and do whatever you want > What does each of the follwoing lines do? It looks like they are setting the function table for the object. So, I need them in my new NewInstance Implementation, don't I? ppointer(instance)^:=pointer(self);

required

if PVmt(self)^.vIntfTable @emptyintf then InitInterfacePointers(self,instance);

required for interfaces support

InitInstance:=TObject(Instance);

required

Show quoted text 7 lines
Amir _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply by email

Amir
to Hans-Peter Diettrich: The initialization of every class instance is a major improvement over e.g. C++ classes.…
Hans-Peter Diettrich wrote 17 lines
Amir schrieb: > The performance of FillChar is not my problem, The main issue in my > case is that my class has a big array defined inside it. I developed > my code such that this array does not need to be initialized with zero > initially (I have not assumed anything about the content of this array). > But the InitInstance function, initialize the array with zero. > > I can see a simple solution to resolve my problem. I can change the > array, in my class, to a dynamic array or pointer or ... > But what if I had, let's say, many variables inside my class? Or a big > data structure? The initialization of every class instance is a major improvement over e.g. C++ classes. I'd say that you should accept it, instead of searching for workarounds, with hard to predict effects in the remaining code.

I understand that, but I usually forget about it and initialize all my variables to 0 in constructor ;).

Amir

Reply by email

waldo kitty
to Amir
Amir wrote 8 lines
On 02/23/2012 02:20 PM, Hans-Peter Diettrich wrote: > The initialization of every class instance is a major improvement over > e.g. C++ classes. I'd say that you should accept it, instead of > searching for workarounds, with hard to predict effects in the remaining > code. I understand that, but I usually forget about it and initialize all my variables to 0 in constructor ;).

yet another way that pascal helps you instead of handing you the gun, pointing it at yer foots and assisting you in pulling the trigger ;) ;) :P :lol:

Reply by email

Alexander Shishkin
to Amir: The performance of FillChar is not my problem, The main issue in my case is that my class…

24.02.2012 0:43, Amir пишет:

Show quoted text 6 lines
The performance of FillChar is not my problem, The main issue in my case is that my class has a big array defined inside it. I developed my code such that this array does not need to be initialized with zero initially (I have not assumed anything about the content of this array). But the InitInstance function, initialize the array with zero.

You also may refactor your class and define array as a pointer to array (dont use dynamic array - they are zero initialized) and manage memory manually ( [Get|Realloc|Free]Mem ).

Reply by email

Hans-Peter Diettrich
to Alexander Shishkin
You also may refactor your class and define array as a pointer to array (dont use dynamic array - they are zero initialized) and manage memory manually ( [Get|Realloc|Free]Mem ).

IMO dynamic arrays are not initialized, at least not in Delphi. Only the pointer is initialized (to Nil).

DoDi

Reply by email

Vincent Snijders
to Hans-Peter Diettrich

Op 24 februari 2012 13:40 heeft Hans-Peter Diettrich het volgende geschreven:

IMO dynamic arrays are not initialized, at least not in Delphi. Only the pointer is initialized (to Nil).

IMO one should not use IMO when claiming easily verifiable facts, it should be used when writing down an opinion.

Vincent

Reply by email

Hans-Peter Diettrich
to Vincent Snijders
Show quoted text 6 lines
Op 24 februari 2012 13:40 heeft Hans-Peter Diettrich het volgende geschreven: > IMO dynamic arrays are not initialized, at least not in Delphi. Only the > pointer is initialized (to Nil). IMO one should not use IMO when claiming easily verifiable facts,

What's *easily* verifiable without knowing about the compiler version?

it should be used when writing down an opinion.

What's wrong with leaving the check to others? As an exercise...

DoDi

Reply by email

Vincent Snijders
to Hans-Peter Diettrich

Op 24 februari 2012 15:14 heeft Hans-Peter Diettrich het volgende geschreven:

Show quoted text 5 lines
> it > should be used when writing down an opinion. What's wrong with leaving the check to others? As an exercise...

Then you should not write "in my opinion", but "I think" or "if I remember correctly". But that is just my opinion ...

Vincent

Reply by email