www.digitalmars.com         C & C++   DMDScript  

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

reply Don <nospam nospam.com> writes:
Weed wrote:
 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





 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 






 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





 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 






 structs (C). D takes the best of both worlds.

refuse structures in general. I suggest to allow to create classes on a





 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 





 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 





 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 





 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.

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



 division is not present (C++).

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.

You certainly can do something about it. By rearranging the expression, you can avoid the unnecessary creation of temporaries.
 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.

That's not what I meant. You can have an object which contains a pointer to heap-allocated data (eg, it could contain a dynamic array). Turning the object from a class into a struct does not remove the heap allocation.
 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).

My point is that you might hope to be able to avoid memory allocation entirely, putting the new value of c2 in-place, but case (a) and (b) make it impossible. There will always be ONE heap allocation.
 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.

OK, then you're getting into another issue...
 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.

I fail to see how D's distinction between classes and structs has any bearing on this. The optimisations you're talking about are only possible in the presence of polymorphism, in cases where you can prove that polymorphism is not being used! I suspect that if you're encountering this issue in speed-critical code, there's a problem with your design.
 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? ]:)

No, I think the design of objects in D is sound. I think operator overloading in both D and C++ is incomplete.
Dec 30 2008
parent reply Weed <resume755 mail.ru> writes:
Don :
 Weed wrote:
 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





 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






 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





 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






 structs (C). D takes the best of both worlds.

refuse structures in general. I suggest to allow to create classes on a





 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





 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





 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





 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.

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



 division is not present (C++).

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.

You certainly can do something about it. By rearranging the expression, you can avoid the unnecessary creation of temporaries.

I consider that it wrong and I quote myself: =========
 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. =========
 
 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.

That's not what I meant. You can have an object which contains a pointer to heap-allocated data (eg, it could contain a dynamic array). Turning the object from a class into a struct does not remove the heap allocation.

And still the object can to access, for example, to a file system in the constructor. :) But if the programmer has written object so that it was fast (did not use call to a heap) that all will be ok.
 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).

My point is that you might hope to be able to avoid memory allocation entirely, putting the new value of c2 in-place, but case (a) and (b) make it impossible. There will always be ONE heap allocation.

If class are stored in a stack that there will be no.
 
 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.

OK, then you're getting into another issue...
 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.

I fail to see how D's distinction between classes and structs has any bearing on this. The optimisations you're talking about are only possible in the presence of polymorphism, in cases where you can prove that polymorphism is not being used! I suspect that if you're encountering this issue in speed-critical code, there's a problem with your design.

Quite right, if polymorphism I would not be necessary to me did not use classes, it is obvious. All the rest that you have told it the common words so experts in marketing speak. :) I give concrete examples from a life where D is bad. You see a design problem in the code resulted above? By the way, one of serious problem: if necessary it is difficult enough to alter a class in structure and vice versa. Insufficiently simply to change the keyword. And it is serious, it is not necessary to me to tell about planning.
 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? ]:)

No, I think the design of objects in D is sound. I think operator overloading in both D and C++ is incomplete.

It simply sugar, it is not necessary to forget
Dec 30 2008
parent reply Don <nospam nospam.com> writes:
Weed wrote:
 Don :
 Weed wrote:
 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





 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






 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





 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






 structs (C). D takes the best of both worlds.

refuse structures in general. I suggest to allow to create classes on a





 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





 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





 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





 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.

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



 division is not present (C++).

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.

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.

you can avoid the unnecessary creation of temporaries.

I consider that it wrong and I quote myself: =========
 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. =========
 Sure, in
 the simple case you mentioned, using a struct works because the size of
 the data is small.


to heap-allocated data (eg, it could contain a dynamic array). Turning the object from a class into a struct does not remove the heap allocation.

And still the object can to access, for example, to a file system in the constructor. :) But if the programmer has written object so that it was fast (did not use call to a heap) that all will be ok.
 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;

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


entirely, putting the new value of c2 in-place, but case (a) and (b) make it impossible. There will always be ONE heap allocation.

If class are stored in a stack that there will be no.
 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.

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.

bearing on this. The optimisations you're talking about are only possible in the presence of polymorphism, in cases where you can prove that polymorphism is not being used! I suspect that if you're encountering this issue in speed-critical code, there's a problem with your design.

Quite right, if polymorphism I would not be necessary to me did not use classes, it is obvious. All the rest that you have told it the common words so experts in marketing speak. :) I give concrete examples from a life where D is bad. You see a design problem in the code resulted above?

If the overhead caused by using classes is unacceptable, then yes. I'd want to see the definition of "checkpoint". If it's really polymorphic, how can it be stored on the stack? This doesn't make sense to me.
 By the way, one of serious problem: if necessary it is difficult enough
 to alter a class in structure and vice versa. Insufficiently simply to
 change the keyword. And it is serious, it is not necessary to me to tell
 about planning.
 
 
 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.

optimization about that that the design of objects in D is wrong? ]:)

overloading in both D and C++ is incomplete.

It simply sugar, it is not necessary to forget

Dec 30 2008
parent Weed <resume755 mail.ru> writes:
Don :

 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.

bearing on this. The optimisations you're talking about are only possible in the presence of polymorphism, in cases where you can prove that polymorphism is not being used! I suspect that if you're encountering this issue in speed-critical code, there's a problem with your design.

Quite right, if polymorphism I would not be necessary to me did not use classes, it is obvious. All the rest that you have told it the common words so experts in marketing speak. :) I give concrete examples from a life where D is bad. You see a design problem in the code resulted above?

If the overhead caused by using classes is unacceptable, then yes. I'd want to see the definition of "checkpoint". If it's really polymorphic, how can it be stored on the stack? This doesn't make sense to me.

Where a problem?
 
 By the way, one of serious problem: if necessary it is difficult enough
 to alter a class in structure and vice versa. Insufficiently simply to
 change the keyword. And it is serious, it is not necessary to me to tell
 about planning.


Dec 30 2008