www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - division of objects into classes and structures is bad

reply Weed <resume755 mail.ru> writes:
(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
Dec 28 2008
next sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
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.
Dec 28 2008
parent reply Weed <resume755 mail.ru> writes:
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.
Dec 29 2008
next sibling parent reply Don <nospam nospam.com> writes:
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

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.
Dec 29 2008
next sibling parent reply Weed <resume755 mail.ru> writes:
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

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++).
Dec 30 2008
next sibling parent Weed <resume755 mail.ru> writes:
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

struct instead of class for your objects.

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.

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.

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.

 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.

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.

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)
Dec 30 2008
prev sibling next sibling parent reply Don <nospam nospam.com> writes:
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

struct instead of class for your objects.

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.

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.

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.

 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.

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.

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.
Dec 30 2008
next sibling parent Weed <resume755 mail.ru> writes:
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

struct instead of class for your objects.

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.

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.

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.

 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.

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.

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? ]:)
Dec 30 2008
prev sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
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.
Dec 30 2008
parent reply Don <nospam nospam.com> writes:
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.
Dec 30 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Don wrote:
 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.

Don't forget that the case you are solving is not general enough because you are focusing on eliminating temporaries, which is only part of the story. I suggest you make place in your thoughts for loop fusion. Andrei
Dec 30 2008
parent reply Don <nospam nospam.com> writes:
Andrei Alexandrescu wrote:
 Don wrote:
 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.

Don't forget that the case you are solving is not general enough because you are focusing on eliminating temporaries, which is only part of the story. I suggest you make place in your thoughts for loop fusion.

Yes. That's the classic problem for matrices, but I'm still trying to work out if it is really a general problem. I was hoping to see some new use cases, but none so far.
Dec 30 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Don wrote:
 Andrei Alexandrescu wrote:
 Don wrote:
 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.

Don't forget that the case you are solving is not general enough because you are focusing on eliminating temporaries, which is only part of the story. I suggest you make place in your thoughts for loop fusion.

Yes. That's the classic problem for matrices, but I'm still trying to work out if it is really a general problem. I was hoping to see some new use cases, but none so far.

I think the matrices case is important enough to make it its own class of problems. For example, bitmap processing is another instance of matrix usefulness, just one that doesn't usually jump to mind when thinking of matrices. Then how about BigInt and arbitrary precision numbers? Wouldn't fusing some operations together be useful? Andrei
Dec 30 2008
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Wed, Dec 31, 2008 at 4:32 AM, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 Don wrote:
 Andrei Alexandrescu wrote:
 Don wrote:
 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:

of manual effort, though, and prevents you from using auto.

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.

general case.

you are focusing on eliminating temporaries, which is only part of the story. I suggest you make place in your thoughts for loop fusion.

out if it is really a general problem. I was hoping to see some new use cases, but none so far.

problems. For example, bitmap processing is another instance of matrix usefulness, just one that doesn't usually jump to mind when thinking of matrices.

Graph algorithms can often be thought of as matrices too. I recall some shortest path algorithms where you write edge weight between node i & j in the i,j entry. Then you do a multiply on the matrices, but instead of using the regular vector inner product for each <row, column>, you use max(). I forget which algo it is. I'm thinking Floyd-Warshall. Also there was some algorithm I recall that can be done with a boolean matrix. Probably some kind of connected components algo? --bb

Any graph algorithm that uses e.g. the adjacency matrix... well, uses a matrix :o). Andrei
Dec 30 2008
prev sibling parent Don <nospam nospam.com> writes:
Andrei Alexandrescu wrote:
 Don wrote:
 Andrei Alexandrescu wrote:
 Don wrote:
 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.

Don't forget that the case you are solving is not general enough because you are focusing on eliminating temporaries, which is only part of the story. I suggest you make place in your thoughts for loop fusion.

Yes. That's the classic problem for matrices, but I'm still trying to work out if it is really a general problem. I was hoping to see some new use cases, but none so far.

I think the matrices case is important enough to make it its own class of problems. For example, bitmap processing is another instance of matrix usefulness, just one that doesn't usually jump to mind when thinking of matrices.

Yes, bitmap processing almost always uses the adjacent pixels, which doesn't translate naturally to BLAS operations. The disturbing thing about both these use cases is that you normally want to involve other operations, such as dot product, which aren't operators. So an optimal solution is pretty elusive.
 Then how about BigInt and arbitrary precision numbers? Wouldn't fusing 
 some operations together be useful?

Not really, since they are rarely cache-limited. Combining slicing operations with arithmetic might be interesting, but it's pretty obscure I think.
Dec 31 2008
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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

struct instead of class for your objects.

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.

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.

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.

 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.

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.

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++).

There is a niche of cases where using the C++-style duality of classes and structs is useful. I think those cases are marginal, and that the conceptual division fostered by D is the better way to go because it's clean and avoids a lot of the inherent problems of duality. Andrei
Dec 30 2008
parent reply Weed <resume755 mail.ru> writes:
Andrei Alexandrescu :
 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

struct instead of class for your objects.

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.

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.

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.

 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.

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.

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++).

There is a niche of cases where using the C++-style duality of classes and structs is useful. I think those cases are marginal, and that the conceptual division fostered by D is the better way to go because it's clean and avoids a lot of the inherent problems of duality.

It is very a pity. My small opinion: it is impossible to reduce performance for struggle against potential errors - such languages already are, it more high-level. It how to refuse pointers because they are dangerous, difficult for beginners and without them it is possible to make any algorithm. What is D? D is a general purpose systems and applications programming language. It is a higher level language than C++, but *retains* the ability to write high performance code and interface directly with the operating system API's and with hardware. http://www.digitalmars.com/d/2.0/overview.html
Dec 30 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Weed wrote:
[about structs vs. classes]
 It is very a pity.
 My small opinion: it is impossible to reduce performance for struggle
 against potential errors - such languages already are, it more
 high-level. It how to refuse pointers because they are dangerous,
 difficult for beginners and without them it is possible to make any
 algorithm.

It's attractive to deal in absolutes, but also dangerous. When C came about, naysayers complained that it was consistently 30% slower than assembler, and generated larger code by an even higher margin. Then, some asked, what would you choose, one OS that's cool because it's written in C, or one that's one third faster? and so on. What people have forgotten by now is that C *was* high level. And it *did* incur a performance hit. It also had desirable properties that overcame that hit.
 What is D?
 D is a general purpose systems and applications programming language. It
 is a higher level language than C++, but *retains* the ability to write
 high performance code and interface directly with the operating system
 API's and with hardware.
 http://www.digitalmars.com/d/2.0/overview.html

Probably the worst thing that could happen to that description is it Kafka-esquely morphing into a dogma. Andrei
Dec 30 2008
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Andrei Alexandrescu:
 What people 
 have forgotten by now is that C *was* high level. And it *did* incur a 
 performance hit. It also had desirable properties that overcame that hit.

But probably C compilers (and JavaVirtualMachines) have improved from the first ones, so the performance difference is now smaller. And in GCC if you use C you find lot of intrinsics (for example one for each new SIMD instruction) that mitigates the problem even more. Bye, bearophile
Dec 30 2008
parent Walter Bright <newshound1 digitalmars.com> writes:
bearophile wrote:
 But probably C compilers (and JavaVirtualMachines) have improved from
 the first ones, so the performance difference is now smaller. And in
 GCC if you use C you find lot of intrinsics (for example one for each
 new SIMD instruction) that mitigates the problem even more.

Not that much smaller.
Jan 01 2009
prev sibling parent reply Weed <resume755 mail.ru> writes:
Andrei Alexandrescu :
 Weed wrote:
 [about structs vs. classes]
 It is very a pity.
 My small opinion: it is impossible to reduce performance for struggle
 against potential errors - such languages already are, it more
 high-level. It how to refuse pointers because they are dangerous,
 difficult for beginners and without them it is possible to make any
 algorithm.

It's attractive to deal in absolutes, but also dangerous. When C came about, naysayers complained that it was consistently 30% slower than assembler, and generated larger code by an even higher margin. Then, some asked, what would you choose, one OS that's cool because it's written in C, or one that's one third faster? and so on. What people have forgotten by now is that C *was* high level. And it *did* incur a performance hit. It also had desirable properties that overcame that hit.

Can in C# (it uses as far as I know too such sharing) such approach and it is justified - microsoft accelerates replacement of hardware for new OS. :) But we after all not blindly copy C#? After all this problem can be solved, IMHO. I suggest to make so: 1. To leave structures in that kind in which they is (POD) 2. To permit classes declaration such what they in C++ 3. To permit transfer the classes on value (for compulsory pass by reference and for declaration through "new" now we have "ref" keyword) 3. To check slicing during compilation. It is possible? 4. "scope" for classes to deprecate as superfluous In that case there will be problems?
 What is D?
 D is a general purpose systems and applications programming language. It
 is a higher level language than C++, but *retains* the ability to write
 high performance code and interface directly with the operating system
 API's and with hardware.
 http://www.digitalmars.com/d/2.0/overview.html

Probably the worst thing that could happen to that description is it Kafka-esquely morphing into a dogma.

