division of objects into classes and structures is bad
Weed
resume755 at mail.ru
Sun Dec 28 14:05:15 PST 2008
(Forgive, again I will mention the patient for me a theme)
About why I consider that division of objects into classes and
structures is bad.
If the object is a class that there will be additional overhead charge
at calculation of expressions with this class because it is impossible
to allocate a class instance on a stack and transfer it as result to
next function.
Example with usage of the overloaded function opAdd:
//===================
import std.stdio;
class C {
int i;
C opAdd( C src ) {
auto ret = new C;
ret.i = i + src.i;
return ret;
}
}
void main() {
auto c1 = new C;
c1.i = 1;
auto c2 = c1 + c1 + c1;
writeln( c2.i );
}
//===================
auto c2 = c1 + c1 + c1; // calculated as:
auto c2 = c1.opAdd( c1 ).opAdd( c1 );
The temporary result of opAdd in this expression is located in
heap (i.e. with overhead), and only then the reference to it is
transferred in the second function opAdd:
assume CS:_D4test1C5opAddMFC4test1CZC4test1C
L0: push EAX
push EBX
push offset FLAT:_D4test1C7__ClassZ
call near ptr __d_newclass
mov EBX,EAX
mov EAX,8[ESP]
mov ECX,8[EAX]
mov EDX,010h[ESP]
add ECX,8[EDX]
mov 8[EBX],ECX
add ESP,4
mov EAX,EBX
pop EBX
pop ECX
ret 4
_D4test1C5opAddMFC4test1CZC4test1C ends
__Dmain comdat
assume CS:__Dmain
L0: push EBX
push offset FLAT:_D4test1C7__ClassZ
call near ptr __d_newclass
mov EBX,EAX
mov dword ptr 8[EBX],1
add ESP,4
push EBX
push EBX
mov EAX,EBX
mov ECX,[EBX]
call dword ptr 014h[ECX]
mov EDX,[EAX]
call dword ptr 014h[EDX]
mov EAX,8[EAX]
pop EBX
ret
__Dmain ends
opAdd is called two times (call dword ptr 014h[ECX] and call dword ptr
014h[EDX]). Objects created in heap (call near ptr __d_newclass). There
is created two objects and the first of them is temporary.
Now we will consider the same example with usage of the object of
structure which allows allocation on a stack:
//===================
struct C {
int i;
int[100] j; // to prevent returning this struct in registers
C opAdd( C src ) {
C ret;
ret.i = i + src.i;
return ret;
}
}
int main() {
C c1;
// initialise i by "random" value to prevent compile-time calculation
c1.i = cast(int)&c1;
auto c2 = c1 + c1 + c1;
return c2.i;
}
//===================
In this case the compiler easily detects that returned value is
allocated in a stack and to transfer it in the following function of
anything it is not necessary to do - enough to leave it in a stack
(linux objdump output):
struct C {
int i;
int[100] j; // to prevent returning this struct in register
C opAdd( C src ) {
...
C ret;
8049075: b9 65 00 00 00 mov $0x65,%ecx
804907a: 31 c0 xor %eax,%eax
804907c: 8b 7d 08 mov 0x8(%ebp),%edi
804907f: f3 ab rep stos %eax,%es:(%edi)
ret.i = i + src.i;
8049081: 8b 8d 5c fe ff ff mov -0x1a4(%ebp),%ecx
8049087: 8b 11 mov (%ecx),%edx
8049089: 03 55 0c add 0xc(%ebp),%edx
804908c: 8b 5d 08 mov 0x8(%ebp),%ebx
804908f: 89 13 mov %edx,(%ebx)
8049091: 8b 45 08 mov 0x8(%ebp),%eax
8049094: 5f pop %edi
8049095: 5b pop %ebx
8049096: c9 leave
8049097: c2 98 01 ret $0x198
804909a: 90 nop
804909b: 90 nop
}
}
int main() {
...
auto c2 = c1 + c1 + c1;
80490c3: 8d 9d bc fc ff ff lea -0x344(%ebp),%ebx
80490c9: b9 65 00 00 00 mov $0x65,%ecx
80490ce: ff 33 pushl (%ebx)
80490d0: 83 eb 04 sub $0x4,%ebx
80490d3: e2 f9 loop 80490ce <_Dmain+0x32>
80490d5: 8d 95 cc fc ff ff lea -0x334(%ebp),%edx
80490db: 52 push %edx
80490dc: 8d b5 bc fc ff ff lea -0x344(%ebp),%esi
80490e2: b1 65 mov $0x65,%cl
80490e4: ff 36 pushl (%esi)
80490e6: 83 ee 04 sub $0x4,%esi
80490e9: e2 f9 loop 80490e4 <_Dmain+0x48>
80490eb: 8d 85 6c fe ff ff lea -0x194(%ebp),%eax
80490f1: 50 push %eax
80490f2: 8d 85 2c fb ff ff lea -0x4d4(%ebp),%eax
80490f8: e8 67 ff ff ff *call 8049064*
80490fd: e8 62 ff ff ff *call 8049064*
return c2.i;
8049102: 8b 85 cc fc ff ff mov -0x334(%ebp),%eax
...
(in 80490f8 and 80490fd simply two calls successively)
If structures and classes were same that excellent optimization in any
case would turn out
More information about the Digitalmars-d
mailing list