www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Old problem with performance

reply Weed <resume755 mail.ru> writes:
(Has started here:
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81359)

To me still does not give rest performance of classes (in comparison
with C++ or with D structs)

I still think that it is a serious problem.

Colleagues from our national D forum have asked to show it and I have
written simple examples on D. I want to share with you too them.

On my computer the code with structure (i.e. object by value) runs in 6
times faster than a code with a class:

$ time ./struct

real    0m8.515s
user    0m7.796s
sys     0m0.016s
$ time ./class

real    0m52.185s
user    0m40.543s
sys     0m0.076s

The code on C++ is also approximately in 6 times faster a code with
classes on D. (I do not give an example on C++ because classes on C++
work just as structures in D.)

I think with it it is necessary to do something.


Examples code:

//========================
struct C {
    int i;
    real[5] unused; // to prevent returning this object in registers

    C opAdd( C src ) {
        C ret;
        ret.i = i + src.i;
        return ret;
    }
}

int main() {
    C c1;
    C c2;

    // initialise i by "random" value to prevent compile-time calculation
    c1.i = cast(int)&c1;
    c2.i = 0;

    for(int i = 0; i < 50_000_000; ++i)
        c2 = c1 + c1 + c1;

    return c2.i;
}

//========================

class C {
    int i;
    real[5] unused; // to prevent returning this object in registers

    C opAdd( C src ) {
        auto ret = new C;
        ret.i = i + src.i;
        return ret;
    }
}

int main() {
    auto c1 = new C;
    auto c2 = new C;

    // initialise i by "random" value to prevent compile-time calculation
    c1.i = cast(int)&c1;
    c2.i = 0;

    for(int i = 0; i < 50_000_000; ++i)
        c2 = c1 + c1 + c1;

    return c2.i;
}
//========================
Feb 07 2009
next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Weed wrote:
 [snip]

If I had to take a guess, I'd say that it's six times slower because you're performing 100 million allocations. You aren't benchmarking class/struct overhead, you're benchmarking the overhead of doing 100 million allocations. You're comparing apples and heffalumps. -- Daniel
Feb 08 2009
next sibling parent reply Weed <resume755 mail.ru> writes:
Daniel Keep пишет:
 Weed wrote:
 [snip]

If I had to take a guess, I'd say that it's six times slower because you're performing 100 million allocations. You aren't benchmarking class/struct overhead, you're benchmarking the overhead of doing 100 million allocations. You're comparing apples and heffalumps.

Yes, but problem is that D does not leave way to create a class instance without allocation
Feb 08 2009
next sibling parent Weed <resume755 mail.ru> writes:
Weed пишет:
 Daniel Keep пишет:
 Weed wrote:
 [snip]

you're performing 100 million allocations. You aren't benchmarking class/struct overhead, you're benchmarking the overhead of doing 100 million allocations. You're comparing apples and heffalumps.

Yes, but problem is that D does not leave way to create a class instance without allocation

I should explain that I compare not only structs and classes in D but also classes in C++, there classes work as fast as well as structures in case of usage of overload of operators.
Feb 08 2009
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Weed wrote:
 Daniel Keep пишет:
 Weed wrote:
 [snip]

you're performing 100 million allocations. You aren't benchmarking class/struct overhead, you're benchmarking the overhead of doing 100 million allocations. You're comparing apples and heffalumps.

Yes, but problem is that D does not leave way to create a class instance without allocation

You can allocate a class on the stack with: scope c = new C();
Feb 08 2009
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Walter Bright wrote:
 Weed wrote:
 Daniel Keep п©п╦я┬п╣я┌:
 Weed wrote:
 [snip]

you're performing 100 million allocations. You aren't benchmarking class/struct overhead, you're benchmarking the overhead of doing 100 million allocations. You're comparing apples and heffalumps.

Yes, but problem is that D does not leave way to create a class instance without allocation

You can allocate a class on the stack with: scope c = new C();

