Jump to page: 1 27  
Page
Thread overview
division of objects into classes and structures is bad
Dec 28, 2008
Weed
Dec 29, 2008
Denis Koroskin
Dec 29, 2008
Weed
Dec 29, 2008
Don
Dec 30, 2008
Weed
Dec 30, 2008
Weed
Dec 30, 2008
Don
Dec 30, 2008
Weed
Dec 30, 2008
Christopher Wright
Dec 30, 2008
Don
Dec 30, 2008
Don
Dec 30, 2008
Bill Baxter
Jan 01, 2009
Don
Dec 30, 2008
Weed
Dec 30, 2008
bearophile
Jan 02, 2009
Walter Bright
Dec 30, 2008
Weed
Dec 30, 2008
Weed
Jan 04, 2009
Weed
Jan 06, 2009
Weed
Jan 06, 2009
Christopher Wright
Jan 06, 2009
Weed
Jan 06, 2009
Bill Baxter
Jan 06, 2009
Weed
Jan 06, 2009
Weed
new principle of division between structures and classes
Jan 06, 2009
Weed
Jan 07, 2009
Weed
Jan 10, 2009
Weed
Jan 10, 2009
Denis Koroskin
Jan 10, 2009
Weed
Jan 10, 2009
Weed
Jan 10, 2009
Christopher Wright
Jan 10, 2009
Weed
Jan 10, 2009
Bill Baxter
Jan 10, 2009
Bill Baxter
Jan 10, 2009
Weed
Jan 11, 2009
Bill Baxter
Jan 11, 2009
Weed
Jan 11, 2009
Denis Koroskin
Jan 11, 2009
Weed
Jan 11, 2009
Weed
Jan 11, 2009
Brad Roberts
Jan 13, 2009
Weed
Jan 13, 2009
Bill Baxter
Jan 13, 2009
Denis Koroskin
Jan 13, 2009
Bill Baxter
Jan 13, 2009
John Reimer
Jan 13, 2009
Brad Roberts
Jan 13, 2009
Benji Smith
Jan 13, 2009
Benji Smith
Jan 13, 2009
Daniel Keep
Jan 15, 2009
Daniel de Kok
Jan 16, 2009
Weed
Jan 10, 2009
Bill Baxter
Jan 10, 2009
Weed
Jan 12, 2009
downs
December 28, 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
December 29, 2008
On Mon, 29 Dec 2008 01:05:15 +0300, Weed <resume755@mail.ru> wrote:

> (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

If that's your use case, then your should seriosly reconsider using struct instead of class for your objects. Alternatively, you can use += instead.
Other than that, this is not a convincing argument.

Reading many of your posts I came to a conclusion that you are shortsighted and too crazy about performance. What you care about is a premature optimization, which is a root of all the evil. You should ensure that your programm is complete and correct, first and *then* start doing profiling and optimizations.

Going back to the topic, dividing user types into two cathegories (structs and classes) is considered modern and right. Some languages lack structs support at all (e.g. Java), but structs are too useful for optimization and language interoperation to drop them in a systems programming language. Some lack classes and try doing everything with structs (C). D takes the best of both worlds.
December 29, 2008
Denis Koroskin пишет:

>>  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
> 
> If that's your use case, then your should seriosly reconsider using struct instead of class for your objects.

Classes always give such overhead if them to use in such quality. For example, classes basically cannot be used as any mathematical objects using overload of arithmetics. But also not only arithmetics, it it is simple as a good example.

> Alternatively, you can use +=
> instead.

Here yes, but if I add classes of different types? Then not to escape any more from creation of the temporary object in the heap.

> Other than that, this is not a convincing argument.
> 
> Reading many of your posts I came to a conclusion that you are shortsighted and too crazy about performance. What you care about is a premature optimization, which is a root of all the evil. You should ensure that your programm is complete and correct, first and *then* start doing profiling and optimizations.

The program is already ready. It entirely consists of the various mathematics. Approximately %30 times of performance are spent for similar superfluous work. On C++ the program will work on %30 faster (I hope :)) and on D I am will turn out to do nothing with it.


> 
> Going back to the topic, dividing user types into two cathegories (structs and classes) is considered modern and right.

I do not accept such argument:)

> Some languages
> lack structs support at all (e.g. Java), but structs are too useful for
> optimization and language interoperation to drop them in a systems
> programming language. Some lack classes and try doing everything with
> structs (C). D takes the best of both worlds.