Seriously, I trusted it
Dec 30 2008
parent reply Weed <resume755 mail.ru> writes:
Weed :
 Andrei Alexandrescu :
 Weed wrote:
 [about structs vs. classes]
 It is very a pity.
 My small opinion: it is impossible to reduce performance for struggle
 against potential errors - such languages already are, it more
 high-level. It how to refuse pointers because they are dangerous,
 difficult for beginners and without them it is possible to make any
 algorithm.

about, naysayers complained that it was consistently 30% slower than assembler, and generated larger code by an even higher margin. Then, some asked, what would you choose, one OS that's cool because it's written in C, or one that's one third faster? and so on. What people have forgotten by now is that C *was* high level. And it *did* incur a performance hit. It also had desirable properties that overcame that hit.

Can in C# (it uses as far as I know too such sharing) such approach and it is justified - microsoft accelerates replacement of hardware for new OS. :) But we after all not blindly copy C#? After all this problem can be solved, IMHO. I suggest to make so: 1. To leave structures in that kind in which they is (POD) 2. To permit classes declaration such what they in C++ 3. To permit transfer the classes on value (for compulsory pass by reference and for declaration through "new" now we have "ref" keyword) 3. To check slicing during compilation. It is possible?

For example prohibit assigning on value to the types, not being base or this type
 
 4. "scope" for classes to deprecate as superfluous
 
 
 In that case there will be problems?
 
 
 What is D?
 D is a general purpose systems and applications programming language. It
 is a higher level language than C++, but *retains* the ability to write
 high performance code and interface directly with the operating system
 API's and with hardware.
 http://www.digitalmars.com/d/2.0/overview.html

Kafka-esquely morphing into a dogma.

Seriously, I trusted it

Dec 30 2008
parent reply Weed <resume755 mail.ru> writes:
Weed :
 Weed :
 Andrei Alexandrescu :
 Weed wrote:
 [about structs vs. classes]
 It is very a pity.
 My small opinion: it is impossible to reduce performance for struggle
 against potential errors - such languages already are, it more
 high-level. It how to refuse pointers because they are dangerous,
 difficult for beginners and without them it is possible to make any
 algorithm.

about, naysayers complained that it was consistently 30% slower than assembler, and generated larger code by an even higher margin. Then, some asked, what would you choose, one OS that's cool because it's written in C, or one that's one third faster? and so on. What people have forgotten by now is that C *was* high level. And it *did* incur a performance hit. It also had desirable properties that overcame that hit.

it is justified - microsoft accelerates replacement of hardware for new OS. :) But we after all not blindly copy C#? After all this problem can be solved, IMHO. I suggest to make so: 1. To leave structures in that kind in which they is (POD) 2. To permit classes declaration such what they in C++ 3. To permit transfer the classes on value (for compulsory pass by reference and for declaration through "new" now we have "ref" keyword) 3. To check slicing during compilation. It is possible?

For example prohibit assigning on value to the types, not being base or this type
 4. "scope" for classes to deprecate as superfluous


 In that case there will be problems?


Who agrees with me? There are still ideas as it is possible to solve this problem and not to destroy language?
Jan 04 2009
next sibling parent Weed <resume755 mail.ru> writes:
Weed :
 Weed :
 Weed :
 Andrei Alexandrescu :
 Weed wrote:
 [about structs vs. classes]
 It is very a pity.
 My small opinion: it is impossible to reduce performance for struggle
 against potential errors - such languages already are, it more
 high-level. It how to refuse pointers because they are dangerous,
 difficult for beginners and without them it is possible to make any
 algorithm.

about, naysayers complained that it was consistently 30% slower than assembler, and generated larger code by an even higher margin. Then, some asked, what would you choose, one OS that's cool because it's written in C, or one that's one third faster? and so on. What people have forgotten by now is that C *was* high level. And it *did* incur a performance hit. It also had desirable properties that overcame that hit.

it is justified - microsoft accelerates replacement of hardware for new OS. :) But we after all not blindly copy C#? After all this problem can be solved, IMHO. I suggest to make so: 1. To leave structures in that kind in which they is (POD) 2. To permit classes declaration such what they in C++ 3. To permit transfer the classes on value (for compulsory pass by reference and for declaration through "new" now we have "ref" keyword) 3. To check slicing during compilation. It is possible?

this type
 4. "scope" for classes to deprecate as superfluous


 In that case there will be problems?


Who agrees with me? There are still ideas as it is possible to solve this problem and not to destroy language?

And against arguments are still necessary
Jan 05 2009
prev sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Weed wrote:
 Who agrees with me? There are still ideas as it is possible to solve
 this problem and not to destroy language?

When you reply to your reply to your reply to your post and nobody else replies to any of your posts, you might start thinking that nobody agrees with you, or cares enough to respond. As to your suggestion that there be compile-time checks for object slicing... well, you'd end up with almost everything with any polymorphism being done by reference for safety. In the remaining situations, scope will usually suffice. I don't think anyone sees sufficient reason to give Walter as much work as you suggest. When would you use this? - In place of the current scope keyword. - For more efficiency with object composition (though scope could be used for this, potentially). - Implementing value semantics with runtime polymorphism. The only interesting thing there is value semantics with polymorphism. If you really care, you can implement polymorphism with structs.
Jan 05 2009
next sibling parent Weed <resume755 mail.ru> writes:
Christopher Wright :
 Weed wrote:
 Who agrees with me? There are still ideas as it is possible to solve
 this problem and not to destroy language?

When you reply to your reply to your reply to your post and nobody else replies to any of your posts, you might start thinking that nobody agrees with you, or cares enough to respond.

I consoled myself that the letter has got lost in the big thread
 As to your suggestion that there be compile-time checks for object
 slicing... well, you'd end up with almost everything with any
 polymorphism being done by reference for safety. In the remaining
 situations, scope will usually suffice.
 
 I don't think anyone sees sufficient reason to give Walter as much work
 as you suggest.

D2.0 not released, it changes supplemented. I seriously consider at it there is a chance to become the most good language, a "silver bullet". :) This discussion - my small contribution.
 When would you use this?
  - In place of the current scope keyword.

I consider that "scope" is attempt to fix bad design. Have come to that that on a stack all the same it is necessary to place classes and have added a word, but it does not solve all problems.
  - For more efficiency with object composition (though scope could be
 used for this, potentially).
  - Implementing value semantics with runtime polymorphism.
 

And, probably, in the future it will help to add other possibilities. For example compile-time initialization of classes about which I here spoke too (not in this thread)
 The only interesting thing there is value semantics with polymorphism.
 If you really care, you can implement polymorphism with structs.

Excellent templates, unit tests, closures, delegates, threads... And after all it is offered to the programmer most implement OOP by hands?
Jan 05 2009
prev sibling parent reply Weed <resume755 mail.ru> writes:
Bill Baxter :
 On Tue, Jan 6, 2009 at 1:41 PM, Christopher Wright <dhasenan gmail.com> wrote:
 Weed wrote:
 Who agrees with me? There are still ideas as it is possible to solve
 this problem and not to destroy language?

replies to any of your posts, you might start thinking that nobody agrees with you, or cares enough to respond. As to your suggestion that there be compile-time checks for object slicing... well, you'd end up with almost everything with any polymorphism being done by reference for safety. In the remaining situations, scope will usually suffice. I don't think anyone sees sufficient reason to give Walter as much work as you suggest. When would you use this? - In place of the current scope keyword. - For more efficiency with object composition (though scope could be used for this, potentially). - Implementing value semantics with runtime polymorphism. The only interesting thing there is value semantics with polymorphism. If you really care, you can implement polymorphism with structs.

My problem is more that I just can't understand the guy so I don't know if I agree with him or not. I think the choice between just value semantics / POD / no polymorphism / heap or stack and reference semantics / non-POD / polymorphism / heap only

No, for classes I suggest to choose between: reference semantics / non-POD / polymorphism / heap only (current state) and value or reference semantics / non-POD / polymorphism / heap or stack As a matter of fact how it was in C++, but with check slicing or simply prohibition of assignment to other types by value. Syntax will demand attention before change - it is necessary to make so that there was no mixing of names of references and classes on value.
Jan 05 2009
parent Weed <resume755 mail.ru> writes:
Weed :
 Bill Baxter :
 On Tue, Jan 6, 2009 at 1:41 PM, Christopher Wright <dhasenan gmail.com> wrote:
 Weed wrote:
 Who agrees with me? There are still ideas as it is possible to solve
 this problem and not to destroy language?

replies to any of your posts, you might start thinking that nobody agrees with you, or cares enough to respond. As to your suggestion that there be compile-time checks for object slicing... well, you'd end up with almost everything with any polymorphism being done by reference for safety. In the remaining situations, scope will usually suffice. I don't think anyone sees sufficient reason to give Walter as much work as you suggest. When would you use this? - In place of the current scope keyword. - For more efficiency with object composition (though scope could be used for this, potentially). - Implementing value semantics with runtime polymorphism. The only interesting thing there is value semantics with polymorphism. If you really care, you can implement polymorphism with structs.

know if I agree with him or not. I think the choice between just value semantics / POD / no polymorphism / heap or stack and reference semantics / non-POD / polymorphism / heap only