Which helps (a bit) with the two instances allocated in main(), but is rather unhelpful with the 100_000_000 allocated in opAdd() (they're returned)...
Feb 08 2009
next sibling parent Sean Kelly <sean invisibleduck.org> writes:
Frits van Bommel wrote:
 Walter Bright wrote:
 Weed wrote:
 Daniel Keep п©п╦я┬п╣я┌:
 Weed wrote:
 [snip]

you're performing 100 million allocations. You aren't benchmarking class/struct overhead, you're benchmarking the overhead of doing 100 million allocations. You're comparing apples and heffalumps.

Yes, but problem is that D does not leave way to create a class instance without allocation

You can allocate a class on the stack with: scope c = new C();

Which helps (a bit) with the two instances allocated in main(), but is rather unhelpful with the 100_000_000 allocated in opAdd() (they're returned)...

It's kind of a lame solution, but if the return value is const then the class could just hold a reference to an internal value it changes as needed. Sean
Feb 08 2009
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Frits van Bommel wrote:
 Which helps (a bit) with the two instances allocated in main(), but is 
 rather unhelpful with the 100_000_000 allocated in opAdd() (they're 
 returned)...

The use of classes in this example is like using a screwdriver as a hammer. It'll work in a pinch, but a hammer works a lot better. If you're allocating 100_000_000 classes in a tight loop, some refactoring looks to be in order. In particular, classes are *meant* to be used as reference types, but the program is trying to treat them as value types. Virtual functions are orthogonal to what value types are - a continuing problem C++ programs have is conflating value types with reference types.
Feb 08 2009
next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Sun, 08 Feb 2009 11:13:00 -0800, Walter Bright wrote:


 In particular, classes are *meant* to be used as reference types, but 
 the program is trying to treat them as value types. Virtual functions 
 are orthogonal to what value types are - a continuing problem C++ 
 programs have is conflating value types with reference types.

In D, what is the recommend technique to derive one user-defined value type from another? -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Feb 08 2009
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 On Sun, 08 Feb 2009 11:13:00 -0800, Walter Bright wrote:
 
 
 In particular, classes are *meant* to be used as reference types, but 
 the program is trying to treat them as value types. Virtual functions 
 are orthogonal to what value types are - a continuing problem C++ 
 programs have is conflating value types with reference types.

In D, what is the recommend technique to derive one user-defined value type from another?

There isn't one. Deriving types only makes sense when expecting polymorphic behavior, which is completely orthogonal to the whole idea of a value type. A polymorphic type should be a reference type. A value type can have *aggregation*, however, as in: struct Sbase { ... base members ... } struct S { Sbase base; ... more members ... } You can also embed interfaces in a struct: interface I { ... } struct S { I i; }
Feb 08 2009
next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Sun, 08 Feb 2009 14:37:18 -0800, Walter Bright wrote:

 Derek Parnell wrote:
 On Sun, 08 Feb 2009 11:13:00 -0800, Walter Bright wrote:
 
 In particular, classes are *meant* to be used as reference types, but 
 the program is trying to treat them as value types. Virtual functions 
 are orthogonal to what value types are - a continuing problem C++ 
 programs have is conflating value types with reference types.

In D, what is the recommend technique to derive one user-defined value type from another?

There isn't one. Deriving types only makes sense when expecting polymorphic behavior, which is completely orthogonal to the whole idea of a value type. A polymorphic type should be a reference type.

Thank you. I was using "derive" in the English language sense rather than the O-O sense, but I get your point - for example a 'currency' value type is only ever to be used as a currency value and not any of its base value types. Fair enough.
 A value type can have *aggregation*, however, as in:
 
 struct Sbase
 {
      ... base members ...
 }
 
 struct S
 {
      Sbase base;
      ... more members ...
 }

 You can also embed interfaces in a struct:
 
 interface I { ... }
 struct S
 {
      I i;
 }

This is what I thought too. In D, a value type can be derived from another value type by using a combination of data aggregation (data inheritance if you will) and interface aggregation (method inheritance?). I feel that D could do with a little more syntax support for derived value types though, because at times it is a bit awkward and/or verbose. I'll dig up some examples of what I mean. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Feb 08 2009
parent dsimcha <dsimcha yahoo.com> writes:
== Quote from Derek Parnell (derek psych.ward)'s article
 This is what I thought too. In D, a value type can be derived from another
 value type by using a combination of data aggregation (data inheritance if
 you will) and interface aggregation (method inheritance?).
 I feel that D could do with a little more syntax support for derived value
 types though, because at times it is a bit awkward and/or verbose. I'll dig
 up some examples of what I mean.

In D2, you could try aggregation and opDot() w/ ref return. See http://digitalmars.com/d/2.0/operatoroverloading.html#Dot . That pretty much simulates a form of inheritance w/o polymorphism. struct Foo { int i; void doStuff() { // Do stuff. } } struct Bar { private Foo foo; ref Foo opDot() { return foo; } } void main() { Bar bar; bar.doStuff(); // Calls Bar.foo.doStuff(). } One caveat, though, is that operator overloading behaves weirdly w/ this. See bug 2327: http://d.puremagic.com/issues/show_bug.cgi?id=2327
Feb 08 2009
prev sibling parent Chad J <gamerchad __spam.is.bad__gmail.com> writes:
Walter Bright wrote:
 Derek Parnell wrote:
 On Sun, 08 Feb 2009 11:13:00 -0800, Walter Bright wrote:


 In particular, classes are *meant* to be used as reference types, but
 the program is trying to treat them as value types. Virtual functions
 are orthogonal to what value types are - a continuing problem C++
 programs have is conflating value types with reference types.

In D, what is the recommend technique to derive one user-defined value type from another?

There isn't one. Deriving types only makes sense when expecting polymorphic behavior, which is completely orthogonal to the whole idea of a value type. A polymorphic type should be a reference type.

So far I am seeing that the slicing problem arises from the ability to place extra data into derived types. But what of method inheritance? It seems that something like this could work: scope class A { int x, y; int foo() { etc... } } class B : A { // More data members are now forbidden. int foo() { etc... } void bar(char[] baz) {...} } struct S { // in-place allocation scope A qux; scope A quux; } void main() { S s; s.qux = new B; }
Feb 08 2009
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Derek Parnell wrote:
 On Sun, 08 Feb 2009 11:13:00 -0800, Walter Bright wrote:
 
 
 In particular, classes are *meant* to be used as reference types, but 
 the program is trying to treat them as value types. Virtual functions 
 are orthogonal to what value types are - a continuing problem C++ 
 programs have is conflating value types with reference types.

In D, what is the recommend technique to derive one user-defined value type from another?

Go to the future and use the alias this thingie. Andrei
Feb 08 2009
prev sibling parent reply Weed <resume755 mail.ru> writes:
Walter Bright пишет:
 Frits van Bommel wrote:
 Which helps (a bit) with the two instances allocated in main(), but is
 rather unhelpful with the 100_000_000 allocated in opAdd() (they're
 returned)...

The use of classes in this example is like using a screwdriver as a hammer. It'll work in a pinch, but a hammer works a lot better. If you're allocating 100_000_000 classes in a tight loop, some refactoring looks to be in order. In particular, classes are *meant* to be used as reference types, but the program is trying to treat them as value types.

But without such types the bulky code turns out (examples in this branch already presented)
 Virtual functions
 are orthogonal to what value types are - a continuing problem C++
 programs have is conflating value types with reference types.

The compiler cannot optimize such code? No We have virtual machine for fast allocations of memory at such heavy using of objects? No Perfectly - it is necessary to leave this question to the programmer: the class will be stored in a stack or in a heap is better will solve the programmer instead of the compiler. It is impossible to lose to an C++ in what.
Feb 08 2009
parent Weed <resume755 mail.ru> writes:
Adam D. Ruppe пишет:
 On Mon, Feb 09, 2009 at 09:59:50AM +0700, Weed wrote:
 Perfectly - it is necessary to leave this question to the programmer:
 the class will be stored in a stack or in a heap is better will solve
 the programmer instead of the compiler.

scope class does just that.

No, D do not allow return scope value.
 You can also put a custom allocator in your
 class and do whatever you want.
 

Often you add custom allocator in the classes who uses overload? You begin to do it after that talk? :)
Feb 08 2009
prev sibling next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
Daniel Keep wrote:
 Weed wrote:
 [snip]

If I had to take a guess, I'd say that it's six times slower because you're performing 100 million allocations. You aren't benchmarking class/struct overhead, you're benchmarking the overhead of doing 100 million allocations. You're comparing apples and heffalumps. -- Daniel

I'm curious, What is a heffalump?
Feb 08 2009
parent BCS <none anon.com> writes:
Hello Yigal,

 I'm curious, What is a heffalump?
 

http://en.wikipedia.org/wiki/Heffalump
Feb 08 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Mon, 09 Feb 2009 01:24:34 +0300, Derek Parnell <derek psych.ward> wrote:

 On Sun, 08 Feb 2009 11:13:00 -0800, Walter Bright wrote:


 In particular, classes are *meant* to be used as reference types, but
 the program is trying to treat them as value types. Virtual functions
 are orthogonal to what value types are - a continuing problem C++
 programs have is conflating value types with reference types.

In D, what is the recommend technique to derive one user-defined value type from another?

A mixin, perhaps: struct Brush { // ... } struct ColoredBrush { mixin Brush; Color color; }
Feb 08 2009
prev sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Mon, Feb 09, 2009 at 09:59:50AM +0700, Weed wrote:
 Perfectly - it is necessary to leave this question to the programmer:
 the class will be stored in a stack or in a heap is better will solve
 the programmer instead of the compiler.

scope class does just that. You can also put a custom allocator in your class and do whatever you want. -- Adam D. Ruppe http://arsdnet.net
Feb 08 2009
prev sibling next sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Sun, 08 Feb 2009 10:26:37 +0300, Weed <resume755 mail.ru> wrote:

 (Has started here:
 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81359)

 To me still does not give rest performance of classes (in comparison
 with C++ or with D structs)

 I still think that it is a serious problem.

 Colleagues from our national D forum have asked to show it and I have
 written simple examples on D. I want to share with you too them.

 On my computer the code with structure (i.e. object by value) runs in 6
 times faster than a code with a class:

 $ time ./struct

 real    0m8.515s
 user    0m7.796s
 sys     0m0.016s
 $ time ./class

 real    0m52.185s
 user    0m40.543s
 sys     0m0.076s

 The code on C++ is also approximately in 6 times faster a code with
 classes on D. (I do not give an example on C++ because classes on C++
 work just as structures in D.)

 I think with it it is necessary to do something.


 Examples code:

 //========================
 struct C {
     int i;
     real[5] unused; // to prevent returning this object in registers

     C opAdd( C src ) {
         C ret;
         ret.i = i + src.i;
         return ret;
     }
 }

 int main() {
     C c1;
     C c2;

     // initialise i by "random" value to prevent compile-time calculation
     c1.i = cast(int)&c1;
     c2.i = 0;

     for(int i = 0; i < 50_000_000; ++i)
         c2 = c1 + c1 + c1;

     return c2.i;
 }

 //========================

 class C {
     int i;
     real[5] unused; // to prevent returning this object in registers

     C opAdd( C src ) {
         auto ret = new C;
         ret.i = i + src.i;
         return ret;
     }
 }

 int main() {
     auto c1 = new C;
     auto c2 = new C;

     // initialise i by "random" value to prevent compile-time calculation
     c1.i = cast(int)&c1;
     c2.i = 0;

     for(int i = 0; i < 50_000_000; ++i)
         c2 = c1 + c1 + c1;

     return c2.i;
 }
 //========================

Your C objects have value semantics. For value semantics you should use value types. Alternatively you may want to use the following trick (I used it for lazy string concatenation in C+): class C { int value; this(int dim)(ref Sum!(dim) sum) { value = sum.value; } Sum!(2) opAdd(C other) { return Sum!(2)(this, other); } } struct Sum(int dimension) { private C[dimension] objects; this(C first, C second) { objects[0] = first; objects[1] = second; } this(int dim)(ref Sum!(dim) sum1, ref Sum!(dimension-dim) sum2) { objects[0..dim][] = sum1.objects[]; objects[dim+1..dimension] = sum2.objects[]; } this(ref Sum!(dimension-1) sum, C obj) { objects[0..$-1][] = sum.objects[]; objects[$-1] = obj; } Sum!(dimension+1) opAdd(C other) { return Sum!(dimension+1)(this, other); } Sum!(dimension+otherDimension) opAdd(otherDimension)(Sum!(otherDimension) other) { return Sum!(dimension+otherDimension)(this, other); } int value() { int value = 0; foreach (C c; objects) { value += c.value; } return value; } } Apparently, it doesn't work at the moment because ctor can't be a template :( Is there an enhancement request in bugzilla? It prevents this pattern from working.
Feb 08 2009
next sibling parent reply Weed <resume755 mail.ru> writes:
Denis Koroskin пишет:

 Your C objects have value semantics. For value semantics you should use value
types.

At present in D is not contain support of value types for objects. (I consider that it is necessary)
 Alternatively you may want to use the following trick (I used it for lazy
string concatenation in C+):

[skip]
 Apparently, it doesn't work at the moment because ctor can't be a
 template :(
 Is there an enhancement request in bugzilla? It prevents this pattern
 from working.

+ it is difficult and also it will be even more difficult if it will be necessary to make support for the construction like: 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. (from http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81432)
Feb 08 2009
parent reply Weed <resume755 mail.ru> writes:
Denis Koroskin пишет:

 [skip]

 Apparently, it doesn't work at the moment because ctor can't be a
 template :(
 Is there an enhancement request in bugzilla? It prevents this pattern
 from working.

+ it is difficult and also it will be even more difficult if it will be necessary to make support for the construction like: 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. (from http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81432)

I believe you should stick with structs for that.

Let's assume, polymorphism is necessary to these objects
Feb 08 2009
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-02-08 09:30:08 -0500, Weed <resume755 mail.ru> said:

 Let's assume, polymorphism is necessary to these objects

Polymorphism doesn't work very well while passing objects by value, even in C++. This is called the slicing problem. <http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c> I think D does a good job at avoiding that problem. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 08 2009
next sibling parent reply Rainer Deyke <rainerd eldwood.com> writes:
Michel Fortin wrote:
 Polymorphism doesn't work very well while passing objects by value, even
 in C++. This is called the slicing problem.

I have heard about the slicing problem. I know what it is. But in all my years of using C++ as my primary language, I have never actually encountered it. I don't believe it actually exists. -- Rainer Deyke - rainerd eldwood.com
Feb 08 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Rainer Deyke wrote:
 Michel Fortin wrote:
 Polymorphism doesn't work very well while passing objects by value, even
 in C++. This is called the slicing problem.

I have heard about the slicing problem. I know what it is. But in all my years of using C++ as my primary language, I have never actually encountered it. I don't believe it actually exists.

How long have you used C++? This is not a tendentious question. In the recent years, the advent of quality smart pointers, an increased scrutiny of use of inheritance, and the teaching of idioms associated with reference types has greatly diminished slicing accidents. But in the olden days, people were inheriting value types left and right because it was the emperor's new clothes. Andrei
Feb 08 2009
parent reply Rainer Deyke <rainerd eldwood.com> writes:
Andrei Alexandrescu wrote:
 How long have you used C++?

My first serious C++ project was started in 1997.
 This is not a tendentious question. In the recent years, the advent of
 quality smart pointers, an increased scrutiny of use of inheritance, and
 the teaching of idioms associated with reference types has greatly
 diminished slicing accidents. But in the olden days, people were
 inheriting value types left and right because it was the emperor's new
 clothes.

Slicing is caused by naive copying of polymorphic types, which is only tangentially related to inheriting value types. Example 1 (C++): struct point2d { int x, int y; }; struct point3d : public point2d { int z; } 'point2d' is a non-polymorphic value type. Slicing behavior exists, is intentional, and does not invalidate any invariants. Example 2 (C++): class data_source { public: virtual char read_byte() = 0; }; class input_file : public data_source { public: char read_byte() = 0; }; 'input_file' is a value type that implements an interface. No slicing problem exists because 'data_source' is an abstract type. Example 3 (C++): class person { public: person(person const& org) { assert(typeid(org) == typeid(*this)); } virtual ~person() {} }; class avatar : public person, public deity { }; 'person' and 'avatar' are value types that can be used polymorphically. In any context 'person&' may be either a reference to an instance of class 'person' or a polymorphic reference to an instance of any class that inherits from 'person'. Unfortunately the C++ type system does not distinguish between these two uses. It is an error (checked at runtime) to construct a new 'person' from a polymorphic reference, but any other use of 'person' as a value type or a polymorphic type is valid. No slicing problem exists except through user error. Example 4 (D): class RubberChicken { RubberChicken dup() { return new RubberChicken(); } } class RubberChickenWithAPulleyInTheMiddle { private Pulley pulley; // Forget to override 'dup'. } Here 'RubberChicken' is a reference type, but due to programmer error, the slicing problem still exists. -- Rainer Deyke - rainerd eldwood.com
Feb 08 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Rainer Deyke wrote:
 Example 3 (C++):
 
 class person {
 public:
   person(person const& org) {
     assert(typeid(org) == typeid(*this));
   }
   virtual ~person() {}
 };
 
 class avatar : public person, public deity {
 };
 
 'person' and 'avatar' are value types that can be used polymorphically.
  In any context 'person&' may be either a reference to an instance of
 class 'person' or a polymorphic reference to an instance of any class
 that inherits from 'person'.  Unfortunately the C++ type system does not
 distinguish between these two uses.  It is an error (checked at runtime)
 to construct a new 'person' from a polymorphic reference, but any other
 use of 'person' as a value type or a polymorphic type is valid.  No
 slicing problem exists except through user error.

The slicing problem exists in spades in this example, or better put its converse (your code will fire asserts when it shouldn't). The reason is rather subtle, so please try the code out to edify yourself. Andrei
Feb 08 2009
parent reply Rainer Deyke <rainerd eldwood.com> writes:
Andrei Alexandrescu wrote:
 The slicing problem exists in spades in this example, or better put its
 converse (your code will fire asserts when it shouldn't). The reason is
 rather subtle, so please try the code out to edify yourself.

You're referring to the automatically generated copy constructor in class 'avatar' which calls the copy constructor in class 'person', right? That's a bug in my silly untested example code, but it's not the slicing problem. Fix 1: class person { person(person const& org, bool allow_slicing = false) { assert(allow_slicing || typeid(org) == typeid(*this)); } }; class avatar : public person, public deity { avatar(avatar const& org, bool allow_slicing = false) : person(org, true) { } }; Fix 2: Trust the programmer to pay attention and remove the silly unnecessary assertion from class 'person'. -- Rainer Deyke - rainerd eldwood.com
Feb 08 2009
next sibling parent Christopher Wright <dhasenan gmail.com> writes:
Rainer Deyke wrote:
 Andrei Alexandrescu wrote:
 The slicing problem exists in spades in this example, or better put its
 converse (your code will fire asserts when it shouldn't). The reason is
 rather subtle, so please try the code out to edify yourself.

You're referring to the automatically generated copy constructor in class 'avatar' which calls the copy constructor in class 'person', right? That's a bug in my silly untested example code, but it's not the slicing problem.

It's also a problem whenever you use avatar by value. It's only by programmer discipline that you avoid the slicing problem.
Feb 09 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Rainer Deyke wrote:
 Andrei Alexandrescu wrote:
 The slicing problem exists in spades in this example, or better put its
 converse (your code will fire asserts when it shouldn't). The reason is
 rather subtle, so please try the code out to edify yourself.

You're referring to the automatically generated copy constructor in class 'avatar' which calls the copy constructor in class 'person', right? That's a bug in my silly untested example code, but it's not the slicing problem. Fix 1: class person { person(person const& org, bool allow_slicing = false) { assert(allow_slicing || typeid(org) == typeid(*this)); } }; class avatar : public person, public deity { avatar(avatar const& org, bool allow_slicing = false) : person(org, true) { } };

So the problem exists since you are explicitly addressing it.
 Fix 2:
 
 Trust the programmer to pay attention and remove the silly unnecessary
 assertion from class 'person'.
 
 

So the problem exists since you are trusting the programmer to avoid it. Andrei
Feb 09 2009
parent reply Rainer Deyke <rainerd eldwood.com> writes:
Andrei Alexandrescu wrote:
 So the problem exists since you are trusting the programmer to avoid it.

The slicing problem exists in the sense that it is possible for a bad programmer to accidentally slice an object. However: - This is not a problem with the language, but an avoidable programmer error in the language. - There are far more common programmer errors in C++ against which D does not guard. For example, dangling pointers to stack variables that have gone out of scope. - D's response to this perceived problem is far too heavy-handed, because it disallows useful correct code. - I would even say that the reference type classes in D lead to more problems. It is very easy for two objects in D to accidentally share internal state, something that is impossible with value types. - Even if the slicing problem were a major issue in C++, it should be possible to partially or totally fix it at the language level in D without sacrificing inheritance for value types. -- Rainer Deyke - rainerd eldwood.com
Feb 09 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
Rainer Deyke wrote:
 Andrei Alexandrescu wrote:
 So the problem exists since you are trusting the programmer to avoid it.

The slicing problem exists in the sense that it is possible for a bad programmer to accidentally slice an object. However: - This is not a problem with the language, but an avoidable programmer error in the language.

Yes. There are many examples of this: memory leaks, array bounds errors, invalid casts, that sort of thing. If you don't mind explicitly coding around these, stay with C or C++. You'll have these errors, but if you're careful and disciplined, it won't happen too often. It will, however, take time and attention to avoid these errors, and to debug them when you slip up. In some cases, a language change can eliminate a class of bugs. Sometimes it's worthwhile. In my experience, the cost of forbidding polymorphic value types is pretty much zero.
   - There are far more common programmer errors in C++ against which D
 does not guard.  For example, dangling pointers to stack variables that
 have gone out of scope.

Yep. Escape analysis is tricky, but I'd like to see it in D.
   - D's response to this perceived problem is far too heavy-handed,
 because it disallows useful correct code.

Well, D is a systems language, so you can still get the same effect, but it's sufficiently difficult and ugly that it is likely not worthwhile. On the other hand, you can probably simplify it to a single template mixin (see the thread on struct interfaces). Given that it is not a common need (as far as I can tell) and is an inherently unsafe thing, I believe that it is reasonable.
   - I would even say that the reference type classes in D lead to more
 problems.  It is very easy for two objects in D to accidentally share
 internal state, something that is impossible with value types.

struct A { int* i; } A a, b; int* i = new int; a.i = i; b.i = i; Hey look, they share internal state! What mechanism for sharing state is available to by-reference objects but not by-value objects?
Feb 11 2009
parent reply Rainer Deyke <rainerd eldwood.com> writes:
Christopher Wright wrote:
   - I would even say that the reference type classes in D lead to more
 problems.  It is very easy for two objects in D to accidentally share
 internal state, something that is impossible with value types.

struct A { int* i; } A a, b; int* i = new int; a.i = i; b.i = i; Hey look, they share internal state!

That's not accidental. You did that on purpose.
 What mechanism for sharing state is available to by-reference objects
 but not by-value objects?

struct C { T t; } C c1; C c2 = c1; If T was a reference type, 'c1' and 'c2' now share state, and it's up to the programmer to write code to prevent this. Moreover, the D language doesn't even give a hint as to whether T or a reference type or a value type. If T is a template parameter, it could be both. What I'd really like to see is a hybrid of struct and class that has the RAII and value semantics of a struct, but the polymorphism of classes. polymorphic_struct A { } polymorphic_struct B : A { } { B b; A a = b; // Creates a true non-sliced copy of 'b'. } // Destructors are called when the variables go out of scope. At the implementation level, the objects could be placed on the heap unless the compiler determines that this is not necessary. The goal is not performance, but consistent value semantics throughout the language. The alternative is to work backwards and create a wrapper for classes to give them value semantics: struct value_semantics(T) { this(T v) { this.value = v; } this(this) { this.value = this.value.dup; } ~this() { delete this.v; } T opDot() { return this; } T value; } -- Rainer Deyke - rainerd eldwood.com
Feb 12 2009
next sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Rainer Deyke wrote:
 What mechanism for sharing state is available to by-reference objects
 but not by-value objects?

struct C { T t; } C c1; C c2 = c1; If T was a reference type, 'c1' and 'c2' now share state, and it's up to the programmer to write code to prevent this. Moreover, the D language doesn't even give a hint as to whether T or a reference type or a value type. If T is a template parameter, it could be both.

Okay, reference types don't have value semantics. That's exactly what I would expect. Why is it a problem? Because there is no explicit builtin copying mechanism for classes? If there were such, would it be a problem if copying were not the default?
Feb 12 2009
parent reply Rainer Deyke <rainerd eldwood.com> writes:
Christopher Wright wrote:
 Rainer Deyke wrote:
 What mechanism for sharing state is available to by-reference objects
 but not by-value objects?

struct C { T t; } C c1; C c2 = c1; If T was a reference type, 'c1' and 'c2' now share state, and it's up to the programmer to write code to prevent this. Moreover, the D language doesn't even give a hint as to whether T or a reference type or a value type. If T is a template parameter, it could be both.

Okay, reference types don't have value semantics. That's exactly what I would expect.

That sounds tautological, but many newcomers to D are surprised by reference semantics when they expected value semantics.
 Why is it a problem?

Because reference types don't play well with RAII types. Because unintentional object aliasing a problem orders of magnitude more common than unintentional object slicing, and at least an order of magnitude harder to detect and fix. Because you can't correctly duplicate a multi-dimensional dynamic array in D by using the built-in 'dup' property. Because having two syntactically similar ways of creating user-defined types with different features sets and different semantics is confusing, especially if you later decide that you need a struct to be a class and all your copies turn into reference or vice versa.
 Because there is no explicit builtin
 copying mechanism for classes? If there were such, would it be a problem
 if copying were not the default?

What exactly do you have in mind? Would it allow classes to correctly work with RAII types? Would it prevent unintentional object aliasing? Would it allow multi-dimensional arrays to be correctly duplicated? Would it remove the need for both classes and structs in the language, or at least the need to switch between the two? -- Rainer Deyke - rainerd eldwood.com
Feb 12 2009
parent reply Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Rainer Deyke wrote:
 Because unintentional object aliasing a problem orders of magnitude more
 common than unintentional object slicing, and at least an order of
 magnitude harder to detect and fix.
 
 Because you can't correctly duplicate a multi-dimensional dynamic array
 in D by using the built-in 'dup' property.

Simply because shallow copies are the default. Usually you can't even have automatic deep copies. What if the array was supposed to be malloc'd or put in a contiguous memory block taken from a memory cache? A .dup that made a deep copy of it would introduce even a larger problem (since it would seem correct) than remembering that dups/struct copies are shallow by default and you need special code to do otherwise.
 Because having two syntactically similar ways of creating user-defined
 types with different features sets and different semantics is confusing,
  especially if you later decide that you need a struct to be a class and
 all your copies turn into reference or vice versa.
 [snip]
 Would it remove the need for both classes and structs in the language,

It's not a bug, it's a feature. What if I decide that I want my int to be a float? Suddenly I have to add all those NaN checks, change my scanf calls, inspect all divisions and places where rounding might happen, not to mention any inline asm or binary shifts... Let's unify floats and ints so this cannot happen. -- Tomasz Stachowiak http://h3.team0xf.com/ h3/h3r3tic on #D freenode
Feb 12 2009
parent reply Rainer Deyke <rainerd eldwood.com> writes:
Tom S wrote:
 Rainer Deyke wrote:
 Because you can't correctly duplicate a multi-dimensional dynamic array
 in D by using the built-in 'dup' property.

Simply because shallow copies are the default. Usually you can't even have automatic deep copies. What if the array was supposed to be malloc'd or put in a contiguous memory block taken from a memory cache? A .dup that made a deep copy of it would introduce even a larger problem (since it would seem correct) than remembering that dups/struct copies are shallow by default and you need special code to do otherwise.

C++ has a way of specifying how deep you want your copies to be at the declaration point: std::vector<boost::shared_ptr<std::vector<int> > > v1, v2; v2 = v1; // Shallow copy. std::vector<std::vector<int> > v3, v4; v4 = v3; // Deep copy. Granted, C++ is a mess and you could do something similar in D by wrapping the reference in a value_semantics wrapper. value_semantics!(value_semantics!(int[])[]) v5, v6; v6 = v5; // Deep copy due to the value_semantics templated struct. Unfortunately this doesn't solve the RAII problem: if value_semantics!(T) deletes the contained reference in its destructor, then it is no longer safe to embed a value_semantics!(T) variable in memory managed by the garbage collector.
 Would it remove the need for both classes and structs in the language,

It's not a bug, it's a feature.

Having both value types and reference types in the language is (arguably) a feature. Having no syntactic distinction between them at the point of use is at best a misfeature. Forcing programmers to change their value types into reference types when they need polymorphism or inheritance, even though safe polymorphic value types are possible, is clearly a misfeature.
 [...] change my scanf calls [...]

Are you serious? -- Rainer Deyke - rainerd eldwood.com
Feb 12 2009
parent Christopher Wright <dhasenan gmail.com> writes:
Rainer Deyke wrote:
 Unfortunately this doesn't solve the RAII problem: if
 value_semantics!(T) deletes the contained reference in its destructor,
 then it is no longer safe to embed a value_semantics!(T) variable in
 memory managed by the garbage collector.

You can take additional steps to ensure safety with notifyRegister, if the type is an object. It's a hole in the language that you can't get notified about the destruction of anything but an object. Maybe there is some way to do this via the runtime and the collector rather than the variable.
Feb 13 2009
prev sibling parent reply Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Rainer Deyke wrote:
 Christopher Wright wrote:
 What mechanism for sharing state is available to by-reference objects
 but not by-value objects?

struct C { T t; } C c1; C c2 = c1; If T was a reference type, 'c1' and 'c2' now share state, and it's up to the programmer to write code to prevent this.

No, they don't. Each instance of C has its own copy of the 't' reference.
 Moreover, the D language
 doesn't even give a hint as to whether T or a reference type or a value
 type.

Like you can't write "typedef X* T;" in C...
 If T is a template parameter, it could be both.

That's what IsExpression and static assertions are for. -- Tomasz Stachowiak http://h3.team0xf.com/ h3/h3r3tic on #D freenode
Feb 12 2009
parent reply Rainer Deyke <rainerd eldwood.com> writes:
Tom S wrote:
 Rainer Deyke wrote:
 If T was a reference type, 'c1' and 'c2' now share state, and it's up to
 the programmer to write code to prevent this.

No, they don't. Each instance of C has its own copy of the 't' reference.

That's like saying object slicing is an intentional feature. Given the intended semantics of 'C', it's a bug.
 Moreover, the D language
 doesn't even give a hint as to whether T or a reference type or a value
 type.

Like you can't write "typedef X* T;" in C...

The C programmer who does that should be shot. In C++ this idiom has some valid uses (iterators, template metaprogramming), but there remains an important distinction: the syntax for dealing with variables of type 'X*' is different than the syntax for dealing with variables of type 'X'. -- Rainer Deyke - rainerd eldwood.com
Feb 12 2009
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Rainer Deyke wrote:
 Tom S wrote:
 Rainer Deyke wrote:
 If T was a reference type, 'c1' and 'c2' now share state, and it's up to
 the programmer to write code to prevent this.


That's like saying object slicing is an intentional feature. Given the intended semantics of 'C', it's a bug.

It's not a bug. There are differences between value types and reference types. Just like how there are differences between atomic types and aggregate types. Or constant types and mutable types. If you want a distinct copy of something, then copy it. -- Daniel
Feb 12 2009
parent reply Rainer Deyke <rainerd eldwood.com> writes:
Daniel Keep wrote:
 It's not a bug.  There are differences between value types and reference
 types.  Just like how there are differences between atomic types and
 aggregate types.  Or constant types and mutable types.

This is a bug: struct MathematicalVector { this(int size) { this.values = new T[size]; } // No copy constructor. // Insert standard mathematical operators here. private T[] values; // Implementation detail. } I want MathematicalVector to be a value type (which is why I declared it as a struct). However, it doesn't behave a value type because I forgot to write the copy constructor. D doesn't cause the bug, but it certainly makes it easier to accidentally write this kind of bug. By contrast, the compiler-generated copy constructors in C++ usually do the right thing for all but a handful of low-level resource-management classes. Which is not to say that C++ doesn't have problems of its own - clearly it does, or I wouldn't be looking at D. -- Rainer Deyke - rainerd eldwood.com
Feb 12 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Rainer Deyke wrote:
 Daniel Keep wrote:
 It's not a bug.  There are differences between value types and reference
 types.  Just like how there are differences between atomic types and
 aggregate types.  Or constant types and mutable types.

This is a bug: struct MathematicalVector { this(int size) { this.values = new T[size]; } // No copy constructor. // Insert standard mathematical operators here. private T[] values; // Implementation detail. } I want MathematicalVector to be a value type (which is why I declared it as a struct). However, it doesn't behave a value type because I forgot to write the copy constructor. D doesn't cause the bug, but it certainly makes it easier to accidentally write this kind of bug. By contrast, the compiler-generated copy constructors in C++ usually do the right thing for all but a handful of low-level resource-management classes. Which is not to say that C++ doesn't have problems of its own - clearly it does, or I wouldn't be looking at D.

C++ ctors won't do the right thing if you use pointers, which is the moral equivalent of using T[] inside MathematicalVector. If you refer to std::vector instead, then that's a carefully-defined type that does have the right copy constructor defined. Andrei
Feb 12 2009
parent reply Rainer Deyke <rainerd eldwood.com> writes:
Andrei Alexandrescu wrote:
 C++ ctors won't do the right thing if you use pointers, which is the
 moral equivalent of using T[] inside MathematicalVector. If you refer to
 std::vector instead, then that's a carefully-defined type that does have
 the right copy constructor defined.

So where's the D equivalent of std::vector? Phobos has nothing. Tango has containers, but they are also reference types. I was under the impression that native arrays were intended to be used directly in D, something I rarely do in C++. Certainly neither of the standard libraries has any hesitation about passing unadorned dynamic arrays around. -- Rainer Deyke - rainerd eldwood.com
Feb 13 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Rainer Deyke wrote:
 Andrei Alexandrescu wrote:
 C++ ctors won't do the right thing if you use pointers, which is the
 moral equivalent of using T[] inside MathematicalVector. If you refer to
 std::vector instead, then that's a carefully-defined type that does have
 the right copy constructor defined.

So where's the D equivalent of std::vector? Phobos has nothing.

It will.
 I was under the impression that native arrays were intended to be used
 directly in D, something I rarely do in C++.  Certainly neither of the
 standard libraries has any hesitation about passing unadorned dynamic
 arrays around.

Phobos2 plans to consistently consider T[] a range, not a full-fledged array. A range corresponds to a pair of iterators in STL. Andrei
Feb 13 2009
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Denis Koroskin wrote:
 On Fri, 13 Feb 2009 17:24:54 +0300, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Rainer Deyke wrote:
 Andrei Alexandrescu wrote:
 C++ ctors won't do the right thing if you use pointers, which is the
 moral equivalent of using T[] inside MathematicalVector. If you 
 refer to
 std::vector instead, then that's a carefully-defined type that does 
 have
 the right copy constructor defined.


It will.
 I was under the impression that native arrays were intended to be used
 directly in D, something I rarely do in C++.  Certainly neither of the
 standard libraries has any hesitation about passing unadorned dynamic
 arrays around.

Phobos2 plans to consistently consider T[] a range, not a full-fledged array. A range corresponds to a pair of iterators in STL. Andrei

Will T[] get front, back, empty properties, and popFront(), popBack() methods?

Yes, not as methods, but as free functions.
 Perhaps, its the time T[] internal structure is changed!
 For example, ptr+size replaced by two pointers, some additional fields 
 added etc..

I agree. Walter said let's get ranges going and then we can look into optimizing the structure. Ranges seem to work really well so the structural change will follow soon. Andrei
Feb 13 2009
prev sibling parent Yigal Chripun <yigal100 gmail.com> writes:
Denis Koroskin wrote:
 On Fri, 13 Feb 2009 17:24:54 +0300, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 Rainer Deyke wrote:
 Andrei Alexandrescu wrote:
 C++ ctors won't do the right thing if you use pointers, which is the
 moral equivalent of using T[] inside MathematicalVector. If you
 refer to
 std::vector instead, then that's a carefully-defined type that does
 have
 the right copy constructor defined.


It will.
 I was under the impression that native arrays were intended to be used
 directly in D, something I rarely do in C++. Certainly neither of the
 standard libraries has any hesitation about passing unadorned dynamic
 arrays around.

Phobos2 plans to consistently consider T[] a range, not a full-fledged array. A range corresponds to a pair of iterators in STL. Andrei

Will T[] get front, back, empty properties, and popFront(), popBack() methods? Perhaps, its the time T[] internal structure is changed! For example, ptr+size replaced by two pointers, some additional fields added etc..

once upon a time there was a huge discussion about template syntax. one of the suggestions was: A!(T) => A[t] with such syntax we could use: Array[T] - this would be an array with the added capacity field Map[Key, Value] - instead of the current AA's Set[T] - example of user defined template ... Array[int] arr1; // dyn. array Array[int, 3] arr2; static array with size 3 or maybe even better: if n is constant => static array, otherwise dyn. with pre-set capacity Array[int] arr3(n); auto r = arr1[]; // range/slice of arr1 IMHO, it's not a good idea to have container types in the language. they should be provided by the stdlib and the language needs to provide only the syntax to make such types behave like builtins. D already provides operator overloads for that.
Feb 13 2009
prev sibling parent Weed <resume755 mail.ru> writes:
Rainer Deyke пишет:
 Michel Fortin wrote:
 Polymorphism doesn't work very well while passing objects by value, even
 in C++. This is called the slicing problem.

I have heard about the slicing problem. I know what it is. But in all my years of using C++ as my primary language, I have never actually encountered it. I don't believe it actually exists.

I am absolutely agree with you. Besides, slicing it is possible to detect in a compile time in many cases. It can be too grandiloquent sounds, but in D us have deprived of one degree of freedom - freedom to use a stack.
Feb 08 2009
prev sibling parent reply Weed <resume755 mail.ru> writes:
Michel Fortin пишет:
 On 2009-02-08 09:30:08 -0500, Weed <resume755 mail.ru> said:
 
 Let's assume, polymorphism is necessary to these objects

Polymorphism doesn't work very well while passing objects by value, even in C++. This is called the slicing problem. <http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c> I think D does a good job at avoiding that problem.

By performance loss :(
Feb 08 2009
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-02-08 23:43:13 -0500, Weed <resume755 mail.ru> said:

 Michel Fortin п©п╦я┬п╣я┌:
 On 2009-02-08 09:30:08 -0500, Weed <resume755 mail.ru> said:
 
 Let's assume, polymorphism is necessary to these objects

Polymorphism doesn't work very well while passing objects by value, even in C++. This is called the slicing problem. <http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c> I think D does a good job at avoiding that problem.

By performance loss :(

No. By forbiding the cases that leads to slicing, like returning a polymorphic object by value. Returning something by-value always cause slicing in C++. Try this C++ code: #include <iostream> class A { public: virtual void hello() { std::cout << "hello from A" << std::endl; } }; class B : public A { public: virtual void hello() { std::cout << "hello from B" << std::endl; } }; A test() { B b; b.hello(); // prints "hello from B" return b; } int main() { test().hello(); // prints "hello from A" A a = test(); a.hello(); // prints "hello from A" } Here, returning B by value "slices" the object, transforming it into a A. There is absolutely no polymorphic behaviour when returning by value. It's as if virtual functions weren't virtual at all: your B object is transformed to a A when it is returned. To preserve polymorphism, you need to return a pointer or a reference, but then test() can't allocate the object on the stack. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 09 2009
parent reply Weed <resume755 mail.ru> writes:
Michel Fortin пишет:
 On 2009-02-08 23:43:13 -0500, Weed <resume755 mail.ru> said:
 
 Michel Fortin пишет:
 On 2009-02-08 09:30:08 -0500, Weed <resume755 mail.ru> said:

 Let's assume, polymorphism is necessary to these objects

Polymorphism doesn't work very well while passing objects by value, even in C++. This is called the slicing problem. <http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c> I think D does a good job at avoiding that problem.

By performance loss :(

No. By forbiding the cases that leads to slicing, like returning a polymorphic object by value.

Let's think, can there are other ways to solve problem? Here, for example my reasons: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81958
Feb 09 2009
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-02-09 07:00:56 -0500, Weed <resume755 mail.ru> said:

 No. By forbiding the cases that leads to slicing, like returning a
 polymorphic object by value.

Let's think, can there are other ways to solve problem? Here, for example my reasons: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81958

I'm
 

It seems like you want the ability to copy classes, and thus pass them by value, but only when there would be no type conversion. I'm not getting where this can be useful though: by forcing your classes to be of a specific type, with no derived types allowed, you're losing polymorphism. The only advantage you have is that your type can be derived from another so you can reuse some of its implementation. There are other ways to acheive that in D however (mixins come to mind). -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 09 2009
next sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2009-02-09 07:34:44 -0500, Michel Fortin <michel.fortin michelf.com> said:

 The only advantage you have is that your type can be derived from 
 another so you can reuse some of its implementation. There are other 
 ways to acheive that in D however (mixins come to mind).

That said, I'm not against adding struct inheritance if it can be done in a way that avoids slicing problems. I think this can be acheived by making slicing innoculous: 1. forbid overriding base members 2. forbid destructors in derived strucs (or else slicing would lose the destructor) Open questions: Is that constraining enough to avoid slicing problems? Do these constrains make the feature irrelevant? -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 09 2009
prev sibling next sibling parent reply Don <nospam nospam.com> writes:
Michel Fortin wrote:
 On 2009-02-09 07:00:56 -0500, Weed <resume755 mail.ru> said:
 
 No. By forbiding the cases that leads to slicing, like returning a
 polymorphic object by value.

Let's think, can there are other ways to solve problem? Here, for example my reasons: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmar .D&article_id=81958

I'm

It seems like you want the ability to copy classes, and thus pass them by value, but only when there would be no type conversion. I'm not getting where this can be useful though: by forcing your classes to be of a specific type, with no derived types allowed, you're losing polymorphism. The only advantage you have is that your type can be derived from another so you can reuse some of its implementation. There are other ways to acheive that in D however (mixins come to mind).

The one case I could think of was the strategy pattern: no data is added (including, no virtual functions) -- the only thing that's added is a different implementation of an existing virtual function. In such a situation, the slicing problem cannot happen. You can have an array of polymorphic types. But the language won't allow it. However, I've encountered this problem in C++ as well. Allowing stack-based classes is NOT sufficient to solve it. What you actually want is a struct with a vtable pointer stored IN the struct itself (rather than one instance of the vtable per struct).
Feb 09 2009
parent Michel Fortin <michel.fortin michelf.com> writes:
On 2009-02-09 16:02:10 -0500, "Denis Koroskin" <2korden gmail.com> said:

 The one case I could think of was the strategy pattern: no data is 
 added  (including, no virtual functions) -- the only thing that's added 
 is a  different implementation of an existing virtual function. In such 
 a  situation, the slicing problem cannot happen.

Michel has posted (2 posts up) an example of a class that happen to have no member fields at all (virtual methods only). And yet it still suffers from slicing.

Indeed. But that's in C++. It could certainly be possible to make that work differently in another language. I'm not sure it's worth it for D though. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 09 2009
prev sibling parent Weed <resume755 mail.ru> writes:
Michel Fortin пишет:
 On 2009-02-09 07:00:56 -0500, Weed <resume755 mail.ru> said:
 
 No. By forbiding the cases that leads to slicing, like returning a
 polymorphic object by value.

Let's think, can there are other ways to solve problem? Here, for example my reasons: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81958

I'm

It seems like you want the ability to copy classes, and thus pass them by value, but only when there would be no type conversion.

Yes, this is the easiest way that came to my head
 I'm not getting where this can be useful though: by forcing your classes
 to be of a specific type, with no derived types allowed, you're losing
 polymorphism.

Yes
 The only advantage you have is that your type can be
 derived from another so you can reuse some of its implementation.

And still able to pass class instance through a stack! And I do not propose to remove the traditional class in the heap
 There
 are other ways to acheive that in D however (mixins come to mind).
 

Feb 09 2009
prev sibling next sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Sun, 08 Feb 2009 17:07:31 +0300, Weed <resume755 mail.ru> wrote:

 Denis Koroskin п©п╦я┬п╣я┌:

 Your C objects have value semantics. For value semantics you should use  
 value types.

At present in D is not contain support of value types for objects. (I consider that it is necessary)
 Alternatively you may want to use the following trick (I used it for  
 lazy string concatenation in C+):

[skip]
 Apparently, it doesn't work at the moment because ctor can't be a
 template :(
 Is there an enhancement request in bugzilla? It prevents this pattern
 from working.

+ it is difficult and also it will be even more difficult if it will be necessary to make support for the construction like: 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. (from http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81432)

I believe you should stick with structs for that.
Feb 08 2009
parent "Denis Koroskin" <2korden gmail.com> writes:
On Fri, 13 Feb 2009 17:24:54 +0300, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Rainer Deyke wrote:
 Andrei Alexandrescu wrote:
 C++ ctors won't do the right thing if you use pointers, which is the
 moral equivalent of using T[] inside MathematicalVector. If you refer  
 to
 std::vector instead, then that's a carefully-defined type that does  
 have
 the right copy constructor defined.


It will.
 I was under the impression that native arrays were intended to be used
 directly in D, something I rarely do in C++.  Certainly neither of the
 standard libraries has any hesitation about passing unadorned dynamic
 arrays around.

Phobos2 plans to consistently consider T[] a range, not a full-fledged array. A range corresponds to a pair of iterators in STL. Andrei

Will T[] get front, back, empty properties, and popFront(), popBack() methods? Perhaps, its the time T[] internal structure is changed! For example, ptr+size replaced by two pointers, some additional fields added etc..
Feb 13 2009
prev sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Mon, 09 Feb 2009 15:53:59 +0300, Don <nospam nospam.com> wrote:

 Michel Fortin wrote:
 On 2009-02-09 07:00:56 -0500, Weed <resume755 mail.ru> said:

 No. By forbiding the cases that leads to slicing, like returning a
 polymorphic object by value.

Let's think, can there are other ways to solve problem? Here, for example my reasons: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81958


It seems like you want the ability to copy classes, and thus pass them by value, but only when there would be no type conversion. I'm not getting where this can be useful though: by forcing your classes to be of a specific type, with no derived types allowed, you're losing polymorphism. The only advantage you have is that your type can be derived from another so you can reuse some of its implementation. There are other ways to acheive that in D however (mixins come to mind).

The one case I could think of was the strategy pattern: no data is added (including, no virtual functions) -- the only thing that's added is a different implementation of an existing virtual function. In such a situation, the slicing problem cannot happen.

Michel has posted (2 posts up) an example of a class that happen to have no member fields at all (virtual methods only). And yet it still suffers from slicing.
 You can have an array of  
 polymorphic types. But the language won't allow it.

 However, I've encountered this problem in C++ as well. Allowing  
 stack-based classes is NOT sufficient to solve it. What you actually  
 want is a struct with a vtable pointer stored IN the struct itself  
 (rather than one instance of the vtable per struct).

Feb 09 2009
prev sibling next sibling parent reply naryl <cy ngs.ru> writes:
Content-Type: text/plain

It's a bit offtopic but I'd be grateful if someone can explain why D with
structs completes this simple benchmark (see attachment) so slowly compared to
C++ with classes on stack:

D struct - 27.85s 
C++ stack - 8.32s

D class - 271.58s
C++ heap - 249.32s

Compiled with "dmd -O". -release decreases performance by 10% in this case.
-inline doesn't affects it at all.
Feb 08 2009
next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Sun, 08 Feb 2009 18:09:41 +0300, naryl <cy ngs.ru> wrote:

 It's a bit offtopic but I'd be grateful if someone can explain why D  
 with structs completes this simple benchmark (see attachment) so slowly  
 compared to C++ with classes on stack:

 D struct - 27.85s
 C++ stack - 8.32s

 D class - 271.58s
 C++ heap - 249.32s

 Compiled with "dmd -O". -release decreases performance by 10% in this  
 case. -inline doesn't affects it at all.

Well, you don't compare C++ vs D struct speed but rather C++ vs D IO. I believe writefln is a bottleneck.
Feb 08 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Sun, 08 Feb 2009 18:20:47 +0300, Denis Koroskin <2korden gmail.com> wrote:

 On Sun, 08 Feb 2009 18:09:41 +0300, naryl <cy ngs.ru> wrote:

 It's a bit offtopic but I'd be grateful if someone can explain why D  
 with structs completes this simple benchmark (see attachment) so slowly  
 compared to C++ with classes on stack:

 D struct - 27.85s
 C++ stack - 8.32s

 D class - 271.58s
 C++ heap - 249.32s

 Compiled with "dmd -O". -release decreases performance by 10% in this  
 case. -inline doesn't affects it at all.

Well, you don't compare C++ vs D struct speed but rather C++ vs D IO. I believe writefln is a bottleneck.

Nevermind, I didn't understand the benchmark from quick glance.
Feb 08 2009
prev sibling next sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Sun, 08 Feb 2009 18:09:41 +0300, naryl <cy ngs.ru> wrote:

 It's a bit offtopic but I'd be grateful if someone can explain why D  
 with structs completes this simple benchmark (see attachment) so slowly  
 compared to C++ with classes on stack:

 D struct - 27.85s
 C++ stack - 8.32s

 D class - 271.58s
 C++ heap - 249.32s

 Compiled with "dmd -O". -release decreases performance by 10% in this  
 case. -inline doesn't affects it at all.

I noticed that you calculate Fib(27) in fibs.cc and Fib(40) in fibs.d Can this affect such a big difference between C++ and D version?
Feb 08 2009
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Denis Koroskin wrote:
 On Sun, 08 Feb 2009 18:40:53 +0300, Denis Koroskin <2korden gmail.com> 
 wrote:
 
 On Sun, 08 Feb 2009 18:09:41 +0300, naryl <cy ngs.ru> wrote:

 It's a bit offtopic but I'd be grateful if someone can explain why D 
 with structs completes this simple benchmark (see attachment) so 
 slowly compared to C++ with classes on stack:

 D struct - 27.85s
 C++ stack - 8.32s

 D class - 271.58s
 C++ heap - 249.32s

 Compiled with "dmd -O". -release decreases performance by 10% in this 
 case. -inline doesn't affects it at all.

I noticed that you calculate Fib(27) in fibs.cc and Fib(40) in fibs.d Can this affect such a big difference between C++ and D version?

They both perform roughly the same when this typo is corrected.

It was a ploy! :o) Andrei
Feb 08 2009
prev sibling parent reply naryl <cy ngs.ru> writes:
Denis Koroskin Wrote:

 On Sun, 08 Feb 2009 18:40:53 +0300, Denis Koroskin <2korden gmail.com> wrote:
 
 On Sun, 08 Feb 2009 18:09:41 +0300, naryl <cy ngs.ru> wrote:

 It's a bit offtopic but I'd be grateful if someone can explain why D  
 with structs completes this simple benchmark (see attachment) so slowly  
 compared to C++ with classes on stack:

 D struct - 27.85s
 C++ stack - 8.32s

 D class - 271.58s
 C++ heap - 249.32s

 Compiled with "dmd -O". -release decreases performance by 10% in this  
 case. -inline doesn't affects it at all.

I noticed that you calculate Fib(27) in fibs.cc and Fib(40) in fibs.d Can this affect such a big difference between C++ and D version?

They both perform roughly the same when this typo is corrected.

Sorry. :) For n=40 I get: C++ compiled with "g++ -O fibs.cc" - 5.37s D compiled with "dmd -O -inline fibs.d" - 14.32s D compiled with "dmd -O -inline -release fibs.d" - 15.20s DMD 2.023 is still almost three times slower.
Feb 08 2009
next sibling parent reply naryl <cy ngs.ru> writes:
Denis Koroskin Wrote:
 And here are results (best/average of 3 runs):
 
 DMD2.023 - 12.492/12.576 ms (-O -inline)
 DMC8.42n - 13.941/14.131 ms (-O -inline)
 
 

The only explanation I see is gcc does some optimization that dmd backend doesn't.
Feb 08 2009
parent grauzone <none example.net> writes:
naryl wrote:
 Denis Koroskin Wrote:
 And here are results (best/average of 3 runs):

 DMD2.023 - 12.492/12.576 ms (-O -inline)
 DMC8.42n - 13.941/14.131 ms (-O -inline)

The only explanation I see is gcc does some optimization that dmd backend doesn't.

That's a good explanation. The only positive thing about the dmd backend (or dmd in general) is its compile speed.
Feb 08 2009
prev sibling parent reply Weed <resume755 mail.ru> writes:
Denis Koroskin пишет:

 And here are results (best/average of 3 runs):
 
 DMD2.023 - 12.492/12.576 ms (-O -inline)
 DMC8.42n - 13.941/14.131 ms (-O -inline)
 
 

Try to return from value() a class instance
Feb 08 2009
parent reply naryl <cy ngs.ru> writes:
Weed Wrote:
 Denis Koroskin О©╫О©╫О©╫О©╫О©╫:
 
 And here are results (best/average of 3 runs):
 
 DMD2.023 - 12.492/12.576 ms (-O -inline)
 DMC8.42n - 13.941/14.131 ms (-O -inline)
 
 

Try to return from value() a class instance

In this case both C++ and D performance drops almost tenfold.
Feb 08 2009
parent Weed <resume755 mail.ru> writes:
naryl пишет:
 Weed Wrote:
 Denis Koroskin яПНяПНяПНяПНяПН:

 And here are results (best/average of 3 runs):

 DMD2.023 - 12.492/12.576 ms (-O -inline)
 DMC8.42n - 13.941/14.131 ms (-O -inline)


In this case both C++ and D performance drops almost tenfold.

Hmmm. Can you show code?
Feb 08 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Sun, 08 Feb 2009 18:40:53 +0300, Denis Koroskin <2korden gmail.com> wrote:

 On Sun, 08 Feb 2009 18:09:41 +0300, naryl <cy ngs.ru> wrote:

 It's a bit offtopic but I'd be grateful if someone can explain why D  
 with structs completes this simple benchmark (see attachment) so slowly  
 compared to C++ with classes on stack:

 D struct - 27.85s
 C++ stack - 8.32s

 D class - 271.58s
 C++ heap - 249.32s

 Compiled with "dmd -O". -release decreases performance by 10% in this  
 case. -inline doesn't affects it at all.

I noticed that you calculate Fib(27) in fibs.cc and Fib(40) in fibs.d Can this affect such a big difference between C++ and D version?

They both perform roughly the same when this typo is corrected.
Feb 08 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Sun, 08 Feb 2009 20:24:08 +0300, naryl <cy ngs.ru> wrote:

 Denis Koroskin Wrote:

 On Sun, 08 Feb 2009 18:40:53 +0300, Denis Koroskin <2korden gmail.com>  
 wrote:

 On Sun, 08 Feb 2009 18:09:41 +0300, naryl <cy ngs.ru> wrote:

 It's a bit offtopic but I'd be grateful if someone can explain why D
 with structs completes this simple benchmark (see attachment) so  


 compared to C++ with classes on stack:

 D struct - 27.85s
 C++ stack - 8.32s

 D class - 271.58s
 C++ heap - 249.32s

 Compiled with "dmd -O". -release decreases performance by 10% in this
 case. -inline doesn't affects it at all.

I noticed that you calculate Fib(27) in fibs.cc and Fib(40) in fibs.d Can this affect such a big difference between C++ and D version?

They both perform roughly the same when this typo is corrected.

Sorry. :) For n=40 I get: C++ compiled with "g++ -O fibs.cc" - 5.37s D compiled with "dmd -O -inline fibs.d" - 14.32s D compiled with "dmd -O -inline -release fibs.d" - 15.20s DMD 2.023 is still almost three times slower.

Here is code, setting and result I got. D version: import std.stdio; extern(Windows) int timeGetTime(); struct Fib { private int _value; int value() { if(_value <= 2) return 1; scope f1 = Fib(_value - 1); scope f2 = Fib(_value - 2); return f1.value() + f2.value(); } } void main() { int start = timeGetTime(); int value = 0; foreach (i; 0 .. 10) { value += Fib(40).value; } int stop = timeGetTime(); writefln(value); writefln("Time elapsed: %s", stop - start); } C++ version: #include <stdio.h> #include <windows.h> class Fib { private: int _value; public: Fib(int n) { _value = n; } int value() { if(_value <= 2) return 1; Fib f1 = Fib(_value - 1); Fib f2 = Fib(_value - 2); return f1.value() + f2.value(); } }; int main() { int start = timeGetTime(); int value = 0; for(int i=0; i<10; i++) { Fib x = Fib(40); value += x.value(); } int stop = timeGetTime(); printf("%d\n", value); printf("Time elapsed: %d\n", stop - start); return 0; } And here are results (best/average of 3 runs): DMD2.023 - 12.492/12.576 ms (-O -inline) DMC8.42n - 13.941/14.131 ms (-O -inline)
Feb 08 2009
prev sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Sun, 08 Feb 2009 23:45:41 +0300, naryl <cy ngs.ru> wrote:

 Denis Koroskin Wrote:
 And here are results (best/average of 3 runs):

 DMD2.023 - 12.492/12.576 ms (-O -inline)
 DMC8.42n - 13.941/14.131 ms (-O -inline)

The only explanation I see is gcc does some optimization that dmd backend doesn't.

Yes, that's why you can't claim D is slow because GCC outperforms DMD.
Feb 08 2009
prev sibling next sibling parent reply Radu <radu.racariu void.space> writes:
Weed wrote:
 (Has started here:
 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81359)

 To me still does not give rest performance of classes (in comparison
 with C++ or with D structs)

 I still think that it is a serious problem.

 Colleagues from our national D forum have asked to show it and I have
 written simple examples on D. I want to share with you too them.

 On my computer the code with structure (i.e. object by value) runs in 6
 times faster than a code with a class:

 $ time ./struct

 real    0m8.515s
 user    0m7.796s
 sys     0m0.016s
 $ time ./class

 real    0m52.185s
 user    0m40.543s
 sys     0m0.076s

 The code on C++ is also approximately in 6 times faster a code with
 classes on D. (I do not give an example on C++ because classes on C++
 work just as structures in D.)

 I think with it it is necessary to do something.


 Examples code:

 //========================
 struct C {
     int i;
     real[5] unused; // to prevent returning this object in registers

     C opAdd( C src ) {
         C ret;
         ret.i = i + src.i;
         return ret;
     }
 }

 int main() {
     C c1;
     C c2;

     // initialise i by "random" value to prevent compile-time calculation
     c1.i = cast(int)&c1;
     c2.i = 0;

     for(int i = 0; i < 50_000_000; ++i)
         c2 = c1 + c1 + c1;

     return c2.i;
 }

 //========================

 class C {
     int i;
     real[5] unused; // to prevent returning this object in registers

     C opAdd( C src ) {
         auto ret = new C;
         ret.i = i + src.i;
         return ret;
     }
 }

 int main() {
     auto c1 = new C;
     auto c2 = new C;

     // initialise i by "random" value to prevent compile-time calculation
     c1.i = cast(int)&c1;
     c2.i = 0;

     for(int i = 0; i < 50_000_000; ++i)
         c2 = c1 + c1 + c1;

     return c2.i;
 }
 //========================
   

template stackAllocator(T) { new(size_t size, void* sp = alloca(T.classinfo.init.length)) { return sp; } delete(void* ptr) { } } final class C { int i; real[5] unused; // to prevent returning this object in registers C opAdd(C src) { auto ret = new C; ret.i = i + src.i; return ret; } mixin stackAllocator!(C); } Radu
Feb 08 2009
parent Weed <resume755 mail.ru> writes:
Radu пишет:

 While nor so orthodox, this improves the situation a bit:
 
 template stackAllocator(T) {
    new(size_t size, void* sp = alloca(T.classinfo.init.length)) {
        return sp;
    }
 
    delete(void* ptr) {
    }
 }
 
 final class C {
    int i;
    real[5] unused; // to prevent returning this object in registers
 
    C opAdd(C src) {
        auto ret = new C;
        ret.i = i + src.i;
        return ret;
    }
 
    mixin stackAllocator!(C);
 
 }
 
 Radu

If the used object it is impossible to change? It is written by other person, for example? And, we all will take for a rule in each object using overload to add stackAllocator?
Feb 08 2009
prev sibling next sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Weed wrote:
 (Has started here:
 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81359)
 
 To me still does not give rest performance of classes (in comparison
 with C++ or with D structs)

On my system, your struct example averaged 0.36 seconds, and your class example averaged 0.38 seconds. Your benchmark is flawed in three ways: 1. You're timing allocations in the class example. Use opAddAssign to avoid it. 2. You're passing large structs in the struct example. Use ref to avoid it. 3. c1 is never assigned to, so c1 + c1 + c1 is moved outside the loop. The assignment has no side effect, so the loop is optimized out. Replace "c1 + c1 + c1" with "c1 + c2 + c1", for instance, and the struct example takes 20 seconds rather than 0.36. The original class example takes 57 seconds to do 50 million allocations. Using those tricks, the class example takes about four seconds on my machine, and the struct example takes two. For reference, the benchmarks I used: // class example ------------------------- scope class C { int i; real[5] unused; // to prevent returning this object in registers final void opAddAssign( C src ) { this.i += src.i; } } int main() { scope c1 = new C; scope c2 = new C; // initialise i by "random" value to prevent compile-time calculation c1.i = cast(int)&c1; c2.i = 0; for(int i = 0; i < 50_000_000; ++i) { c2 += c1; c2 += c1; c2 += c1; } return c2.i; } // struct example -------------------------- struct C { int i; real[5] unused; // to prevent returning this object in registers void opAddAssign( ref C src ) { this.i += src.i; } } int main() { C c1, c2; // initialise i by "random" value to prevent compile-time calculation c1.i = cast(int)&c1; c2.i = 0; for(int i = 0; i < 50_000_000; ++i) { c2 += c1; c2 += c1; c2 += c1; } return c2.i; }
Feb 08 2009
parent reply Weed <resume755 mail.ru> writes:
Christopher Wright пишет:
 Weed wrote:
 (Has started here:
 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81359)


 To me still does not give rest performance of classes (in comparison
 with C++ or with D structs)

On my system, your struct example averaged 0.36 seconds, and your class example averaged 0.38 seconds. Your benchmark is flawed in three ways: 1. You're timing allocations in the class example. Use opAddAssign to avoid it.

Earlier I proved that it is impossible. For example here in such expression: ============== 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. ==============
 2. You're passing large structs in the struct example. Use ref to avoid it.
 3. c1 is never assigned to, so c1 + c1 + c1 is moved outside the loop.
 The assignment has no side effect, so the loop is optimized out. Replace
 "c1 + c1 + c1" with "c1 + c2 + c1", for instance, and the struct example
 takes 20 seconds rather than 0.36.

Really, it has a little affected. Thanks!
Feb 08 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
Weed wrote:
 Christopher Wright пишет:
 Weed wrote:
 (Has started here:
 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81359)


 To me still does not give rest performance of classes (in comparison
 with C++ or with D structs)

example averaged 0.38 seconds. Your benchmark is flawed in three ways: 1. You're timing allocations in the class example. Use opAddAssign to avoid it.

Earlier I proved that it is impossible. For example here in such expression: ============== 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. ==============

In that example, you can use structs instead of classes. Your response to that is that structs do not participate in polymorphism. There was a suggestion elsewhere like this: interface IPathResolver { Checkpoint getCheckpoint(Path* path, int i); } struct Path { char[] path; // any other info you need.... IPathResolver resolver; Checkpoint getCheckpoint(int value) { return resolver.getCheckpoint(this, value); } } This way, you only allocate once for each type you need, you have polymorphism, and you can put stuff on the stack for quick access.
Feb 08 2009
parent reply Weed <resume755 mail.ru> writes:
Christopher Wright пишет:
 Weed wrote:
 Christopher Wright пишет:
 Weed wrote:
 (Has started here:
 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81359)



 To me still does not give rest performance of classes (in comparison
 with C++ or with D structs)

example averaged 0.38 seconds. Your benchmark is flawed in three ways: 1. You're timing allocations in the class example. Use opAddAssign to avoid it.

Earlier I proved that it is impossible. For example here in such expression: ============== 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. ==============

In that example, you can use structs instead of classes. Your response to that is that structs do not participate in polymorphism. There was a suggestion elsewhere like this: interface IPathResolver { Checkpoint getCheckpoint(Path* path, int i); } struct Path { char[] path; // any other info you need.... IPathResolver resolver; Checkpoint getCheckpoint(int value) { return resolver.getCheckpoint(this, value); } } This way, you only allocate once for each type you need, you have polymorphism, and you can put stuff on the stack for quick access.

And if I need some different such combinations? For each it is necessary to write such 8-10 lines? This is terrible!
Feb 08 2009
next sibling parent downs <default_357-line yahoo.de> writes:
Weed wrote:
 Christopher Wright пишет:
 Weed wrote:
 Christopher Wright пишет:
 Weed wrote:
 (Has started here:
 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81359)



 To me still does not give rest performance of classes (in comparison
 with C++ or with D structs)

example averaged 0.38 seconds. Your benchmark is flawed in three ways: 1. You're timing allocations in the class example. Use opAddAssign to avoid it.

expression: ============== 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. ==============

to that is that structs do not participate in polymorphism. There was a suggestion elsewhere like this: interface IPathResolver { Checkpoint getCheckpoint(Path* path, int i); } struct Path { char[] path; // any other info you need.... IPathResolver resolver; Checkpoint getCheckpoint(int value) { return resolver.getCheckpoint(this, value); } } This way, you only allocate once for each type you need, you have polymorphism, and you can put stuff on the stack for quick access.

And if I need some different such combinations? For each it is necessary to write such 8-10 lines? This is terrible!

I'm sure you can do something with templates. :)
Feb 08 2009
prev sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Weed wrote:
 And if I need some different such combinations? For each it is necessary
 to write such 8-10 lines? This is terrible!

You need to add those lines for every method you need virtual dispatch with for your value type. It's an overhead of three lines per method, two for the interface (declaration and member), and one extra line where you create the struct. If you're reasonable, your struct constructor will create a default instance. So, it's not that great an overhead.
Feb 09 2009
parent reply Weed <resume755 mail.ru> writes:
Christopher Wright пишет:
 Weed wrote:
 And if I need some different such combinations? For each it is necessary
 to write such 8-10 lines? This is terrible!

You need to add those lines for every method you need virtual dispatch with for your value type. It's an overhead of three lines per method, two for the interface (declaration and member), and one extra line where you create the struct. If you're reasonable, your struct constructor will create a default instance. So, it's not that great an overhead.

Do not be surprised that so many continue to write in C++! :)
Feb 09 2009
parent Yigal Chripun <yigal100 gmail.com> writes:
Weed wrote:
 Christopher Wright пишет:
 Weed wrote:
 And if I need some different such combinations? For each it is necessary
 to write such 8-10 lines? This is terrible!

with for your value type. It's an overhead of three lines per method, two for the interface (declaration and member), and one extra line where you create the struct. If you're reasonable, your struct constructor will create a default instance. So, it's not that great an overhead.

Do not be surprised that so many continue to write in C++! :)

huh? there are still billions of people that continue smoking even though the problems that causes are well known. people have bad habits - using C++ instead of proper languages (like D) is one of them. Unless a person wants to be cured, nothing can be done to cure him of such a strong addiction. I hope you too will one day be cured of the illness that is C++.
Feb 09 2009
prev sibling next sibling parent reply Kagamin <spam here.lot> writes:
Weed Wrote:

 The code on C++ is also approximately in 6 times faster a code with
 classes on D. (I do not give an example on C++ because classes on C++
 work just as structures in D.)

 class C {
     int i;
     real[5] unused; // to prevent returning this object in registers
 
     C opAdd( C src ) {
         auto ret = new C;
         ret.i = i + src.i;
         return ret;
     }
 }

Well, D class code here is not equivalent to C++ class code. D code has more features, namely, it's polymorphic: C.opAdd is able to work with classes, derived from C, while corresponding C++ code is unable to do so. If you can't use polymorphism, why do you use classes?
Feb 09 2009
next sibling parent Weed <resume755 mail.ru> writes:
Kagamin пишет:
 Weed Wrote:
 
 The code on C++ is also approximately in 6 times faster a code with
 classes on D. (I do not give an example on C++ because classes on C++
 work just as structures in D.)

 class C {
     int i;
     real[5] unused; // to prevent returning this object in registers

     C opAdd( C src ) {
         auto ret = new C;
         ret.i = i + src.i;
         return ret;
     }
 }

Well, D class code here is not equivalent to C++ class code. D code has more features, namely, it's polymorphic: C.opAdd is able to work with classes, derived from C, while corresponding C++ code is unable to do so. If you can't use polymorphism, why do you use classes?

There opAdd is only for demonstration. In real life it may be virtual method getSomething()
Feb 09 2009
prev sibling next sibling parent reply Weed <resume755 mail.ru> writes:
Kagamin пишет:
 Weed Wrote:
 
 The code on C++ is also approximately in 6 times faster a code with
 classes on D. (I do not give an example on C++ because classes on C++
 work just as structures in D.)

 class C {
     int i;
     real[5] unused; // to prevent returning this object in registers

     C opAdd( C src ) {
         auto ret = new C;
         ret.i = i + src.i;
         return ret;
     }
 }

Well, D class code here is not equivalent to C++ class code. D code has more features, namely, it's polymorphic: C.opAdd is able to work with classes, derived from C, while corresponding C++ code is unable to do so.

It is really true?
Feb 09 2009
parent reply Kagamin <spam here.lot> writes:
Weed Wrote:

 Well, D class code here is not equivalent to C++ class code. D code has more
features, namely, it's polymorphic: C.opAdd is able to work with classes,
derived from C, while corresponding C++ code is unable to do so.

It is really true?

Of course.
 There opAdd is only for demonstration.

If your example has nothing in common with real-world tasks and needs, it's useless.
Feb 09 2009
parent reply Weed <resume755 mail.ru> writes:
Kagamin пишет:
 Weed Wrote:
 
 Well, D class code here is not equivalent to C++ class code. D code has more
features, namely, it's polymorphic: C.opAdd is able to work with classes,
derived from C, while corresponding C++ code is unable to do so.


Of course.

But I do not understand in what here problem. In C++ is impossible to do overloading with "virtual" keyword? Probably I badly know C ++, sorry.
Feb 09 2009
parent reply Kagamin <spam here.lot> writes:
Weed Wrote:

 Well, D class code here is not equivalent to C++ class code. D code has more
features, namely, it's polymorphic: C.opAdd is able to work with classes,
derived from C, while corresponding C++ code is unable to do so.



do overloading with "virtual" keyword? Probably I badly know C ++, sorry.

In C++ you can't pass object derived from C to opAdd method. In D you can.
Feb 09 2009
parent Weed <resume755 mail.ru> writes:
Kagamin пишет:
 Weed Wrote:
 
 Well, D class code here is not equivalent to C++ class code. D code has more
features, namely, it's polymorphic: C.opAdd is able to work with classes,
derived from C, while corresponding C++ code is unable to do so.



do overloading with "virtual" keyword? Probably I badly know C ++, sorry.

In C++ you can't pass object derived from C to opAdd method. In D you can.

I am do not agree with you: // C++: class C { public: virtual void doSomething( C src ) {} }; class C2 : public C { }; int main( int argc, char* argv[] ) { C c; C2 c2; c.doSomething( c2 ); return 0; }
Feb 09 2009
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
Kagamin:

Well, D class code here is not equivalent to C++ class code. D code has more
features, namely, it's polymorphic: C.opAdd is able to work with classes,
derived from C, while corresponding C++ code is unable to do so. If you can't
use polymorphism, why do you use classes?<

I'd like to create a more apples-to-apples comparison, so if you have suggestions regarding how to improve the code I am willing to fix things and redo the timings. Maybe adding a 'virtual' to the C++ method? I too have done some benchmarks, see timings, notes and code here: http://leonardo-m.livejournal.com/76547.html Bye, bearophile
Feb 09 2009
prev sibling parent reply Weed <resume755 mail.ru> writes:
What is the result of the discussion?

Will the language change? Not once, could in 6-12 months?..

For me, the answer to that question was tantamount to answering the
question "whether or not to use D for new projects?"


Weed пишет:
 (Has started here:
 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81359)
 
 To me still does not give rest performance of classes (in comparison
 with C++ or with D structs)
 
 I still think that it is a serious problem.
 
 Colleagues from our national D forum have asked to show it and I have
 written simple examples on D. I want to share with you too them.
 
 On my computer the code with structure (i.e. object by value) runs in 6
 times faster than a code with a class:
 
 $ time ./struct
 
 real    0m8.515s
 user    0m7.796s
 sys     0m0.016s
 $ time ./class
 
 real    0m52.185s
 user    0m40.543s
 sys     0m0.076s

[...]
Feb 19 2009
parent reply Kagamin <spam here.lot> writes:
Weed Wrote:

 Will the language change?

Hmm... You already has Walter's answer. He's the boss.
Feb 19 2009
parent reply Weed <resume755 mail.ru> writes:
Kagamin пишет:
 Weed Wrote:
 
 Will the language change?

Hmm... You already has Walter's answer. He's the boss.

I want a more specific answer (yes or no) if possible...
Feb 19 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
Weed wrote:
 Kagamin пишет:
 Weed Wrote:

 Will the language change?


I want a more specific answer (yes or no) if possible...

It will not. If you come up with some really awesome use case, then it could, but nobody has yet, and the issue comes up every few months.
Feb 19 2009
parent reply Weed <resume755 mail.ru> writes:
Christopher Wright пишет:
 Weed wrote:
 Kagamin пишет:
 Weed Wrote:

 Will the language change?


I want a more specific answer (yes or no) if possible...

It will not. If you come up with some really awesome use case, then it could, but nobody has yet, and the issue comes up every few months.

Good (I think) use cases have been in this thread
Feb 20 2009
parent reply Don <nospam nospam.com> writes:
Weed wrote:
 Christopher Wright пишет:
 Weed wrote:
 Kagamin пишет:
 Weed Wrote:

 Will the language change?



could, but nobody has yet, and the issue comes up every few months.

Good (I think) use cases have been in this thread

They have not. The examples have been incomplete. You've provided use cases involving classes, but haven't given _any_ details about the contents of those classes. It's possible that you have indeed found use cases, but you haven't actually shown them here.
Feb 20 2009
parent reply Weed <resume755 mail.ru> writes:
Don пишет:
 Weed wrote:
 Christopher Wright пишет:
 Weed wrote:
 Kagamin пишет:
 Weed Wrote:

 Will the language change?



could, but nobody has yet, and the issue comes up every few months.

Good (I think) use cases have been in this thread

They have not. The examples have been incomplete. You've provided use cases involving classes, but haven't given _any_ details about the contents of those classes. It's possible that you have indeed found use cases, but you haven't actually shown them here.

I do not understand the claims. For example, any class that implements the mathematical object (matrix, vector, number, etc.) is suitable for example. I think it is obvious. Yes, the discussion in this thread showed that almost always possible for each case to find a different approach, using additives and other scary code. But what if these "perversions" flaw somewhere in the idea of a "reference-only" type? I understand people who are against the changes of language, as they would at least explore these changes. I myself belong to those people, I do not like changes associated with cosmetic amenities, breaking the old-established solution for years. I also understand the people who came from the languages Java and C#, which is not familiar with the semantics of the class value. But just such a case, when the inertia hinders the development of language and prevents them winning at least a substantial number of positions. (Namely: the replacement of old C++. Yes, I believe, without C++ replacement functionality D will not be needed.) I think the problem is real and requires action. Not sure it will be a value semantic. Maybe we come up with something entirely new? I do not know. But the problem is that one must at least acknowledge it and not come off the common phrases that "your use cases are not serious" etc.
Feb 20 2009
parent reply Don <nospam nospam.com> writes:
Weed wrote:
 Don пишет:
 Weed wrote:
 Christopher Wright пишет:
 Weed wrote:
 Kagamin пишет:
 Weed Wrote:

 Will the language change?



could, but nobody has yet, and the issue comes up every few months.


cases involving classes, but haven't given _any_ details about the contents of those classes. It's possible that you have indeed found use cases, but you haven't actually shown them here.

I do not understand the claims. For example, any class that implements the mathematical object (matrix, vector, number, etc.) is suitable for example. I think it is obvious.

Absolutely not! Those cases involve no polymorphism! No virtual function calls.
 Yes, the discussion in this thread showed that almost always possible
 for each case to find a different approach, using additives and other
 scary code. But what if these "perversions" flaw somewhere in the idea
 of a "reference-only" type?
 
 I understand people who are against the changes of language, as they
 would at least explore these changes. I myself belong to those people, I
 do not like changes associated with cosmetic amenities, breaking the
 old-established solution for years.

I don't think the resistance comes from intertia and committment to the "long-established solution". There's plenty of C++ programmers here (including myself).
 I also understand the people who came from the languages Java and C#,
 which is not familiar with the semantics of the class value.
 
 But just such a case, when the inertia hinders the development of
 language and prevents them winning at least a substantial number of
 positions. (Namely: the replacement of old C++. Yes, I believe, without
 C++ replacement functionality D will not be needed.)

 I think the problem is real and requires action. Not sure it will be a
 value semantic. Maybe we come up with something entirely new? I do not know.
 
 But the problem is that one must at least acknowledge it and not come
 off the common phrases that "your use cases are not serious" etc.

You really MUST start from a solid use case. I'm genuinely surprised that you've had so much trouble coming up with one; it suggests to me that you're not looking at the right problem.
Feb 20 2009
parent reply Weed <resume755 mail.ru> writes:
Don пишет:
 Weed wrote:
 Don пишет:
 Weed wrote:
 Christopher Wright пишет:
 Weed wrote:
 Kagamin пишет:
 Weed Wrote:

 Will the language change?



could, but nobody has yet, and the issue comes up every few months.


cases involving classes, but haven't given _any_ details about the contents of those classes. It's possible that you have indeed found use cases, but you haven't actually shown them here.

I do not understand the claims. For example, any class that implements the mathematical object (matrix, vector, number, etc.) is suitable for example. I think it is obvious.

Absolutely not! Those cases involve no polymorphism! No virtual function calls.

I do not understand why. They are not principally to demonstrate the problem, but certainly should be able to use them. It is possible that in the real code these opportunities are not used, but tomorrow they may need it, and a day after we again decide to remove them. This should not affect the syntax or the choice between the structures+mixins and classes, and so on!
 
 Yes, the discussion in this thread showed that almost always possible
 for each case to find a different approach, using additives and other
 scary code. But what if these "perversions" flaw somewhere in the idea
 of a "reference-only" type?

 I understand people who are against the changes of language, as they
 would at least explore these changes. I myself belong to those people, I
 do not like changes associated with cosmetic amenities, breaking the
 old-established solution for years.

I don't think the resistance comes from intertia and committment to the "long-established solution". There's plenty of C++ programmers here (including myself).

All your opinion. I proceed from the assumption that the programmer should be lazy. :) This positively affects the quality of the code and its reuse
 
 I also understand the people who came from the languages Java and C#,
 which is not familiar with the semantics of the class value.

 But just such a case, when the inertia hinders the development of
 language and prevents them winning at least a substantial number of
 positions. (Namely: the replacement of old C++. Yes, I believe, without
 C++ replacement functionality D will not be needed.)

 I think the problem is real and requires action. Not sure it will be a
 value semantic. Maybe we come up with something entirely new? I do not
 know.

 But the problem is that one must at least acknowledge it and not come
 off the common phrases that "your use cases are not serious" etc.

You really MUST start from a solid use case. I'm genuinely surprised that you've had so much trouble coming up with one; it suggests to me that you're not looking at the right problem.

I design a future project. I can not spend time on his coding in advance knowing that the problem about which we speak will not allow me to achieve the desired properties of the finished product. Thus, one can expect from me detailed use cases from real life. Yes, probably each issue I will be able to circumvent by using tricks, repeatedly described here. But at what cost? There will be an unjustified waste of time, the developer and a serious increase of complexity of the program (compared to C++)
Feb 20 2009
parent Don <nospam nospam.com> writes:
Weed wrote:
 Don пишет:
 Weed wrote:
 Don пишет:
 Weed wrote:
 Christopher Wright пишет:
 Weed wrote:
 Kagamin пишет:
 Weed Wrote:

 Will the language change?



could, but nobody has yet, and the issue comes up every few months.


cases involving classes, but haven't given _any_ details about the contents of those classes. It's possible that you have indeed found use cases, but you haven't actually shown them here.

For example, any class that implements the mathematical object (matrix, vector, number, etc.) is suitable for example. I think it is obvious.

calls.

I do not understand why. They are not principally to demonstrate the problem, but certainly should be able to use them.

AFAIK, OO linear algebra libraries have never been successful. Lots of people have tried.
 It is possible that in the real code these opportunities are not used,
 but tomorrow they may need it, and a day after we again decide to remove
 them.
 
 This should not affect the syntax or the choice between the
 structures+mixins and classes, and so on!
 
 
 Yes, the discussion in this thread showed that almost always possible
 for each case to find a different approach, using additives and other
 scary code. But what if these "perversions" flaw somewhere in the idea
 of a "reference-only" type?

 I understand people who are against the changes of language, as they
 would at least explore these changes. I myself belong to those people, I
 do not like changes associated with cosmetic amenities, breaking the
 old-established solution for years.

"long-established solution". There's plenty of C++ programmers here (including myself).

All your opinion. I proceed from the assumption that the programmer should be lazy. :) This positively affects the quality of the code and its reuse
 I also understand the people who came from the languages Java and C#,
 which is not familiar with the semantics of the class value.

 But just such a case, when the inertia hinders the development of
 language and prevents them winning at least a substantial number of
 positions. (Namely: the replacement of old C++. Yes, I believe, without
 C++ replacement functionality D will not be needed.)
 I think the problem is real and requires action. Not sure it will be a
 value semantic. Maybe we come up with something entirely new? I do not
 know.

 But the problem is that one must at least acknowledge it and not come
 off the common phrases that "your use cases are not serious" etc.

that you've had so much trouble coming up with one; it suggests to me that you're not looking at the right problem.

I design a future project. I can not spend time on his coding in advance knowing that the problem about which we speak will not allow me to achieve the desired properties of the finished product. Thus, one can expect from me detailed use cases from real life. Yes, probably each issue I will be able to circumvent by using tricks, repeatedly described here. But at what cost? There will be an unjustified waste of time, the developer and a serious increase of complexity of the program (compared to C++)

You are asking for the language to be massively changed, but are not giving any examples of where it is actually required. You're just providing rhetoric that it is necessary. Up to now, it reminds me of the Weapons of Mass Destruction in Iraq. You're telling us they're there, but without any evidence. Give a use case!
Feb 21 2009