Probably I have not understood something, but I do not suggest to refuse structures in general. I suggest to allow to create classes on a stack as it is made in C++. That is actually to make structures and classes same, than they and are, for example, in C++.

In the initial message I have shown that for perfomance important that the class could be transferred and on value. And it not artful premature optimisation - objects on value always so are transferred, all programmers know it and use when do not wish to allocate a place in a heap, that is usually always when the object will live in {}.

Besides, a class in a stack it is normal - keyword addition "scope" for classes too speaks about it.

Rigidly having divided classes and structures D deprives of the programmer of some possibilities which give it C++-like languages. I consider that such languages should give all possibilities which allows CPU but hiding concrete architecture, otherwise I would choose less difficult in use language.
December 29, 2008
Weed wrote:
> Denis Koroskin пишет:
> 
>>>  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
>> If that's your use case, then your should seriosly reconsider using
>> struct instead of class for your objects.
> 
> Classes always give such overhead if them to use in such quality. For
> example, classes basically cannot be used as any mathematical objects
> using overload of arithmetics. But also not only arithmetics, it it is
> simple as a good example.
> 
>> Alternatively, you can use +=
>> instead.
> 
> Here yes, but if I add classes of different types? Then not to escape
> any more from creation of the temporary object in the heap.
> 
>> Other than that, this is not a convincing argument.
>>
>> Reading many of your posts I came to a conclusion that you are
>> shortsighted and too crazy about performance. What you care about is a
>> premature optimization, which is a root of all the evil. You should
>> ensure that your programm is complete and correct, first and *then*
>> start doing profiling and optimizations.
> 
> The program is already ready. It entirely consists of the various
> mathematics. Approximately %30 times of performance are spent for
> similar superfluous work. On C++ the program will work on %30 faster (I
> hope :)) and on D I am will turn out to do nothing with it.
> 
> 
>> Going back to the topic, dividing user types into two cathegories
>> (structs and classes) is considered modern and right.
> 
> I do not accept such argument:)
> 
>> Some languages
>> lack structs support at all (e.g. Java), but structs are too useful for
>> optimization and language interoperation to drop them in a systems
>> programming language. Some lack classes and try doing everything with
>> structs (C). D takes the best of both worlds.
> 
> Probably I have not understood something, but I do not suggest to refuse
> structures in general. I suggest to allow to create classes on a stack
> as it is made in C++. That is actually to make structures and classes
> same, than they and are, for example, in C++.
> 
> In the initial message I have shown that for perfomance important that
> the class could be transferred and on value. And it not artful premature
> optimisation - objects on value always so are transferred, all
> programmers know it and use when do not wish to allocate a place in a
> heap, that is usually always when the object will live in {}.
> 
> Besides, a class in a stack it is normal - keyword addition "scope" for
> classes too speaks about it.
> 
> Rigidly having divided classes and structures D deprives of the
> programmer of some possibilities which give it C++-like languages. I
> consider that such languages should give all possibilities which allows
> CPU but hiding concrete architecture, otherwise I would choose less
> difficult in use language.