No, for classes I suggest to choose between: reference semantics / non-POD / polymorphism / heap only (current state) and value or reference semantics / non-POD / polymorphism / heap or stack As a matter of fact how it was in C++, but with check slicing or simply prohibition of assignment to other types by value.

====
 Syntax will demand
 attention before change - it is necessary to make so that there was no
 mixing of names of references and classes on value.

^^^^^^^^ Here the nonsense is written, it is not necessary to be afraid of mixing. I should sleep more:)
Jan 05 2009
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Tue, Jan 6, 2009 at 1:41 PM, Christopher Wright <dhasenan gmail.com> wrote:
 Weed wrote:
 Who agrees with me? There are still ideas as it is possible to solve
 this problem and not to destroy language?

When you reply to your reply to your reply to your post and nobody else replies to any of your posts, you might start thinking that nobody agrees with you, or cares enough to respond. As to your suggestion that there be compile-time checks for object slicing... well, you'd end up with almost everything with any polymorphism being done by reference for safety. In the remaining situations, scope will usually suffice. I don't think anyone sees sufficient reason to give Walter as much work as you suggest. When would you use this? - In place of the current scope keyword. - For more efficiency with object composition (though scope could be used for this, potentially). - Implementing value semantics with runtime polymorphism. The only interesting thing there is value semantics with polymorphism. If you really care, you can implement polymorphism with structs.

My problem is more that I just can't understand the guy so I don't know if I agree with him or not. I think the choice between just value semantics / POD / no polymorphism / heap or stack and reference semantics / non-POD / polymorphism / heap only Are not quite sufficent. I find myself often wanting things that are mixes of these two attribute sets, and it's often difficult for me to decide up front which is more appropriate for a given case. For instance reference-semantics POD. And other times it's out of my hands writing a library -- the more appropriate one depends on the person using the library not me. So I've played around with things like implementing the guts of something entirely as a mixin that gets mixed in to both a struct and a class shell. It sorta works but it's a lot more work than the unified model in C++ where I just write one class and the user decides how to use it. And scope has a lot of holes. You can't create a scope member of a class. I don't think you can create an array of scope objects, either. So I'm not really convinced that D got the distinction right. It's never really felt right to me and it still doesn't today. But I don't have any better suggestions at the moment. It just feels very un-orthogonal to me. Different unrelated choices are all bundled under the same big toggle switch rather than being able to toggle the different attributes the way I want them. --bb
Jan 05 2009
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Wed, Dec 31, 2008 at 4:32 AM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 Don wrote:
 Andrei Alexandrescu wrote:
 Don wrote:
 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.

Don't forget that the case you are solving is not general enough because you are focusing on eliminating temporaries, which is only part of the story. I suggest you make place in your thoughts for loop fusion.

Yes. That's the classic problem for matrices, but I'm still trying to work out if it is really a general problem. I was hoping to see some new use cases, but none so far.

I think the matrices case is important enough to make it its own class of problems. For example, bitmap processing is another instance of matrix usefulness, just one that doesn't usually jump to mind when thinking of matrices.

Graph algorithms can often be thought of as matrices too. I recall some shortest path algorithms where you write edge weight between node i & j in the i,j entry. Then you do a multiply on the matrices, but instead of using the regular vector inner product for each <row, column>, you use max(). I forget which algo it is. I'm thinking Floyd-Warshall. Also there was some algorithm I recall that can be done with a boolean matrix. Probably some kind of connected components algo? --bb
Dec 30 2008
prev sibling next sibling parent reply Weed <resume755 mail.ru> writes:
(Here I generalise my sentence with supplement)


The POD data and the data supporting polymorphism are necessary to us.
POD the data is stored in structs and polymorphic objects is classes.

Both types (class and struct) can be instanced in a heap or on a stack.
(And in invariant ROM too, but there it is not important)

Classes and structures support inheritance. But structures do not
support polymorphism (as are POD type without vptr) - at attempt to
implement virtual function in structure the compiler will give out an
error: "struct StructName is POD type, it is not support polymorphism,
use class instead of". (And certainly structures are not inherited from
classes, including from super class Object)

Thus, the programmer always knows the object is POD-data or not. The
problem of simple alteration of structure to a class and vice versa when
necessary also solved.

For an exception of splitting of objects it is necessary to check during
compilation: or to forbid assignment on value for types not being to the
data (how now it works for the structs objects on value) or to forbid
the access to the fields added at descending inheritance (its more
difficult but desirable)


Please, state critical remarks
Jan 06 2009
next sibling parent reply Weed <resume755 mail.ru> writes:
Weed :
 (Here I generalise my sentence with supplement)
 
 
 The POD data and the data supporting polymorphism are necessary to us.
 POD the data is stored in structs and polymorphic objects is classes.
 
 Both types (class and struct) can be instanced in a heap or on a stack.
 (And in invariant ROM too, but there it is not important)
 
 Classes and structures support inheritance. But structures do not
 support polymorphism (as are POD type without vptr) - at attempt to
 implement virtual function in structure the compiler will give out an
 error: "struct StructName is POD type, it is not support polymorphism,
 use class instead of". (And certainly structures are not inherited from
 classes, including from super class Object)
 
 Thus, the programmer always knows the object is POD-data or not. The
 problem of simple alteration of structure to a class and vice versa when
 necessary also solved.
 
 For an exception of splitting of objects it is necessary to check during
 compilation: or to forbid assignment on value for types not being to the
 data (how now it works for the structs objects on value) or to forbid
 the access to the fields added at descending inheritance (its more
 difficult but desirable)

And similar it will not break an existing code
 Please, state critical remarks

Jan 07 2009
parent reply Weed <resume755 mail.ru> writes:
Weed :
 Weed :
 (Here I generalise my sentence with supplement)


 The POD data and the data supporting polymorphism are necessary to us.
 POD the data is stored in structs and polymorphic objects is classes.

 Both types (class and struct) can be instanced in a heap or on a stack.
 (And in invariant ROM too, but there it is not important)

 Classes and structures support inheritance. But structures do not
 support polymorphism (as are POD type without vptr) - at attempt to
 implement virtual function in structure the compiler will give out an
 error: "struct StructName is POD type, it is not support polymorphism,
 use class instead of". (And certainly structures are not inherited from
 classes, including from super class Object)

 Thus, the programmer always knows the object is POD-data or not. The
 problem of simple alteration of structure to a class and vice versa when
 necessary also solved.

 For an exception of splitting of objects it is necessary to check during
 compilation: or to forbid assignment on value for types not being to the
 data (how now it works for the structs objects on value) or to forbid
 the access to the fields added at descending inheritance (its more
 difficult but desirable)

And similar it will not break an existing code

 
 Please, state critical remarks


Tell, what it is necessary to make that discussion of this question has taken place? I in despair. I even think to wait supports 2.0 in open source compiler using LLVM and to add there this functionality (I hope, my skills will just grow for such operation.)
Jan 09 2009
next sibling parent Weed <resume755 mail.ru> writes:
Denis Koroskin :

 Back to topic, C++ doesn't have any meaningful separation between
 classes and structs. D does - (one of it is that) classes are heap
 allocated by default whereas structs are stack allocated by default. You
 can override either behavior:
 
 class C {}
 struct S {}
 
 C c = new C(); // heap-allocated (default)
 S s = S(); // stack-allocated (default)
 
 scope C c = new C(); // stack-allocated

This way does not approach because scope value is impossible to return from function.
 S* s = new S(); // heap allocated
 
 One problem I see with it is that the syntax is so much different, but
 that's another topic.
 Other one is that the following works for local variables exclusively,
 i.e. you can't have class instance aggregated inside another class by
 value.

I am not understand what here a problem with class inside other class.
 Yes, I think there is a room for improvement but it is of little
 priority for me.
 
 You don't provide use cases nor examples of possible syntax, but they
 are crucial for understanding.

I still was not sure what syntax could. While I operate with the such - probably, it don't break an existing code: class C {} struct S {} C c = new C(); // heap-allocated S s; // stack-allocated S s = S(); // stack-allocated S* s = new S(); // heap allocated C c(); // stack-allocated (added by me) Brackets () do not allow to mix object with the reference. Also, they may contain constructor parameters. Further objects are used as usually.
 What else?
 
 Struct inheritance - yes it is nice to have, even without polymorphism.
 It was proposed many times but with no success. Someone suggested to use
 aggregation and opDot instead and allow implicit cast of pointer to
 struct to pointer to struct's first element to emulate inheritance:

Inheriting of structures is necessary for convenience of replacement "struct" to "class" and vice versa. Therefore possibility of replacement of struct inheritance on other constructions it is not essential.
 I'd suggest you to state you ideas as simple and keep your posts as
 small as possible (trust me, few people like reading long posts). You
 could also ask someone to check your post before submitting - this will
 increase the probability of your message to be read and understood.

I agree, but the changes offered by me separately look as unreasonably, and only in the sum they yield good result. Thanks for kind words :)
Jan 10 2009
prev sibling parent reply Weed <resume755 mail.ru> writes:
Denis Koroskin :

 I'd suggest you to state you ideas as simple and keep your posts as
 small as possible (trust me, few people like reading long posts).