Use classes if you want polymorphism. Otherwise, use structs. It's a clear distinction, which is not at all arbitrary -- there are significant implications for the generated code.
December 30, 2008
Don пишет:
> Weed wrote:
>> Denis Koroskin пишет:
>>
>>>>  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
>>> If that's your use case, then your should seriosly reconsider using struct instead of class for your objects.
>>
>> Classes always give such overhead if them to use in such quality. For example, classes basically cannot be used as any mathematical objects using overload of arithmetics. But also not only arithmetics, it it is simple as a good example.
>>
>>> Alternatively, you can use +=
>>> instead.
>>
>> Here yes, but if I add classes of different types? Then not to escape any more from creation of the temporary object in the heap.
>>
>>> Other than that, this is not a convincing argument.
>>>
>>> Reading many of your posts I came to a conclusion that you are shortsighted and too crazy about performance. What you care about is a premature optimization, which is a root of all the evil. You should ensure that your programm is complete and correct, first and *then* start doing profiling and optimizations.
>>
>> The program is already ready. It entirely consists of the various mathematics. Approximately %30 times of performance are spent for similar superfluous work. On C++ the program will work on %30 faster (I hope :)) and on D I am will turn out to do nothing with it.
>>
>>
>>> Going back to the topic, dividing user types into two cathegories (structs and classes) is considered modern and right.
>>
>> I do not accept such argument:)
>>
>>> Some languages
>>> lack structs support at all (e.g. Java), but structs are too useful for
>>> optimization and language interoperation to drop them in a systems
>>> programming language. Some lack classes and try doing everything with
>>> structs (C). D takes the best of both worlds.
>>
>> Probably I have not understood something, but I do not suggest to refuse structures in general. I suggest to allow to create classes on a stack as it is made in C++. That is actually to make structures and classes same, than they and are, for example, in C++.
>>
>> In the initial message I have shown that for perfomance important that the class could be transferred and on value. And it not artful premature optimisation - objects on value always so are transferred, all programmers know it and use when do not wish to allocate a place in a heap, that is usually always when the object will live in {}.
>>
>> Besides, a class in a stack it is normal - keyword addition "scope" for classes too speaks about it.
>>
>> Rigidly having divided classes and structures D deprives of the programmer of some possibilities which give it C++-like languages. I consider that such languages should give all possibilities which allows CPU but hiding concrete architecture, otherwise I would choose less difficult in use language.
> 
> Use classes if you want polymorphism. Otherwise, use structs. It's a clear distinction, which is not at all arbitrary -- there are significant implications for the generated code.

And if polymorphism is necessary and such calculations are necessary as I have above described? To emulate polymorphism with the mixins? Or simply to reconcile to such obvious losses?

I about that that division into classes and structures in language D automatically reduce perfomance of programs. Unlike languages where this division is not present (C++).
December 30, 2008
Weed пишет:
> Don пишет:
>> Weed wrote:
>>> Denis Koroskin пишет:
>>>
>>>>>  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
>>>> If that's your use case, then your should seriosly reconsider using struct instead of class for your objects.
>>> Classes always give such overhead if them to use in such quality. For example, classes basically cannot be used as any mathematical objects using overload of arithmetics. But also not only arithmetics, it it is simple as a good example.
>>>
>>>> Alternatively, you can use +=
>>>> instead.
>>> Here yes, but if I add classes of different types? Then not to escape any more from creation of the temporary object in the heap.
>>>
>>>> Other than that, this is not a convincing argument.
>>>>
>>>> Reading many of your posts I came to a conclusion that you are shortsighted and too crazy about performance. What you care about is a premature optimization, which is a root of all the evil. You should ensure that your programm is complete and correct, first and *then* start doing profiling and optimizations.
>>> The program is already ready. It entirely consists of the various mathematics. Approximately %30 times of performance are spent for similar superfluous work. On C++ the program will work on %30 faster (I hope :)) and on D I am will turn out to do nothing with it.
>>>
>>>
>>>> Going back to the topic, dividing user types into two cathegories (structs and classes) is considered modern and right.
>>> I do not accept such argument:)
>>>
>>>> Some languages
>>>> lack structs support at all (e.g. Java), but structs are too useful for
>>>> optimization and language interoperation to drop them in a systems
>>>> programming language. Some lack classes and try doing everything with
>>>> structs (C). D takes the best of both worlds.
>>> Probably I have not understood something, but I do not suggest to refuse structures in general. I suggest to allow to create classes on a stack as it is made in C++. That is actually to make structures and classes same, than they and are, for example, in C++.
>>>
>>> In the initial message I have shown that for perfomance important that the class could be transferred and on value. And it not artful premature optimisation - objects on value always so are transferred, all programmers know it and use when do not wish to allocate a place in a heap, that is usually always when the object will live in {}.
>>>
>>> Besides, a class in a stack it is normal - keyword addition "scope" for classes too speaks about it.
>>>
>>> Rigidly having divided classes and structures D deprives of the programmer of some possibilities which give it C++-like languages. I consider that such languages should give all possibilities which allows CPU but hiding concrete architecture, otherwise I would choose less difficult in use language.
>> Use classes if you want polymorphism. Otherwise, use structs. It's a clear distinction, which is not at all arbitrary -- there are significant implications for the generated code.
> 
> And if polymorphism is necessary and such calculations are necessary as I have above described? To emulate polymorphism with the mixins? Or simply to reconcile to such obvious losses?
> 
> I about that that division into classes and structures in language D automatically reduce perfomance of programs. Unlike languages where this division is not present (C++).

To solve a problem it is possible having allowed to transfer classes on value (checking during compilation that has not occurred slicing)
December 30, 2008
Weed wrote:
> Don пишет:
>> Weed wrote:
>>> Denis Koroskin пишет:
>>>
>>>>>  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
>>>> If that's your use case, then your should seriosly reconsider using
>>>> struct instead of class for your objects.
>>> Classes always give such overhead if them to use in such quality. For
>>> example, classes basically cannot be used as any mathematical objects
>>> using overload of arithmetics. But also not only arithmetics, it it is
>>> simple as a good example.
>>>
>>>> Alternatively, you can use +=
>>>> instead.
>>> Here yes, but if I add classes of different types? Then not to escape
>>> any more from creation of the temporary object in the heap.
>>>
>>>> Other than that, this is not a convincing argument.
>>>>
>>>> Reading many of your posts I came to a conclusion that you are
>>>> shortsighted and too crazy about performance. What you care about is a
>>>> premature optimization, which is a root of all the evil. You should
>>>> ensure that your programm is complete and correct, first and *then*
>>>> start doing profiling and optimizations.
>>> The program is already ready. It entirely consists of the various
>>> mathematics. Approximately %30 times of performance are spent for
>>> similar superfluous work. On C++ the program will work on %30 faster (I
>>> hope :)) and on D I am will turn out to do nothing with it.
>>>
>>>
>>>> Going back to the topic, dividing user types into two cathegories
>>>> (structs and classes) is considered modern and right.
>>> I do not accept such argument:)
>>>
>>>> Some languages
>>>> lack structs support at all (e.g. Java), but structs are too useful for
>>>> optimization and language interoperation to drop them in a systems
>>>> programming language. Some lack classes and try doing everything with
>>>> structs (C). D takes the best of both worlds.
>>> Probably I have not understood something, but I do not suggest to refuse
>>> structures in general. I suggest to allow to create classes on a stack
>>> as it is made in C++. That is actually to make structures and classes
>>> same, than they and are, for example, in C++.
>>>
>>> In the initial message I have shown that for perfomance important that
>>> the class could be transferred and on value. And it not artful premature
>>> optimisation - objects on value always so are transferred, all
>>> programmers know it and use when do not wish to allocate a place in a
>>> heap, that is usually always when the object will live in {}.
>>>
>>> Besides, a class in a stack it is normal - keyword addition "scope" for
>>> classes too speaks about it.
>>>
>>> Rigidly having divided classes and structures D deprives of the
>>> programmer of some possibilities which give it C++-like languages. I
>>> consider that such languages should give all possibilities which allows
>>> CPU but hiding concrete architecture, otherwise I would choose less
>>> difficult in use language.
>> Use classes if you want polymorphism. Otherwise, use structs. It's a
>> clear distinction, which is not at all arbitrary -- there are
>> significant implications for the generated code.
> 
> And if polymorphism is necessary and such calculations are necessary as
> I have above described? To emulate polymorphism with the mixins? Or
> simply to reconcile to such obvious losses?
> 
> I about that that division into classes and structures in language D
> automatically reduce perfomance of programs. Unlike languages where this
> division is not present (C++).

I agree with you that there's a problem, but I think you're wrong about the solution. C++ suffers from severe problems with creation of temporaries in expressions. The problem occurs whenever you have heap allocations inside an object which does operator overloading. Sure, in the simple case you mentioned, using a struct works because the size of the data is small. But it general, it's not possible to avoid the heap allocation, and so in C++ you'll still have a problem.

The creation of temporaries during expressions is something I'm currently working on solving. The case you mentioned is addressed by a proposal I made long ago:
http://d.puremagic.com/issues/show_bug.cgi?id=124

 c2 = c1 + c1 + c1;

would be transformed into
t1 = c1 + c1;
t1.opAddAssign(c1);
c2 = t1;
which gets rid of the temporary heap allocation.
I don't think you could ever get rid of the heap allocation for c2 since
(a) c2 might be null, initially; and (b) c2 might be pointing to the same place as c1.

Nonetheless, I'd like to do better than this.
Consider:
C c1, c2, c3;
c3 = c1*5 + c2/6;

The optimal solution depends on whether in-place operations are possible or not. Interestingly, X+=Y is more efficient than X=X+Y only if in-place operations are possible; there's no point in defining it if in-place is impossible.