The extra-short formulation of my idea: Objects should be divided on POD (struct) and non-POD (class). Instead of such division as now: POD && value type (struct) and POD && reference type (class).
Jan 10 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
Weed wrote:
 Denis Koroskin :
 
 I'd suggest you to state you ideas as simple and keep your posts as
 small as possible (trust me, few people like reading long posts).

The extra-short formulation of my idea: Objects should be divided on POD (struct) and non-POD (class). Instead of such division as now: POD && value type (struct) and POD && reference type (class).

The reference versus value type difference is just a matter of defaults. Returning a class instance on the stack from a function is possible with inout parameters, though you can't use a constructor in that case: void main () { scope MyClass obj = new MyClass; foo (obj); } void foo (inout MyClass obj) { // initialize obj somehow }
Jan 10 2009
next sibling parent Weed <resume755 mail.ru> writes:
Christopher Wright :
 Weed wrote:
 Denis Koroskin :

 I'd suggest you to state you ideas as simple and keep your posts as
 small as possible (trust me, few people like reading long posts).

The extra-short formulation of my idea: Objects should be divided on POD (struct) and non-POD (class). Instead of such division as now: POD && value type (struct) and POD && reference type (class).

The reference versus value type difference is just a matter of defaults. Returning a class instance on the stack from a function is possible with inout parameters, though you can't use a constructor in that case: void main () { scope MyClass obj = new MyClass; foo (obj); } void foo (inout MyClass obj) { // initialize obj somehow }

Problem not only with constructor calling. This way does not works for temporary objects. For example, at operator overloading in expression like a = b + c + d. The case in detail was considered in this thread: "division of objects into classes and structures is bad", http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81359 Also, it impossible to create an array of scope objects.
Jan 10 2009
prev sibling parent reply Weed <resume755 mail.ru> writes:
Bill Baxter :

 But since classes can be polymorphic, value copying gets you into
 slicing problems.  That's why value copying is disabled to begin with.
  So disabling value copies is a good thing.

It is not always a good thing. I propose to prohibit only the copying by value of the base type to derivative type
 
 And that's also the problem with putting scope'd things inside another
 class or an array.  Since they don't have value semantics,

Yes, this is what I mean
Jan 10 2009
parent reply Weed <resume755 mail.ru> writes:
Bill Baxter :
 2009/1/11 Weed <resume755 mail.ru>:
 Bill Baxter :

 But since classes can be polymorphic, value copying gets you into
 slicing problems.  That's why value copying is disabled to begin with.
  So disabling value copies is a good thing.


Yeh, I just mean there is some merit in disabling value copies. But I don't rule out the possibility that there may be an even better way that banning them altogether.
 I propose to prohibit only the copying by value of the base type to
 derivative type

Ok, this is key. How do you propose to do this? In general it requires a runtime check, I think. And I think you need to say that you prohibit copying unless typeA==typeB exactly. If you allow copying either way between base and derived you are asking for trouble. But still given Base x = get_one(); Base y = get_another(); *x = *y; // presumed value copy syntax you have no way in general to know that x and y are really both a Base at compile time. So you must have a run-time check there. Perhaps it could be omitted for -release builds, though.

It can lead to a difficult and non-reproduceable errors than old C++-style splitting. It is possible to try to prohibit assignment of the dereferenced pointers? Simply to prohibit assignment too it is possible, essentially it changes nothing.
 And that's also the problem with putting scope'd things inside another
 class or an array.  Since they don't have value semantics,


So assuming you had this, the important question is what would you do with it?

The most difficult. My arguments: 1. Problem of a choice of correct type for the object. A mathematical matrix - a classical example. A class it should be or structure? My offer without serious consequences allows to move solution of this problem from a design stage to a programming stage - is it will be simple by replacement keyword class to struct. 2. Performance increases. It is not necessary to allocate at the slightest pretext memory in a heap. 3. I offer syntax which presumably does not break an existing code. + On how many I understand, in the existing compiler all necessary for implementation already is, a problem only in syntax addition. 4. Java and C# also uses objects by reference? But both these of language are interpreted. I assume that the interpreter generally with identical speed allocates memory in a heap and in a stack, therefore authors of these languages and used reference model. D is compiled language and to borrow reference model incorrectly. In D the programmer should have possibility most to decide where to place object.
 You still have the problem that the current system works pretty well.
 And has a lot of history.  So you need a very compelling use case to
 convince Walter that something should change.

Thus, actually Java and C# systems works pretty well. And they has a lot of history. But not D. This situation reminds me history with the Hungarian notation: Many years ago many developers have started to deliver interfaces and to sell books where it was used. This very correct invention affirmed as many books that and only in ~5 years there were articles with the materials specifying in groundlessness of usage of this notation. Till this time many entered it into corporate standards and suffered, though, I am sure, they were visited by thoughts about irrelevance of the given notation except for special cases.
Jan 10 2009
next sibling parent reply Weed <resume755 mail.ru> writes:
Denis Koroskin :
 On Sun, 11 Jan 2009 05:04:11 +0300, Weed <resume755 mail.ru> wrote:
 
 Bill Baxter :
 2009/1/11 Weed <resume755 mail.ru>:
 Bill Baxter :

 But since classes can be polymorphic, value copying gets you into
 slicing problems.  That's why value copying is disabled to begin with.
  So disabling value copies is a good thing.


Yeh, I just mean there is some merit in disabling value copies. But I don't rule out the possibility that there may be an even better way that banning them altogether.
 I propose to prohibit only the copying by value of the base type to
 derivative type

Ok, this is key. How do you propose to do this? In general it requires a runtime check, I think. And I think you need to say that you prohibit copying unless typeA==typeB exactly. If you allow copying either way between base and derived you are asking for trouble. But still given Base x = get_one(); Base y = get_another(); *x = *y; // presumed value copy syntax you have no way in general to know that x and y are really both a Base at compile time. So you must have a run-time check there. Perhaps it could be omitted for -release builds, though.

It can lead to a difficult and non-reproduceable errors than old C++-style splitting. It is possible to try to prohibit assignment of the dereferenced pointers? Simply to prohibit assignment too it is possible, essentially it changes nothing.

Err.. I don't get what you say. The *x = *y is just one of the possible syntaxes, nothing else.

(I have incorrectly expressed) If dereferencing was not used in lvalue or rvalue and is both a classes by value it is possible to assignment, except cases when the base type to the derivative is assigned. Example: class C {} class C2 : C {} C c(); C2 c2(); C* c_p = &c; C2* c2_p = &c2; c = c2; // ok c2 = c; // err *c_p = *c2_p; // err *c2_p = *c_p; // err c2 = *c2_p; // err and: c = c2 + *c2_p; // ok
 And that's also the problem with putting scope'd things inside another
 class or an array.  Since they don't have value semantics,


So assuming you had this, the important question is what would you do with it?

The most difficult. My arguments: 1. Problem of a choice of correct type for the object. A mathematical matrix - a classical example. A class it should be or structure? My offer without serious consequences allows to move solution of this problem from a design stage to a programming stage - is it will be simple by replacement keyword class to struct.

Having the same syntax for both classes and struct is a nice goal, I agree. But it should be taken as a different issue and solved separately, too.

So I offer: the inheritance of structures this one of offers, which in itself it seems insignificant, but useful if to make classes by value.
 2. Performance increases. It is not necessary to allocate at the
 slightest pretext memory in a heap.


 3. I offer syntax which presumably does not break an existing code.
 + On how many I understand, in the existing compiler all necessary for
 implementation already is, a problem only in syntax addition.


 4. Java and C# also uses objects by reference? But both these of
 language are interpreted. I assume that the interpreter generally with
 identical speed allocates memory in a heap and in a stack, therefore
 authors of these languages and used reference model.

Neither of these languages are interpreted, they both are compiled into native code at runtime.

Oh!:) but I suspect such classes scheme somehow correspond with JIT-compilation.
 
 D is compiled language and to borrow reference model incorrectly. In D
 the programmer should have possibility most to decide where to place
 object.


 You still have the problem that the current system works pretty well.
 And has a lot of history.  So you need a very compelling use case to
 convince Walter that something should change.

Thus, actually Java and C# systems works pretty well. And they has a lot of history. But not D.

DMD 0.001 was released 7 years ago. Long enough, I think.

Yes, I know. I hint at that that it seems this scheme have copied because it is popular but have not considered characteristic for C++-like compiled language nuances.
Jan 11 2009
next sibling parent reply Weed <resume755 mail.ru> writes:
Weed :

 4. Java and C# also uses objects by reference? But both these of
 language are interpreted. I assume that the interpreter generally with
 identical speed allocates memory in a heap and in a stack, therefore
 authors of these languages and used reference model.

native code at runtime.

Oh!:) but I suspect such classes scheme somehow correspond with JIT-compilation.

I guess allocation in Java occurs fast because of usage of the its own memory manager. I do not know how it is fair, but: http://www.ibm.com/developerworks/java/library/j-jtp09275.html "Pop quiz: Which language boasts faster raw allocation performance, the Java language, or C/C++? The answer may surprise you -- allocation in modern JVMs is far faster than the best performing malloc implementations. The common code path for new Object() in HotSpot 1.4.2 and later is approximately 10 machine instructions (data provided by Sun; see Resources), whereas the best performing malloc implementations in C require on average between 60 and 100 instructions per call (Detlefs, et. al.; see Resources)." Therefore they do not worry concerning performance of creation of objects in a heap and reference classes for them approach.
Jan 11 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Weed wrote:
 Weed пишет:
 
 4. Java and C# also uses objects by reference? But both these of
 language are interpreted. I assume that the interpreter generally with
 identical speed allocates memory in a heap and in a stack, therefore
 authors of these languages and used reference model.