Case 1: in-place operations are possible, += exists. All operators include destination.
Convert to t1 = c2/6; c3 = c1*5;  c3+=t1;
---
LocalHeap h;
t1 = h.new(C);  // create on LocalHeap h
t1.operatorWithDestination("/")(c2, 6);
C t2 = new C;  // create on heap
t2.operatorWithDestination!("*")(c1, 5);
c3 = t2.operatorAssign!("+")(t1); // in-place += operation on heap
h.releaseAll;
---
Case 2: in-place operations are impossible, += doesn't exist.
---
LocalHeap h;
t1 = c1.operatorTemporary!("*")(5, h); // create on local heap
t2 = c2.operatorTemporary!("/")(6, h); // create on local heap
c3 = t1.operator!("+")(t2); // create on heap
h.releaseAll;
---
It's far too complicated at present to be workable, but that's the basic idea.
December 30, 2008
Don пишет:
> Weed wrote:
>> Don пишет:
>>> Weed wrote:
>>>> Denis Koroskin пишет:
>>>>
>>>>>>  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
>>>>> If that's your use case, then your should seriosly reconsider using struct instead of class for your objects.
>>>> Classes always give such overhead if them to use in such quality. For example, classes basically cannot be used as any mathematical objects using overload of arithmetics. But also not only arithmetics, it it is simple as a good example.
>>>>
>>>>> Alternatively, you can use +=
>>>>> instead.
>>>> Here yes, but if I add classes of different types? Then not to escape any more from creation of the temporary object in the heap.
>>>>
>>>>> Other than that, this is not a convincing argument.
>>>>>
>>>>> Reading many of your posts I came to a conclusion that you are shortsighted and too crazy about performance. What you care about is a premature optimization, which is a root of all the evil. You should ensure that your programm is complete and correct, first and *then* start doing profiling and optimizations.
>>>> The program is already ready. It entirely consists of the various mathematics. Approximately %30 times of performance are spent for similar superfluous work. On C++ the program will work on %30 faster (I hope :)) and on D I am will turn out to do nothing with it.
>>>>
>>>>
>>>>> Going back to the topic, dividing user types into two cathegories (structs and classes) is considered modern and right.
>>>> I do not accept such argument:)
>>>>
>>>>> Some languages
>>>>> lack structs support at all (e.g. Java), but structs are too useful
>>>>> for
>>>>> optimization and language interoperation to drop them in a systems
>>>>> programming language. Some lack classes and try doing everything with
>>>>> structs (C). D takes the best of both worlds.
>>>> Probably I have not understood something, but I do not suggest to
>>>> refuse
>>>> structures in general. I suggest to allow to create classes on a stack
>>>> as it is made in C++. That is actually to make structures and classes
>>>> same, than they and are, for example, in C++.
>>>>
>>>> In the initial message I have shown that for perfomance important that
>>>> the class could be transferred and on value. And it not artful
>>>> premature
>>>> optimisation - objects on value always so are transferred, all
>>>> programmers know it and use when do not wish to allocate a place in a
>>>> heap, that is usually always when the object will live in {}.
>>>>
>>>> Besides, a class in a stack it is normal - keyword addition "scope" for classes too speaks about it.
>>>>
>>>> Rigidly having divided classes and structures D deprives of the programmer of some possibilities which give it C++-like languages. I consider that such languages should give all possibilities which allows CPU but hiding concrete architecture, otherwise I would choose less difficult in use language.
>>> Use classes if you want polymorphism. Otherwise, use structs. It's a clear distinction, which is not at all arbitrary -- there are significant implications for the generated code.
>>
>> And if polymorphism is necessary and such calculations are necessary as I have above described? To emulate polymorphism with the mixins? Or simply to reconcile to such obvious losses?
>>
>> I about that that division into classes and structures in language D automatically reduce perfomance of programs. Unlike languages where this division is not present (C++).
> 
> I agree with you that there's a problem, but I think you're wrong about the solution. C++ suffers from severe problems with creation of temporaries in expressions. The problem occurs whenever you have heap allocations inside an object which does operator overloading.

Nothing can be done with it in any case.
If the class uses in itself dynamic allocation through "new" that this
memory will be allocated in a heap. But time in a class is used such way
of allocation that for this purpose there are reasons.

> Sure, in
> the simple case you mentioned, using a struct works because the size of
> the data is small.

No. Structure used only because it is the type transferred on value in D.

> But it general, it's not possible to avoid the heap allocation, and so in C++ you'll still have a problem.
> 
> The creation of temporaries during expressions is something I'm currently working on solving. The case you mentioned is addressed by a proposal I made long ago: http://d.puremagic.com/issues/show_bug.cgi?id=124
> 
>  c2 = c1 + c1 + c1;
> 
> would be transformed into
> t1 = c1 + c1;
> t1.opAddAssign(c1);
> c2 = t1;
> which gets rid of the temporary heap allocation.
> I don't think you could ever get rid of the heap allocation for c2 since
> (a) c2 might be null, initially;

In this case opAdd returns the result object to which the name c2 will be assigned.

> and (b) c2 might be pointing to the
> same place as c1.

There will be the same as (a).

If it is necessary to equate to the existing object (for example that it did not change the position in memory) it is possible to overload that the operator [] = and to make so: c2 [] = c1 + c1 + c1;

> 
> Nonetheless, I'd like to do better than this.
> Consider:
> C c1, c2, c3;
> c3 = c1*5 + c2/6;
> 
> The optimal solution depends on whether in-place operations are possible or not. Interestingly, X+=Y is more efficient than X=X+Y only if in-place operations are possible; there's no point in defining it if in-place is impossible.

There can be I something do not understand, but the decision should be more the general than optimization of an overload of operators. Eventually, the overload of operators is simply syntactic sugar.  I used them simply as an example.

It is possible to think up other example where there is no overload:

space_ship_1.calculatePathTo("Moon").getCheckpoint(3).getCoords;

In this example we create a temporary class "path", create temporary class "checkpoint" and we take coords of checkpoint of this path. It is not expedient to us to store all this path and checkpoint because it is vary.

> 
> Case 1: in-place operations are possible, += exists. All operators
> include destination.
> Convert to t1 = c2/6; c3 = c1*5;  c3+=t1;
> ---
> LocalHeap h;
> t1 = h.new(C);  // create on LocalHeap h
> t1.operatorWithDestination("/")(c2, 6);
> C t2 = new C;  // create on heap
> t2.operatorWithDestination!("*")(c1, 5);
> c3 = t2.operatorAssign!("+")(t1); // in-place += operation on heap
> h.releaseAll;
> ---
> Case 2: in-place operations are impossible, += doesn't exist.
> ---
> LocalHeap h;
> t1 = c1.operatorTemporary!("*")(5, h); // create on local heap
> t2 = c2.operatorTemporary!("/")(6, h); // create on local heap
> c3 = t1.operator!("+")(t2); // create on heap
> h.releaseAll;
> ---
> It's far too complicated at present to be workable, but that's the basic idea.

Whether tells word introduction "scope" and such attempts of optimization about that that the design of objects in D is wrong? ]:)
December 30, 2008
Don wrote:
> The creation of temporaries during expressions is something I'm currently working on solving. The case you mentioned is addressed by a proposal I made long ago:

The easiest way is to add an intermediate struct. This takes a fair bit of manual effort, though, and prevents you from using auto.

Essentially, you need a struct MyClassAddition that just records operands. Then give it an implicit cast to MyClass that does the work. This is an ugly solution because you need to duplicate the operator overloads on MyClassXXX as well as MyClass.

I believe I got this solution from an article by Andrei. It should work pretty well for classes that define few overloads.
December 30, 2008
Christopher Wright wrote:
> Don wrote:
>> The creation of temporaries during expressions is something I'm currently working on solving. The case you mentioned is addressed by a proposal I made long ago:
> 
> The easiest way is to add an intermediate struct. This takes a fair bit of manual effort, though, and prevents you from using auto.

That particular case is the easiest possible one. The case x = y - x, for example, is much more difficult to recognize.

>
> Essentially, you need a struct MyClassAddition that just records operands. Then give it an implicit cast to MyClass that does the work. This is an ugly solution because you need to duplicate the operator overloads on MyClassXXX as well as MyClass.
> 
> I believe I got this solution from an article by Andrei. It should work pretty well for classes that define few overloads.

I'm talking about a language solution. I want to solve this for the general case.
« First   ‹ Prev
1 2 3 4 5 6 7