native code at runtime.

JIT-compilation.

I guess allocation in Java occurs fast because of usage of the its own memory manager. I do not know how it is fair, but: http://www.ibm.com/developerworks/java/library/j-jtp09275.html "Pop quiz: Which language boasts faster raw allocation performance, the Java language, or C/C++? The answer may surprise you -- allocation in modern JVMs is far faster than the best performing malloc implementations. The common code path for new Object() in HotSpot 1.4.2 and later is approximately 10 machine instructions (data provided by Sun; see Resources), whereas the best performing malloc implementations in C require on average between 60 and 100 instructions per call (Detlefs, et. al.; see Resources)."

Meh, that should be taken with a grain of salt. An allocator that only bumps a pointer will simply eat more memory and be less cache-friendly. Many applications aren't that thrilled with the costs of such a model. Andrei
Jan 11 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Brad Roberts wrote:
 Andrei Alexandrescu wrote:
 Weed wrote:
 Weed пишет:

 4. Java and C# also uses objects by reference? But both these of
 language are interpreted. I assume that the interpreter generally with
 identical speed allocates memory in a heap and in a stack, therefore
 authors of these languages and used reference model.

native code at runtime.

JIT-compilation.

memory manager. I do not know how it is fair, but: http://www.ibm.com/developerworks/java/library/j-jtp09275.html "Pop quiz: Which language boasts faster raw allocation performance, the Java language, or C/C++? The answer may surprise you -- allocation in modern JVMs is far faster than the best performing malloc implementations. The common code path for new Object() in HotSpot 1.4.2 and later is approximately 10 machine instructions (data provided by Sun; see Resources), whereas the best performing malloc implementations in C require on average between 60 and 100 instructions per call (Detlefs, et. al.; see Resources)."

bumps a pointer will simply eat more memory and be less cache-friendly. Many applications aren't that thrilled with the costs of such a model. Andrei

Take it as nicely seasoned. The current jvm gc and memory subsystem is _extremely_ clever. However, it completely relies on the ability to move objects during garbage collection. If it was purely the allocator that behaved that way, you'd be right. But it's interaction with the gc is where the system comes together to form a useful whole.

I understand. My point is that a 10-cycles-per-allocation allocator will necessarily use more memory than one that attempts to reuse memory. There's no way around that. I mean we know what those cycles do :o). Some application don't work well with that. Escape analysis does reduce the number of cache-unfriendly patterns, but, as of today, not to the point the issue can be safely ignored. There's no contention that GC has made great progress lately, and that's a great thing. Andrei
Jan 12 2009
parent reply Weed <resume755 mail.ru> writes:
Andrei Alexandrescu :
 Brad Roberts wrote:
 Andrei Alexandrescu wrote:
 Weed wrote:
 Weed :

 4. Java and C# also uses objects by reference? But both these of
 language are interpreted. I assume that the interpreter generally
 with
 identical speed allocates memory in a heap and in a stack, therefore
 authors of these languages and used reference model.

into native code at runtime.

JIT-compilation.

memory manager. I do not know how it is fair, but: http://www.ibm.com/developerworks/java/library/j-jtp09275.html "Pop quiz: Which language boasts faster raw allocation performance, the Java language, or C/C++? The answer may surprise you -- allocation in modern JVMs is far faster than the best performing malloc implementations. The common code path for new Object() in HotSpot 1.4.2 and later is approximately 10 machine instructions (data provided by Sun; see Resources), whereas the best performing malloc implementations in C require on average between 60 and 100 instructions per call (Detlefs, et. al.; see Resources)."

bumps a pointer will simply eat more memory and be less cache-friendly. Many applications aren't that thrilled with the costs of such a model. Andrei

Take it as nicely seasoned. The current jvm gc and memory subsystem is _extremely_ clever. However, it completely relies on the ability to move objects during garbage collection. If it was purely the allocator that behaved that way, you'd be right. But it's interaction with the gc is where the system comes together to form a useful whole.

I understand. My point is that a 10-cycles-per-allocation allocator

10 *cycles* per allocation?
 will
 necessarily use more memory than one that attempts to reuse memory.
 There's no way around that. I mean we know what those cycles do :o).
 Some application don't work well with that. Escape analysis does reduce
 the number of cache-unfriendly patterns, but, as of today, not to the
 point the issue can be safely ignored.
 
 There's no contention that GC has made great progress lately, and that's
 a great thing.

In any case, we cannot add such memory manager in D. And such resource allocation does not approach us, and mandatory creation of objects in a heap does not approach for D.
Jan 12 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 So it may be worth while to have a special kind construct for
 containing data that the compiler is free to move around.  This type
 would have a hidden pointer inside of it that can be moved around by
 the gc, but applications would not be allowed to access that pointer.
  And I suppose that means all access to the data would given via
 lvalue only.   Probably wouldn't take much on the part of the GC to
 provide the necessary hooks.  Just some sort of "relocatable alloc"
 call.  Rest could probably be handled in higher level libs.

Interesting. (Link?) Structs in D are supposed to be location transparent (it is unclear to me to what extent this should be enforced vs. just assumed), so if the compiler can show the address of a struct is not taken, it should be free to move it around. Andrei
Jan 12 2009
prev sibling parent reply Benji Smith <dlanguage benjismith.net> writes:
Andrei Alexandrescu wrote:
 Weed wrote:
 Weed пишет:

 4. Java and C# also uses objects by reference? But both these of
 language are interpreted. I assume that the interpreter generally with
 identical speed allocates memory in a heap and in a stack, therefore
 authors of these languages and used reference model.

native code at runtime.

JIT-compilation.

I guess allocation in Java occurs fast because of usage of the its own memory manager. I do not know how it is fair, but: http://www.ibm.com/developerworks/java/library/j-jtp09275.html "Pop quiz: Which language boasts faster raw allocation performance, the Java language, or C/C++? The answer may surprise you -- allocation in modern JVMs is far faster than the best performing malloc implementations. The common code path for new Object() in HotSpot 1.4.2 and later is approximately 10 machine instructions (data provided by Sun; see Resources), whereas the best performing malloc implementations in C require on average between 60 and 100 instructions per call (Detlefs, et. al.; see Resources)."

Meh, that should be taken with a grain of salt. An allocator that only bumps a pointer will simply eat more memory and be less cache-friendly. Many applications aren't that thrilled with the costs of such a model. Andrei

Actually, memory allocated in the JVM is very cache-friendly, since two subsequent allocations will always be adjacent to one another in physical memory. And, since the JVM uses a moving GC, long-lived objects move closer and closer together. Of course, Java programmers tend to be less careful about memory allocation, so they usually consume **way** too much memory and lose the benefits of the moving GC. Java-the-langauge and Java-the-platform are very efficient, even if the java frameworks and java patterns tend to bloated and nasty. --benji
Jan 12 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Benji Smith wrote:
 Actually, memory allocated in the JVM is very cache-friendly, since two 
 subsequent allocations will always be adjacent to one another in 
 physical memory. And, since the JVM uses a moving GC, long-lived objects 
 move closer and closer together.

Well the problem is that the allocation size grows quickly. Allocate and dispose one object per loop -> pages will be quickly eaten. for (...) { JavaClassWithAReallyLongNameAsTheyUsuallyAre o = factory.giveMeOne(); o.method(); } The escape analyzer could catch that the variable doesn't survive the pass through the loop, but the call to method makes things rather tricky (virtual, source unavailable...). So then we're facing a quickly growing allocation block and consequently less cache friendliness and more frequent collections. Andrei
Jan 12 2009
parent reply Benji Smith <dlanguage benjismith.net> writes:
Andrei Alexandrescu wrote:
 Benji Smith wrote:
 Actually, memory allocated in the JVM is very cache-friendly, since 
 two subsequent allocations will always be adjacent to one another in 
 physical memory. And, since the JVM uses a moving GC, long-lived 
 objects move closer and closer together.

Well the problem is that the allocation size grows quickly. Allocate and dispose one object per loop -> pages will be quickly eaten. for (...) { JavaClassWithAReallyLongNameAsTheyUsuallyAre o = factory.giveMeOne(); o.method(); } The escape analyzer could catch that the variable doesn't survive the pass through the loop, but the call to method makes things rather tricky (virtual, source unavailable...). So then we're facing a quickly growing allocation block and consequently less cache friendliness and more frequent collections. Andrei

Good point. I remember five years ago when people were buzzing about the possible implementation of escape analysis in the next Java version, and how it'd move a boatload of intermediate object allocations from the heap to the stack. Personally, I don't think it'll ever happen. They can't even agree on how to get *closures* into the language. I personally think the JVM and the HotSpot compiler are two of the greatest accomplishments of computer science. But the Java community has long since jumped the shark, and I don't expect much innovation from that neighborhood anymore. --benji
Jan 12 2009
parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Benji Smith wrote:
 [snip]

 ... But the Java community has
 long since jumped the shark, and I don't expect much innovation from
 that neighborhood anymore.
 
 --benji

import javax.actions.aquatic.jumper.*; import javax.zoology.animals.*; class SharkJumper { public SharkJumper() { } public void jump() throws Exception { AnimalFactory animalFactory = new AnimalFactory(false); GenusRegistry genusRegistry = new GenusRegistry(); Animal animal = animalFactory.createAnimal(genusRegistry.findScientifficNameFromCommonName("shark")); ActionFactory actionFactory = new ActionFactory(); Action action = actionFactory.createFromVerb("jump", Culture.createFromShortName("en")); action.performActionOnObjectPassedAsFirstArgument(animal); } } Sorry, couldn't resist a chance to bash Java :D Apologies for any mistakes; it's been a while since I was forced a gunpoint to write Java code... -- Daniel
Jan 12 2009
prev sibling parent Weed <resume755 mail.ru> writes:
Weed :
 Denis Koroskin :
 On Sun, 11 Jan 2009 05:04:11 +0300, Weed <resume755 mail.ru> wrote:

 Bill Baxter :
 2009/1/11 Weed <resume755 mail.ru>:
 Bill Baxter :

 But since classes can be polymorphic, value copying gets you into
 slicing problems.  That's why value copying is disabled to begin with.
  So disabling value copies is a good thing.


don't rule out the possibility that there may be an even better way that banning them altogether.
 I propose to prohibit only the copying by value of the base type to
 derivative type

requires a runtime check, I think. And I think you need to say that you prohibit copying unless typeA==typeB exactly. If you allow copying either way between base and derived you are asking for trouble. But still given Base x = get_one(); Base y = get_another(); *x = *y; // presumed value copy syntax you have no way in general to know that x and y are really both a Base at compile time. So you must have a run-time check there. Perhaps it could be omitted for -release builds, though.

C++-style splitting. It is possible to try to prohibit assignment of the dereferenced pointers? Simply to prohibit assignment too it is possible, essentially it changes nothing.

syntaxes, nothing else.

(I have incorrectly expressed) If dereferencing was not used in lvalue or rvalue and is both a classes by value it is possible to assignment, except cases when the base type to the derivative is assigned. Example: class C {} class C2 : C {} C c(); C2 c2(); C* c_p = &c; C2* c2_p = &c2; c = c2; // ok c2 = c; // err *c_p = *c2_p; // err *c2_p = *c_p; // err c2 = *c2_p; // err and: c = c2 + *c2_p; // ok

And any remark on the sentence?..
Jan 15 2009
prev sibling parent "Daniel de Kok" <me danieldk.org> writes:
On Tue, Jan 13, 2009 at 7:30 AM, Daniel Keep
<daniel.keep.lists gmail.com> wrote:
[snip]
 Sorry, couldn't resist a chance to bash Java :D  Apologies for any
 mistakes; it's been a while since I was forced a gunpoint to write Java
 code...

That's why there is Scala. A far more elegant language, the same VM. -- Daniel
Jan 14 2009
prev sibling next sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
On Wed, Jan 7, 2009 at 4:22 AM, Weed <resume755 mail.ru> wrote:
 (Here I generalise my sentence with supplement)


 The POD data and the data supporting polymorphism are necessary to us.
 POD the data is stored in structs and polymorphic objects is classes.

 Both types (class and struct) can be instanced in a heap or on a stack.
 (And in invariant ROM too, but there it is not important)

 Classes and structures support inheritance. But structures do not
 support polymorphism (as are POD type without vptr) - at attempt to
 implement virtual function in structure the compiler will give out an
 error: "struct StructName is POD type, it is not support polymorphism,
 use class instead of". (And certainly structures are not inherited from
 classes, including from super class Object)

 Thus, the programmer always knows the object is POD-data or not. The
 problem of simple alteration of structure to a class and vice versa when
 necessary also solved.

 For an exception of splitting of objects it is necessary to check during
 compilation: or to forbid assignment on value for types not being to the
 data (how now it works for the structs objects on value) or to forbid
 the access to the fields added at descending inheritance (its more
 difficult but desirable)


 Please, state critical remarks

Your English is really hard to make sense of. That's my guess for why there are no responses. Heck, I'd *like* to respond, but I just can't tell what you're trying to say. --bb
Jan 09 2009
parent reply Weed <resume755 mail.ru> writes:
Bill Baxter :

 Please, state critical remarks

Your English is really hard to make sense of. That's my guess for why there are no responses. Heck, I'd *like* to respond, but I just can't tell what you're trying to say.

All is absolutely poor? If not everything, can you select that demands the explanation?
Jan 10 2009
parent downs <default_357-line yahoo.de> writes:
Weed wrote:
 Bill Baxter :
 
 Please, state critical remarks

That's my guess for why there are no responses. Heck, I'd *like* to respond, but I just can't tell what you're trying to say.

All is absolutely poor? If not everything, can you select that demands the explanation?

Translation: Is it all bad? Otherwise, can you say what you want clarified?
Jan 12 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Sat, 10 Jan 2009 04:02:59 +0300, Weed <resume755 mail.ru> wrote:

 Weed пишет:
 Weed пишет:
 (Here I generalise my sentence with supplement)


 The POD data and the data supporting polymorphism are necessary to us.
 POD the data is stored in structs and polymorphic objects is classes.

 Both types (class and struct) can be instanced in a heap or on a stack.
 (And in invariant ROM too, but there it is not important)

 Classes and structures support inheritance. But structures do not
 support polymorphism (as are POD type without vptr) - at attempt to
 implement virtual function in structure the compiler will give out an
 error: "struct StructName is POD type, it is not support polymorphism,
 use class instead of". (And certainly structures are not inherited from
 classes, including from super class Object)

 Thus, the programmer always knows the object is POD-data or not. The
 problem of simple alteration of structure to a class and vice versa  
 when
 necessary also solved.

 For an exception of splitting of objects it is necessary to check  
 during
 compilation: or to forbid assignment on value for types not being to  
 the
 data (how now it works for the structs objects on value) or to forbid
 the access to the fields added at descending inheritance (its more
 difficult but desirable)

And similar it will not break an existing code

 Please, state critical remarks


Tell, what it is necessary to make that discussion of this question has taken place? I in despair. I even think to wait supports 2.0 in open source compiler using LLVM and to add there this functionality (I hope, my skills will just grow for such operation.)

I'll explain here what *I* think about. There are a lot of topics concurrently discussed in D newsgroups. I am interested in some of them, others I skip. I usually skip your posts because they are a pain to read and have little of interesting ideas. Perhaps I am wrong and didn't fully understand your idea, but I don't think we reconsider class/structs design. I believe the design is sound and I don't think anyone (expect you) would like to have these basic language principles changed. As such, the whole idea is pretty much a "dead horse" and I don't want to shoot it by contributing to it. If it was a forum and I were a moderator I'd just close the topic to stop spreading confusion, but that's just me. Back to topic, C++ doesn't have any meaningful separation between classes and structs. D does - (one of it is that) classes are heap allocated by default whereas structs are stack allocated by default. You can override either behavior: class C {} struct S {} C c = new C(); // heap-allocated (default) S s = S(); // stack-allocated (default) scope C c = new C(); // stack-allocated S* s = new S(); // heap allocated One problem I see with it is that the syntax is so much different, but that's another topic. Other one is that the following works for local variables exclusively, i.e. you can't have class instance aggregated inside another class by value. Yes, I think there is a room for improvement but it is of little priority for me. You don't provide use cases nor examples of possible syntax, but they are crucial for understanding. What else? Struct inheritance - yes it is nice to have, even without polymorphism. It was proposed many times but with no success. Someone suggested to use aggregation and opDot instead and allow implicit cast of pointer to struct to pointer to struct's first element to emulate inheritance: struct Foo { void fooMethod() {} } struct Bar { Foo foo; Foo* opDot() { return &foo; } float f; } Bar* bar = new Bar(); bar.fooMethod(); Foo* foo = bar; But it didn't have Walter's support either (and I understand why). I'd suggest you to state you ideas as simple and keep your posts as small as possible (trust me, few people like reading long posts). You could also ask someone to check your post before submitting - this will increase the probability of your message to be read and understood. P.S. Не унывай! :)
Jan 09 2009
prev sibling next sibling parent Bill Baxter <wbaxter gmail.com> writes:
2009/1/11 Christopher Wright <dhasenan gmail.com>:
 Weed wrote:
 Denis Koroskin =D0=C9=DB=C5=D4:

 I'd suggest you to state you ideas as simple and keep your posts as
 small as possible (trust me, few people like reading long posts).

The extra-short formulation of my idea: Objects should be divided on POD (struct) and non-POD (class). Instead of such division as now: POD && value type (struct) and POD && reference type (class).

The reference versus value type difference is just a matter of defaults. Returning a class instance on the stack from a function is possible with inout parameters, though you can't use a constructor in that case: void main () { scope MyClass obj =3D new MyClass; foo (obj); } void foo (inout MyClass obj) { // initialize obj somehow }

I don't think that does what you think it does. Remember that obj is basically a pointer under the hood. So what that 'inout' does is allow you to make obj point to something else. You don't need the 'inout' if all you want to do is change the contents of what obj points to. I think what 'scope MyClass obj' actually does is reserve the appropriate amount of space on the stack somewhere, then make obj point to that space instead of the heap. So that means you can still reassign obj if you feel like it. And it can be reassigned to either a scope or non-scope instance. It doesn't matter to obj because obj is just a pointer. --bb
Jan 10 2009
prev sibling next sibling parent Bill Baxter <wbaxter gmail.com> writes:
2009/1/11 Christopher Wright <dhasenan gmail.com>:
 Instead of such division as now: POD && value type (struct) and POD &&
 reference type (class).

The reference versus value type difference is just a matter of defaults.

No it's not. scope MyClass does not make a MyClass that works like a value type. It merely makes a MyClass instance that's allocated on the stack. The semantics of it otherwise is identical to a regular class. Another difference is that class instances can only be 'scope' in functions. A use case that's missing is: class MyClass { scope OtherClass foo; } This would embed the memory for 'foo' right inside MyClass, so that scope x = new MyClass; Would not involve any heap allocations. Currently if you have a class inside a class, there's no way to use 'scope' to avoid the heap allocation on the contained class. Also you could imagine scope arrays. MyClass foo = new scope MyClass[10]; Where this would do one heap allocation, not 10. There is a problem with this though, mentioned below, precisely because scope MyClass does *not* have value semantics.
 Returning a class instance on the stack from a function is possible with
 inout parameters, though you can't use a constructor in that case:
 void main ()
 {
        scope MyClass obj = new MyClass;
        foo (obj);
 }

 void foo (inout MyClass obj)
 {
        // initialize obj somehow
 }

To further follow up on this, what you are showing is *not* returning a class instance on the stack via inout. You are just modifying a pre-existing instance. Returning a scope instance via inout would look like this: void main() { MyClass obj; foo(obj); } void foo(inout MyClass obj) { scope tmp = new MyClass; obj = tmp; } And that will crash because it refers to memory allocated on the stack. What is impossible with D right now is to make a value copy of a class (short of getting hackish with memcpy). But since classes can be polymorphic, value copying gets you into slicing problems. That's why value copying is disabled to begin with. So disabling value copies is a good thing. And that's also the problem with putting scope'd things inside another class or an array. Since they don't have value semantics, there's no way to overwrite the memory that's there with a new version of the object. If you try to overwrite it you will instead just change the pointer. (This is what happens with scope objects in functions now). Assuming this worked: class MyClass { scope OtherClass foo; } scope x = new MyClass; x.foo = new OtherClass; You haven't reused the memory that foo originally occupied. You've instead made foo point to the heap. And now you have a chunk of your class that stored the original foo that's dead, useless and uncollectable. So the conclusion is that scope without value semantics is of somewhat limited use. Or at least in the case of embedded scope objects, they should best be considered kind of like 'final', only for cases where you're never going to rebind the reference to something else. --bb
Jan 10 2009
prev sibling next sibling parent Bill Baxter <wbaxter gmail.com> writes:
2009/1/11 Weed <resume755 mail.ru>:
 Bill Baxter =D0=C9=DB=C5=D4:

 But since classes can be polymorphic, value copying gets you into
 slicing problems.  That's why value copying is disabled to begin with.
  So disabling value copies is a good thing.

It is not always a good thing.

Yeh, I just mean there is some merit in disabling value copies. But I don't rule out the possibility that there may be an even better way that banning them altogether.
 I propose to prohibit only the copying by value of the base type to
 derivative type

Ok, this is key. How do you propose to do this? In general it requires a runtime check, I think. And I think you need to say that you prohibit copying unless typeA=3D=3DtypeB exactly. If you allow copying either way between base and derived you are asking for trouble. But still given Base x =3D get_one(); Base y =3D get_another(); *x =3D *y; // presumed value copy syntax you have no way in general to know that x and y are really both a Base at compile time. So you must have a run-time check there. Perhaps it could be omitted for -release builds, though.
 And that's also the problem with putting scope'd things inside another
 class or an array.  Since they don't have value semantics,

Yes, this is what I mean

So assuming you had this, the important question is what would you do with = it? You still have the problem that the current system works pretty well. And has a lot of history. So you need a very compelling use case to convince Walter that something should change. --bb
Jan 10 2009
prev sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Sun, 11 Jan 2009 05:04:11 +0300, Weed <resume755 mail.ru> wrote:

 Bill Baxter пишет:
 2009/1/11 Weed <resume755 mail.ru>:
 Bill Baxter пишет:

 But since classes can be polymorphic, value copying gets you into
 slicing problems.  That's why value copying is disabled to begin with.
  So disabling value copies is a good thing.


Yeh, I just mean there is some merit in disabling value copies. But I don't rule out the possibility that there may be an even better way that banning them altogether.
 I propose to prohibit only the copying by value of the base type to
 derivative type

Ok, this is key. How do you propose to do this? In general it requires a runtime check, I think. And I think you need to say that you prohibit copying unless typeA==typeB exactly. If you allow copying either way between base and derived you are asking for trouble. But still given Base x = get_one(); Base y = get_another(); *x = *y; // presumed value copy syntax you have no way in general to know that x and y are really both a Base at compile time. So you must have a run-time check there. Perhaps it could be omitted for -release builds, though.

It can lead to a difficult and non-reproduceable errors than old C++-style splitting. It is possible to try to prohibit assignment of the dereferenced pointers? Simply to prohibit assignment too it is possible, essentially it changes nothing.

Err.. I don't get what you say. The *x = *y is just one of the possible syntaxes, nothing else.
 And that's also the problem with putting scope'd things inside another
 class or an array.  Since they don't have value semantics,


So assuming you had this, the important question is what would you do with it?

The most difficult. My arguments: 1. Problem of a choice of correct type for the object. A mathematical matrix - a classical example. A class it should be or structure? My offer without serious consequences allows to move solution of this problem from a design stage to a programming stage - is it will be simple by replacement keyword class to struct.

Having the same syntax for both classes and struct is a nice goal, I agree. But it should be taken as a different issue and solved separately, too.
 2. Performance increases. It is not necessary to allocate at the
 slightest pretext memory in a heap.


 3. I offer syntax which presumably does not break an existing code.
 + On how many I understand, in the existing compiler all necessary for
 implementation already is, a problem only in syntax addition.


 4. Java and C# also uses objects by reference? But both these of
 language are interpreted. I assume that the interpreter generally with
 identical speed allocates memory in a heap and in a stack, therefore
 authors of these languages and used reference model.

Neither of these languages are interpreted, they both are compiled into native code at runtime.
 D is compiled language and to borrow reference model incorrectly. In D
 the programmer should have possibility most to decide where to place  
 object.


 You still have the problem that the current system works pretty well.
 And has a lot of history.  So you need a very compelling use case to
 convince Walter that something should change.

Thus, actually Java and C# systems works pretty well. And they has a lot of history. But not D.

DMD 0.001 was released 7 years ago. Long enough, I think.
 This situation reminds me history with the Hungarian notation:
 Many years ago many developers have started to deliver interfaces and to
 sell books where it was used. This very correct invention affirmed as
 many books that and only in ~5 years there were articles with the
 materials specifying in groundlessness of usage of this notation. Till
 this time many entered it into corporate standards and suffered, though,
 I am sure, they were visited by thoughts about irrelevance of the
 given notation except for special cases.

Jan 10 2009
parent Bill Baxter <wbaxter gmail.com> writes:
On Tue, Jan 13, 2009 at 10:17 AM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 Bill Baxter wrote:
 So it may be worth while to have a special kind construct for
 containing data that the compiler is free to move around.  This type
 would have a hidden pointer inside of it that can be moved around by
 the gc, but applications would not be allowed to access that pointer.
  And I suppose that means all access to the data would given via
 lvalue only.   Probably wouldn't take much on the part of the GC to
 provide the necessary hooks.  Just some sort of "relocatable alloc"
 call.  Rest could probably be handled in higher level libs.

Interesting. (Link?) Structs in D are supposed to be location transparent (it is unclear to me to what extent this should be enforced vs. just assumed), so if the compiler can show the address of a struct is not taken, it should be free to move it around.

The message doesn't appear to have come up on the list archive yet, but here's the full text: """
 Third solution that was popular is to make your data completely

One nice thing I've found is that only some of your data needs to be like this to get the full benefit, because it can move around to fill in the gaps left by all the other fixed-position parts. Especially helpful on consoles, where most of your data is stuff like sound, texture and geometry buffers, which are all trivially relocatable. TomF. """ (TomF is Tom Forsyth, been in the game biz for a long time, now at Intel working on Larabee) --bb
Jan 12 2009
prev sibling next sibling parent Brad Roberts <braddr puremagic.com> writes:
Andrei Alexandrescu wrote:
 Weed wrote:
 Weed пишет:

 4. Java and C# also uses objects by reference? But both these of
 language are interpreted. I assume that the interpreter generally with
 identical speed allocates memory in a heap and in a stack, therefore
 authors of these languages and used reference model.

native code at runtime.

JIT-compilation.

I guess allocation in Java occurs fast because of usage of the its own memory manager. I do not know how it is fair, but: http://www.ibm.com/developerworks/java/library/j-jtp09275.html "Pop quiz: Which language boasts faster raw allocation performance, the Java language, or C/C++? The answer may surprise you -- allocation in modern JVMs is far faster than the best performing malloc implementations. The common code path for new Object() in HotSpot 1.4.2 and later is approximately 10 machine instructions (data provided by Sun; see Resources), whereas the best performing malloc implementations in C require on average between 60 and 100 instructions per call (Detlefs, et. al.; see Resources)."

Meh, that should be taken with a grain of salt. An allocator that only bumps a pointer will simply eat more memory and be less cache-friendly. Many applications aren't that thrilled with the costs of such a model. Andrei

Take it as nicely seasoned. The current jvm gc and memory subsystem is _extremely_ clever. However, it completely relies on the ability to move objects during garbage collection. If it was purely the allocator that behaved that way, you'd be right. But it's interaction with the gc is where the system comes together to form a useful whole. Later, Brad
Jan 11 2009
prev sibling next sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
2009/1/13 Weed <resume755 mail.ru>:
 Andrei Alexandrescu =D0=C9=DB=C5=D4:
 Brad Roberts wrote:
 Andrei Alexandrescu wrote:
 Weed wrote:
 Weed =D0=C9=DB=C5=D4:

 4. Java and C# also uses objects by reference? But both these of
 language are interpreted. I assume that the interpreter generally
 with
 identical speed allocates memory in a heap and in a stack, therefo=








 authors of these languages and used reference model.

into native code at runtime.

JIT-compilation.






 memory manager.
     I do not know how it is fair, but:
 http://www.ibm.com/developerworks/java/library/j-jtp09275.html

 "Pop quiz: Which language boasts faster raw allocation performance, t=





 Java language, or C/C++? The answer may surprise you -- allocation in
 modern JVMs is far faster than the best performing malloc
 implementations. The common code path for new Object() in HotSpot 1.4=





 and later is approximately 10 machine instructions (data provided by
 Sun; see Resources), whereas the best performing malloc implementatio=





 in C require on average between 60 and 100 instructions per call
 (Detlefs, et. al.; see Resources)."

bumps a pointer will simply eat more memory and be less cache-friendly=




 Many applications aren't that thrilled with the costs of such a model.

 Andrei

Take it as nicely seasoned. The current jvm gc and memory subsystem is _extremely_ clever. However, it completely relies on the ability to move objects during garbage collection. If it was purely the allocator that behaved that way, you'd be right. But it's interaction with the g=



 is where the system comes together to form a useful whole.

I understand. My point is that a 10-cycles-per-allocation allocator

10 *cycles* per allocation?
 will
 necessarily use more memory than one that attempts to reuse memory.
 There's no way around that. I mean we know what those cycles do :o).
 Some application don't work well with that. Escape analysis does reduce
 the number of cache-unfriendly patterns, but, as of today, not to the
 point the issue can be safely ignored.

 There's no contention that GC has made great progress lately, and that's
 a great thing.

In any case, we cannot add such memory manager in D. And such resource allocation does not approach us, and mandatory creation of objects in a heap does not approach for D.

I'm not and expert in garbage collection but... An interesting point was made yesterday on the GDAlgorithms mailing list. Acording to this one game industry veteran, you don't need a fully moving garbage collector to get many of the benefits. If *some* of the memory can be moved around then often that's enough to fill in the gaps and keep fragmentation down. So it may be worth while to have a special kind construct for containing data that the compiler is free to move around. This type would have a hidden pointer inside of it that can be moved around by the gc, but applications would not be allowed to access that pointer. And I suppose that means all access to the data would given via lvalue only. Probably wouldn't take much on the part of the GC to provide the necessary hooks. Just some sort of "relocatable alloc" call. Rest could probably be handled in higher level libs. But like I said I don't know much about GC. --bb
Jan 12 2009
parent John Reimer <terminal.node gmail.com> writes:
Hello Bill,

 2009/1/13 Weed <resume755 mail.ru>:
 
 Andrei Alexandrescu пишет:
 
 Brad Roberts wrote:
 
 Andrei Alexandrescu wrote:
 
 Weed wrote:
 
 Weed пишет:
 
 4. Java and C# also uses objects by reference? But both these
 of
 language are interpreted. I assume that the interpreter
 generally
 with
 identical speed allocates memory in a heap and in a stack,
 therefore
 authors of these languages and used reference model.

compiled into native code at runtime.

JIT-compilation.

its own memory manager. I do not know how it is fair, but: http://www.ibm.com/developerworks/java/library/j-jtp09275.html "Pop quiz: Which language boasts faster raw allocation performance, the Java language, or C/C++? The answer may surprise you -- allocation in modern JVMs is far faster than the best performing malloc implementations. The common code path for new Object() in HotSpot 1.4.2 and later is approximately 10 machine instructions (data provided by Sun; see Resources), whereas the best performing malloc implementations in C require on average between 60 and 100 instructions per call (Detlefs, et. al.; see Resources)."

only bumps a pointer will simply eat more memory and be less cache-friendly. Many applications aren't that thrilled with the costs of such a model. Andrei

subsystem is _extremely_ clever. However, it completely relies on the ability to move objects during garbage collection. If it was purely the allocator that behaved that way, you'd be right. But it's interaction with the gc is where the system comes together to form a useful whole.


 will
 necessarily use more memory than one that attempts to reuse memory.
 There's no way around that. I mean we know what those cycles do :o).
 Some application don't work well with that. Escape analysis does
 reduce
 the number of cache-unfriendly patterns, but, as of today, not to
 the
 point the issue can be safely ignored.
 There's no contention that GC has made great progress lately, and
 that's a great thing.
 

resource allocation does not approach us, and mandatory creation of objects in a heap does not approach for D.

An interesting point was made yesterday on the GDAlgorithms mailing list. Acording to this one game industry veteran, you don't need a fully moving garbage collector to get many of the benefits. If *some* of the memory can be moved around then often that's enough to fill in the gaps and keep fragmentation down. So it may be worth while to have a special kind construct for containing data that the compiler is free to move around. This type would have a hidden pointer inside of it that can be moved around by the gc, but applications would not be allowed to access that pointer. And I suppose that means all access to the data would given via lvalue only. Probably wouldn't take much on the part of the GC to provide the necessary hooks. Just some sort of "relocatable alloc" call. Rest could probably be handled in higher level libs. But like I said I don't know much about GC. --bb

Recently, I started looking into garbage collection theory. Of course, the first source I went to was Hans Boem's. Here's a couple of links that might be interesting: http://www.hpl.hp.com/personal/Hans_Boehm/gc/complexity.html http://www.hpl.hp.com/personal/Hans_Boehm/gc/conservative.html And a whole list of other articles here: http://www.hpl.hp.com/personal/Hans_Boehm/gc/ Lots of interesting material... and a fair bit of it is over my head. But, nonetheless, the fundamentals are there. I'm not sure if some of there studies are accurate, biased, or out-dated since I don't keep up with state-of-the-art gc research. But, it seems to me, that Hans is still one of the top experts in the field, so some of what's there must be very pertinant. -JJR
Jan 12 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Tue, 13 Jan 2009 04:17:22 +0300, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:

 Bill Baxter wrote:
 So it may be worth while to have a special kind construct for
 containing data that the compiler is free to move around.  This type
 would have a hidden pointer inside of it that can be moved around by
 the gc, but applications would not be allowed to access that pointer.
  And I suppose that means all access to the data would given via
 lvalue only.   Probably wouldn't take much on the part of the GC to
 provide the necessary hooks.  Just some sort of "relocatable alloc"
 call.  Rest could probably be handled in higher level libs.

Interesting. (Link?) Structs in D are supposed to be location transparent (it is unclear to me to what extent this should be enforced vs. just assumed), so if the compiler can show the address of a struct is not taken, it should be free to move it around. Andrei

If the compiler can show the address of a struct is not taken, it should recycle the memory :p
Jan 12 2009
prev sibling parent Brad Roberts <braddr puremagic.com> writes:
On Tue, 13 Jan 2009, Bill Baxter wrote:

 I'm not and expert in garbage collection but...
 An interesting point was made yesterday on the GDAlgorithms mailing
 list.  Acording to this one game industry veteran, you don't need a
 fully moving garbage collector to get many of the benefits.  If *some*
 of the memory can be moved around then often that's enough to fill in
 the gaps and keep fragmentation down.
 
 So it may be worth while to have a special kind construct for
 containing data that the compiler is free to move around.  This type
 would have a hidden pointer inside of it that can be moved around by
 the gc, but applications would not be allowed to access that pointer.
  And I suppose that means all access to the data would given via
 lvalue only.   Probably wouldn't take much on the part of the GC to
 provide the necessary hooks.  Just some sort of "relocatable alloc"
 call.  Rest could probably be handled in higher level libs.
 
 But like I said I don't know much about GC.
 
 --bb

And wouldn't ya know it.. I spent parts of saturday pondering on exactly how to implement exactly that. I've got most of the code pictured in my head, but none of it written. :) Maybe one day I'll make the time to try it. Later, Brad
Jan 12 2009