www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Give struct the status it deserves

reply Hong <Hong_member pathlink.com> writes:
Structs are second class citizens of... or maybe refugees of D, they are victims
of discrimination.
Following are the reasons:

1. most D programmers don't use struct, unless they happen to be the poor guy
writing the C interface.

2. to use structs you have to use pointers, but the spec says pointers are only
provided for C compatibility, structs are to be avoided as long as the world
spins.

3. pointers + overloaded operators make a mess.... this is a less offensive
example: (*o) = (*t)[(*i) * (*j)] ... the pointers and operators are abusing
each other.

4. structs cannot have constructor, "static opCall" is best you can do.

5. structs are more efficient? Not when structs are passed around by value. To
change a struct member: 1. make a copy 2. change the copy 3. Copy the copy back
into the original location. Two damned copies for.... efficiency.

6. yes, you can use pointers to change a struct member without copying, if you
can be bothered to define 2 accessor methods, one returns a copy another one
returns a pointer.

7. Most standard containers (DTL) do not allow a pointer to a contained struct
to be retrieved, all changes, or just about anything has to be done via copies.

8. structs have different behaviours between array and Vector, array [] returns
a reference, Vector [] returns a copy, nice rookie trap.


Imho, following are little suggestions for structs to make first class citizens
of D:

1. have some sort of reference semantic to replace pointers. Pointers are not
necessary unless structs are used with "new"

2. add constructor for struct, destructors are not necessary.

I use structs because I need efficiency and stack allocated data structures,
however, it is painful to see a C++ program turns into a C like program when
ported to D.
Mar 25 2006
next sibling parent reply Hasan Aljudy <hasan.aljudy gmail.com> writes:
Hong wrote:
 Structs are second class citizens of... or maybe refugees of D, they are
victims
 of discrimination.
 Following are the reasons:
 
 1. most D programmers don't use struct, unless they happen to be the poor guy
 writing the C interface.
 
 2. to use structs you have to use pointers, but the spec says pointers are only
 provided for C compatibility, structs are to be avoided as long as the world
 spins.
 
 3. pointers + overloaded operators make a mess.... this is a less offensive
 example: (*o) = (*t)[(*i) * (*j)] ... the pointers and operators are abusing
 each other.
 
 4. structs cannot have constructor, "static opCall" is best you can do.
 
 5. structs are more efficient? Not when structs are passed around by value. To
 change a struct member: 1. make a copy 2. change the copy 3. Copy the copy back
 into the original location. Two damned copies for.... efficiency.
 
 6. yes, you can use pointers to change a struct member without copying, if you
 can be bothered to define 2 accessor methods, one returns a copy another one
 returns a pointer.
 
 7. Most standard containers (DTL) do not allow a pointer to a contained struct
 to be retrieved, all changes, or just about anything has to be done via copies.
 
 8. structs have different behaviours between array and Vector, array [] returns
 a reference, Vector [] returns a copy, nice rookie trap.
 
 
 Imho, following are little suggestions for structs to make first class citizens
 of D:
 
 1. have some sort of reference semantic to replace pointers. Pointers are not
 necessary unless structs are used with "new"
 
 2. add constructor for struct, destructors are not necessary.
 
 I use structs because I need efficiency and stack allocated data structures,
 however, it is painful to see a C++ program turns into a C like program when
 ported to D.
 
 

structs are a light wieght alternative to classes. Seriously, if structs don't suffice for your needs, use classes! You won't lose /that/ much performance.
Mar 25 2006
parent reply Hong <Hong_member pathlink.com> writes:
Thanks, that is one work arounds, the avoidance approach.
There are 2 approaches to structs,

1. Minimalist approach, use structs only for C interfaces, and tiny data types
such as Point for GUI programming. Most Java programmer would prefer this.

2. Use structs for all data structures that do not require heap allocation, such
as containers. MinTL is a good example. However, current support for struct
makes this approach difficult. People used to C++ might prefer this approach,
when performance is critical. If D is to gain a foot hold on the high
performance computing world, such as scientific computing, more support for
struct will be needed.

In article <e044ro$ifn$1 digitaldaemon.com>, Hasan Aljudy says...
structs are a light wieght alternative to classes.
Seriously, if structs don't suffice for your needs, use classes! You 
won't lose /that/ much performance.

Mar 25 2006
parent reply Hasan Aljudy <hasan.aljudy gmail.com> writes:
What's wrong with classes?!

Classes won't make your code slow.

Hong wrote:
 Thanks, that is one work arounds, the avoidance approach.
 There are 2 approaches to structs,
 
 1. Minimalist approach, use structs only for C interfaces, and tiny data types
 such as Point for GUI programming. Most Java programmer would prefer this.
 
 2. Use structs for all data structures that do not require heap allocation,
such
 as containers. MinTL is a good example. However, current support for struct
 makes this approach difficult. People used to C++ might prefer this approach,
 when performance is critical. If D is to gain a foot hold on the high
 performance computing world, such as scientific computing, more support for
 struct will be needed.
 
 In article <e044ro$ifn$1 digitaldaemon.com>, Hasan Aljudy says...
 
structs are a light wieght alternative to classes.
Seriously, if structs don't suffice for your needs, use classes! You 
won't lose /that/ much performance.


Mar 26 2006
next sibling parent BCS <BCS_member pathlink.com> writes:
In article <e07506$1ss2$1 digitaldaemon.com>, Hasan Aljudy says...
What's wrong with classes?!

Classes won't make your code slow.

HA!! I know someone who has done research on exactly that and says that they make code VARY slow (as in several times slower) I'll talk to him tomorrow and see if I can get some links to post. One example of things that make classes slow is that all methods are called by reference (from object* get vtbl (1), add index (2), load (3) call from ptr(4) as opposed to call function from incline address) The other thing is that structs don't have a vtbl in each instance and therefor can be used to map data to files and hardwear.
Mar 26 2006
prev sibling parent reply Hong <Hong_member pathlink.com> writes:
Ok, even people are telling me that class is not slower, I have decided to make
a benchmark to test them out. Here they are,

Version using struct
http://members.iinet.net.au/~honglee/benchmark/benchmarkstruct.d

Version using class
http://members.iinet.net.au/~honglee/benchmark/benchmarkclass.d

On my computer, the struct version runs 300 times faster than the class version.
Interestingly, deleting the object explicitly in the class version actually
slowed the program even further.

The difference seems real.

In article <e07506$1ss2$1 digitaldaemon.com>, Hasan Aljudy says...
What's wrong with classes?!

Classes won't make your code slow.

Mar 26 2006
next sibling parent Derek Parnell <derek psych.ward> writes:
On Mon, 27 Mar 2006 00:32:40 +0000 (UTC), Hong wrote:

 Ok, even people are telling me that class is not slower, I have decided to make
 a benchmark to test them out. Here they are,
 
 Version using struct
 http://members.iinet.net.au/~honglee/benchmark/benchmarkstruct.d
 
 Version using class
 http://members.iinet.net.au/~honglee/benchmark/benchmarkclass.d
 
 On my computer, the struct version runs 300 times faster than the class
version.
 Interestingly, deleting the object explicitly in the class version actually
 slowed the program even further.
 
 The difference seems real.

On my machine, the 'class' version was 135 times slower. I changed the 'struct' version to use a pointer (Point *p = new Point;) and that now took 50 times slower than the first struct version. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocracy!" 27/03/2006 12:05:14 PM
Mar 26 2006
prev sibling parent reply Hasan Aljudy <hasan.aljudy gmail.com> writes:
Heh, this is so flawed.

it's got nothing to do with classes vs. structs.

The benchmark is designed so that the struct version runs faster than 
the class version.

change the class version to:

	Point p = new Point();
	for (uint i = 0; i < n; ++i)
	{		
		Point.count += p.x;
		// delete p;
	}

and the struct version to:

	for (uint i = 0; i < n; ++i)
	{
		Point * p = new Point();
		Point.count += p.x;
	}

These programs don't logically do anything different. However, the class 
version now runs much much faster than the struct version.

on my machine:
class

operation took 0 m 0.25 s
struct

operation took 0 m 15.265 s Try to come up with a more realistic example, and optamize both versions. This way, you can prove that it's the class vs. struct issue and not an optimization issue. Hong wrote:
 Ok, even people are telling me that class is not slower, I have decided to make
 a benchmark to test them out. Here they are,
 
 Version using struct
 http://members.iinet.net.au/~honglee/benchmark/benchmarkstruct.d
 
 Version using class
 http://members.iinet.net.au/~honglee/benchmark/benchmarkclass.d
 
 On my computer, the struct version runs 300 times faster than the class
version.
 Interestingly, deleting the object explicitly in the class version actually
 slowed the program even further.
 
 The difference seems real.
 
 In article <e07506$1ss2$1 digitaldaemon.com>, Hasan Aljudy says...
 
What's wrong with classes?!

Classes won't make your code slow.


Mar 26 2006
next sibling parent reply Dave <Dave_member pathlink.com> writes:
These run as the same speed on my system, which is pretty cool since the
accessor function for class Point.x is vitual. If you finalize it the class
version is actually faster. No doubt the OP has a point when you try to alloc.
classes in a tight loop, and I'd agree that stack allocated classes would be
great, but if you can work around the allocation bottleneck then classes tend to
be faster and easier to code because they are passed around by reference w/o
pointer syntax.

class Point
{
static int count = 0;
int _x = 1;
int x() { return _x; } // this is vitual
}

Point p = new Point();
for (uint i = 0; i < n; ++i)
{		
Point.count += p.x;
// delete p;
}

;---

struct Point
{
static int count = 0;
int _x = 1;
package int x() { return _x; }
}

Point p;
for (uint i = 0; i < n; ++i)
{
Point.count += p.x;
}

In article <e07ka1$2kml$1 digitaldaemon.com>, Hasan Aljudy says...
Heh, this is so flawed.

it's got nothing to do with classes vs. structs.

The benchmark is designed so that the struct version runs faster than 
the class version.

change the class version to:

	Point p = new Point();
	for (uint i = 0; i < n; ++i)
	{		
		Point.count += p.x;
		// delete p;
	}

and the struct version to:

	for (uint i = 0; i < n; ++i)
	{
		Point * p = new Point();
		Point.count += p.x;
	}

These programs don't logically do anything different. However, the class 
version now runs much much faster than the struct version.

on my machine:
class

operation took 0 m 0.25 s
struct

operation took 0 m 15.265 s Try to come up with a more realistic example, and optamize both versions. This way, you can prove that it's the class vs. struct issue and not an optimization issue. Hong wrote:
 Ok, even people are telling me that class is not slower, I have decided to make
 a benchmark to test them out. Here they are,
 
 Version using struct
 http://members.iinet.net.au/~honglee/benchmark/benchmarkstruct.d
 
 Version using class
 http://members.iinet.net.au/~honglee/benchmark/benchmarkclass.d
 
 On my computer, the struct version runs 300 times faster than the class
version.
 Interestingly, deleting the object explicitly in the class version actually
 slowed the program even further.
 
 The difference seems real.
 
 In article <e07506$1ss2$1 digitaldaemon.com>, Hasan Aljudy says...
 
What's wrong with classes?!

Classes won't make your code slow.



Mar 26 2006
parent reply Derek Parnell <derek psych.ward> writes:
On Mon, 27 Mar 2006 04:11:37 +0000 (UTC), Dave wrote:

 These run as the same speed on my system, which is pretty cool since the
 accessor function for class Point.x is vitual. If you finalize it the class
 version is actually faster. No doubt the OP has a point when you try to alloc.
 classes in a tight loop, and I'd agree that stack allocated classes would be
 great, but if you can work around the allocation bottleneck then classes tend
to
 be faster and easier to code because they are passed around by reference w/o
 pointer syntax.

I wasn't so sure about the 'passed around' claim. So I amended my test to pass the object and the results were almost identical. So it appears that passing structs is not an issue at all. In fact, if you look at the generated machine code, the struct seems to be passed by reference even if you code otherwise. ------------------ version(test_struct_stack) { struct Point { static int count = 0; int x() { return 1; } } void upd(Point p) { Point.count += p.x; } void doit() { Point p; upd(p); } const static char[] Title = "STACK STRUCTS"; } version(test_struct_heap) { struct Point { static int count = 0; int x() { return 1; } } void upd(Point p) { Point.count += p.x; } void doit() { Point *p = new Point; upd(*p); } const static char[] Title = "HEAP STRUCTS"; } version(test_class) { class Point { static int count = 0; int x() { return 1; } } void upd(Point p) { Point.count += p.x; } void doit() { Point p = new Point; upd(p); } const static char[] Title = "CLASSES"; } ----------------------- -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocracy!" 27/03/2006 4:02:10 PM
Mar 26 2006
parent reply Dave <Dave_member pathlink.com> writes:
In article <17vpw8ovosqie$.1f65d29ys83k6$.dlg 40tude.net>, Derek Parnell says...
On Mon, 27 Mar 2006 04:11:37 +0000 (UTC), Dave wrote:

 These run as the same speed on my system, which is pretty cool since the
 accessor function for class Point.x is vitual. If you finalize it the class
 version is actually faster. No doubt the OP has a point when you try to alloc.
 classes in a tight loop, and I'd agree that stack allocated classes would be
 great, but if you can work around the allocation bottleneck then classes tend
to
 be faster and easier to code because they are passed around by reference w/o
 pointer syntax.

I wasn't so sure about the 'passed around' claim. So I amended my test to pass the object and the results were almost identical. So it appears that passing structs is not an issue at all. In fact, if you look at the generated machine code, the struct seems to be passed by reference even if you code otherwise.

Not what I'm seeing - give this a try: import std.stdio, std.date; struct S { char[1000] str; } class C { char[1000] str; } void main() { const int y = 1_000_000; { S s; d_time st = getUTCtime(); for(int x = 0; x < y; x++) { fooS(s); } d_time en = getUTCtime(); writefln((en-st) / cast(double)TicksPerSecond); } { C c = new C; d_time st = getUTCtime(); for(int x = 0; x < y; x++) { fooC(c); } d_time en = getUTCtime(); writefln((en-st) / cast(double)TicksPerSecond); } } void fooS(S s) { s.str[] = '\0'; } void fooC(C c) { c.str[] = '\0'; }
Mar 27 2006
parent Derek Parnell <derek psych.ward> writes:
On Mon, 27 Mar 2006 15:16:38 +0000 (UTC), Dave wrote:

 In article <17vpw8ovosqie$.1f65d29ys83k6$.dlg 40tude.net>, Derek Parnell
says...
On Mon, 27 Mar 2006 04:11:37 +0000 (UTC), Dave wrote:

 These run as the same speed on my system, which is pretty cool since the
 accessor function for class Point.x is vitual. If you finalize it the class
 version is actually faster. No doubt the OP has a point when you try to alloc.
 classes in a tight loop, and I'd agree that stack allocated classes would be
 great, but if you can work around the allocation bottleneck then classes tend
to
 be faster and easier to code because they are passed around by reference w/o
 pointer syntax.

I wasn't so sure about the 'passed around' claim. So I amended my test to pass the object and the results were almost identical. So it appears that passing structs is not an issue at all. In fact, if you look at the generated machine code, the struct seems to be passed by reference even if you code otherwise.

Not what I'm seeing - give this a try:

You're right. I was using the 'inout' paradigm which uses byRef method. If you just use the 'in' paradigm, then it does copy struct data to the called routine. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocracy!" 28/03/2006 10:19:18 AM
Mar 27 2006
prev sibling next sibling parent Hong <Hong_member pathlink.com> writes:
Probably should point out that the major point of the benchmark is to test the
allocation and deallocation of 100000000 object or struct, i.e. test the
performance between "stack allocation" and "heap allocation with GC".

Anyway, your modified version is very flawed, for a number of reasons,

* only one allocation is done for the class version but 100000000 structs were
created, clearly not a comparison here.

* structs are more or less designed to use stack allocation, but you forced it
to use heap allocation and made it GC collected. This does not demonstrate how
structs are used 99% of the time.

In article <e07ka1$2kml$1 digitaldaemon.com>, Hasan Aljudy says...
Heh, this is so flawed.

it's got nothing to do with classes vs. structs.

The benchmark is designed so that the struct version runs faster than 
the class version.

change the class version to:

	Point p = new Point();
	for (uint i = 0; i < n; ++i)
	{		
		Point.count += p.x;
		// delete p;
	}

and the struct version to:

	for (uint i = 0; i < n; ++i)
	{
		Point * p = new Point();
		Point.count += p.x;
	}

These programs don't logically do anything different. However, the class 
version now runs much much faster than the struct version.

on my machine:
class

operation took 0 m 0.25 s
struct

operation took 0 m 15.265 s Try to come up with a more realistic example, and optamize both versions. This way, you can prove that it's the class vs. struct issue and not an optimization issue. Hong wrote:
 Ok, even people are telling me that class is not slower, I have decided to make
 a benchmark to test them out. Here they are,
 
 Version using struct
 http://members.iinet.net.au/~honglee/benchmark/benchmarkstruct.d
 
 Version using class
 http://members.iinet.net.au/~honglee/benchmark/benchmarkclass.d
 
 On my computer, the struct version runs 300 times faster than the class
version.
 Interestingly, deleting the object explicitly in the class version actually
 slowed the program even further.
 
 The difference seems real.
 
 In article <e07506$1ss2$1 digitaldaemon.com>, Hasan Aljudy says...
 
What's wrong with classes?!

Classes won't make your code slow.



Mar 26 2006
prev sibling next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Sun, 26 Mar 2006 19:57:08 -0700, Hasan Aljudy wrote:

 Heh, this is so flawed.
 
 it's got nothing to do with classes vs. structs.
 
 The benchmark is designed so that the struct version runs faster than 
 the class version.
 
 change the class version to:
 
 	Point p = new Point();
 	for (uint i = 0; i < n; ++i)
 	{		
 		Point.count += p.x;
 		// delete p;
 	}
 
 and the struct version to:
 
 	for (uint i = 0; i < n; ++i)
 	{
 		Point * p = new Point();
 		Point.count += p.x;
 	}
 
 These programs don't logically do anything different. However, the class 
 version now runs much much faster than the struct version.

I think that you're amendment does not make them logically or otherwise the same. Your class version does one GC operation (create a new object) but your struct version does one GC operation per iteration - a new Point is allocated for every iteration. Here is my amended benchmark test program. In this, I test structs created on the stack, structs created on the heap, and classes (created on the heap of course). The class edition runs about 3 times longer than the heap struct edition and about 16 times longer that the stack struct edition. The heap struct edition runs about 5 times longer than the stack struct edition. So given that there is a big difference between classes (on the heap) and structs on the heap, those differences seem to be attributable to the implementation of classes over the implementation of structs. In these tests, all editions do one 'create' per iteration, and all use a member function in the created object. So in effect, the only differences are where the object resides and whether its a class or struct. // -------------- code starts ------------------- private import std.c.stdio; private import std.c.time; private import std.stdio; const static uint n = 100000000; version(test_struct_stack) { struct Point { static int count = 0; int x() { return 1; } } void doit() { Point p; Point.count += p.x; } const static char[] Title = "STACK STRUCTS"; } version(test_struct_heap) { struct Point { static int count = 0; int x() { return 1; } } void doit() { Point *p = new Point; Point.count += p.x; } const static char[] Title = "HEAP STRUCTS"; } version(test_class) { class Point { static int count = 0; int x() { return 1; } } void doit() { Point p = new Point; Point.count += p.x; } const static char[] Title = "CLASSES"; } int main() { clock_t time1 = clock(); for (uint i = 0; i < n; ++i) { doit(); } clock_t time2 = clock(); double seconds = (time2 - time1); seconds /= CLOCKS_PER_SEC; int minutes = cast(int)(seconds/60); seconds -= minutes*60; writefln("Testing %s %d operation took %sm %ss", Title, Point.count, minutes, seconds); return 0; } // -------------- code ends ------------------- Here is the compile and run output. --------------------------- C:\temp>build bt -version=test_struct_stack -Tbtss -full Path and Version : y:\util\build.exe v2.9(1197) built on Wed Aug 10 11:03:42 2005 y:\dmd\bin\..\..\dm\bin\link.exe bt,btss.exe,,user32+kernel32,btss.def/noi; C:\temp>build bt -version=test_struct_heap -Tbtsh -full Path and Version : y:\util\build.exe v2.9(1197) built on Wed Aug 10 11:03:42 2005 y:\dmd\bin\..\..\dm\bin\link.exe bt,btsh.exe,,user32+kernel32,btsh.def/noi; C:\temp>build bt -version=test_class -Tbtc -full Path and Version : y:\util\build.exe v2.9(1197) built on Wed Aug 10 11:03:42 2005 y:\dmd\bin\..\..\dm\bin\link.exe bt,btc.exe,,user32+kernel32,btc.def/noi; C:\temp>btss Testing STACK STRUCTS 100000000 operation took 0m 5.648s C:\temp>btsh Testing HEAP STRUCTS 100000000 operation took 0m 27.68s C:\temp>btc Testing CLASSES 100000000 operation took 1m 31.462s C:\temp> --------------------------- -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocracy!" 27/03/2006 3:29:29 PM
Mar 26 2006
parent reply Hasan Aljudy <hasan.aljudy gmail.com> writes:
Derek Parnell wrote:
 On Sun, 26 Mar 2006 19:57:08 -0700, Hasan Aljudy wrote:
 
 
Heh, this is so flawed.

it's got nothing to do with classes vs. structs.

The benchmark is designed so that the struct version runs faster than 
the class version.

change the class version to:

	Point p = new Point();
	for (uint i = 0; i < n; ++i)
	{		
		Point.count += p.x;
		// delete p;
	}

and the struct version to:

	for (uint i = 0; i < n; ++i)
	{
		Point * p = new Point();
		Point.count += p.x;
	}

These programs don't logically do anything different. However, the class 
version now runs much much faster than the struct version.

I think that you're amendment does not make them logically or otherwise the same. Your class version does one GC operation (create a new object) but your struct version does one GC operation per iteration - a new Point is allocated for every iteration.

The program doesn't really do anything, you know. Creating a new instance serves no purpose at all. So, as an optimization, you can do away without constant allocation; just reuse the same object. The example serves very well to illustrate how slow it is to allocate millions of objects on the heap in one go. However, it provides no proof that, in pratical situations, structs are much much faster than classes. Which is basically what the example tries to hint at.
Mar 26 2006
parent reply "Derek Parnell" <derek psych.ward> writes:
On Mon, 27 Mar 2006 17:12:34 +1100, Hasan Aljudy <hasan.aljudy gmail.com>  
wrote:

 Derek Parnell wrote:
 On Sun, 26 Mar 2006 19:57:08 -0700, Hasan Aljudy wrote:

 Heh, this is so flawed.

 it's got nothing to do with classes vs. structs.

 The benchmark is designed so that the struct version runs faster than  
 the class version.

 change the class version to:

 	Point p = new Point();
 	for (uint i = 0; i < n; ++i)
 	{		
 		Point.count += p.x;
 		// delete p;
 	}

 and the struct version to:

 	for (uint i = 0; i < n; ++i)
 	{
 		Point * p = new Point();
 		Point.count += p.x;
 	}

 These programs don't logically do anything different. However, the  
 class version now runs much much faster than the struct version.

otherwise the same. Your class version does one GC operation (create a new object) but your struct version does one GC operation per iteration - a new Point is allocated for every iteration.

The program doesn't really do anything, you know.

Wow! Really?! Are you sure?
 Creating a new instance serves no purpose at all. So, as an  
 optimization, you can do away without constant allocation; just reuse  
 the same object.

I think you are deliberately missing the point, Hasan. I thought the point was to show that heap-allocated things are inherently slower than stack-allocated things. The original example didn't do a good job of that but my subsequent example does I believe. Can you please comment on my example code?
 The example serves very well to illustrate how slow it is to allocate  
 millions of objects on the heap in one go.
 However, it provides no proof that, in pratical situations, structs are  
 much much faster than classes. Which is basically what the example tries  
 to hint at.

But I think my example does show that classes (allocated on the heap) are slower than structs whether allocated on the heap or on the stack. Classes are slower than structs. Prove otherwise. I'm happy to be shown that I'm wrong. -- Derek Parnell Melbourne, Australia
Mar 27 2006
next sibling parent reply Wolfgang Draxinger <wdraxinger darkstargames.de> writes:
Derek Parnell wrote:

 Creating a new instance serves no purpose at all. So, as an
 optimization, you can do away without constant allocation;
 just reuse the same object.

I think you are deliberately missing the point, Hasan. I thought the point was to show that heap-allocated things are inherently slower than stack-allocated things.

It makes no difference for the use of objects if they are on the heap or on the stack. Both are only portions of the same address space (and may even clash if they interpenetrate). The only thing that makes things slow is the allocation of heap memory which may cause the garbage collector to kick in. But once the memory is allocated it can be used as fast as stack memory.
 Classes are slower than structs. Prove otherwise. I'm happy to
 be shown that I'm wrong.

I don't see a reason for this. There's no difference in accessing the data. Put a struct on the stack, the compiler will compile it to access it indirectly (i.e. by "pointers"). Allocate a class on the heap, it get's accessed in the very same way. As a rule of thumb: Any data you going to work with in a D program is encapsulated in classes. Stuff that gets dumped into files or memory goes into structs. It's that simple. -- Wolfgang Draxinger
Mar 27 2006
next sibling parent Alexander Panek <alexander.panek brainsware.org> writes:
Amen!

Wolfgang Draxinger wrote:
 Derek Parnell wrote:
 
 Creating a new instance serves no purpose at all. So, as an
 optimization, you can do away without constant allocation;
 just reuse the same object.

thought the point was to show that heap-allocated things are inherently slower than stack-allocated things.

It makes no difference for the use of objects if they are on the heap or on the stack. Both are only portions of the same address space (and may even clash if they interpenetrate). The only thing that makes things slow is the allocation of heap memory which may cause the garbage collector to kick in. But once the memory is allocated it can be used as fast as stack memory.
 Classes are slower than structs. Prove otherwise. I'm happy to
 be shown that I'm wrong.

I don't see a reason for this. There's no difference in accessing the data. Put a struct on the stack, the compiler will compile it to access it indirectly (i.e. by "pointers"). Allocate a class on the heap, it get's accessed in the very same way. As a rule of thumb: Any data you going to work with in a D program is encapsulated in classes. Stuff that gets dumped into files or memory goes into structs. It's that simple.

Mar 27 2006
prev sibling next sibling parent Hong <Hong_member pathlink.com> writes:
Derek made the benchmark quite nice, thank you for that.

Wolfgang wrote:
The only thing that makes things slow is the allocation of heap
memory which may cause the garbage collector to kick in.

Wolfgang wrote:
 But
once the memory is allocated it can be used as fast as stack
memory.

This is not entirely correct. Memory fragmentation means that cache miss is much more frequent on heap allocated data structures. Struct makes it such that data can be bundled nicely together in memory, making them faster to access. Wolfgang wrote:
I don't see a reason for this. There's no difference in accessing
the data. Put a struct on the stack, the compiler will compile
it to access it indirectly (i.e. by "pointers"). Allocate a
class on the heap, it get's accessed in the very same way.

Derek meant that struct allocation is much faster than class allocation (~150x), allocation needs to be included when measuring performance, not just access speed. Wolfgang wrote:
As a rule of thumb: Any data you going to work with in a D
program is encapsulated in classes. Stuff that gets dumped into
files or memory goes into structs. It's that simple.

This is the minimal struct approach. However, since that we have already shown that struct allocation/deallocation is more than 100x faster, it makes good sense to use struct for data types that require enormous amount of allocation/deallocation, such as Vector3d in computer graphics, or Point in GUI programming. In fact minTL uses struct to create containers.
Mar 27 2006
prev sibling parent "Derek Parnell" <derek psych.ward> writes:
On Mon, 27 Mar 2006 22:40:21 +1100, Wolfgang Draxinger  
<wdraxinger darkstargames.de> wrote:

 Derek Parnell wrote:

 Classes are slower than structs. Prove otherwise. I'm happy to
 be shown that I'm wrong.

I don't see a reason for this. There's no difference in accessing the data. Put a struct on the stack, the compiler will compile it to access it indirectly (i.e. by "pointers"). Allocate a class on the heap, it get's accessed in the very same way.

I really do understand the theory. And on the surface, it would seem that your analysis is valid, however the timings show otherwise. There really is a difference between heap-allocated classes and heap-allocated structs. Classes are slower. Prove it yourself. Write your own benchmark test and submit its results. Critique my example - pull it apart and improve it. Show me where I've bungled. Until then, the figures show that classes are always slower than structs when used for equivalent tasks.
 As a rule of thumb: Any data you going to work with in a D
 program is encapsulated in classes. Stuff that gets dumped into
 files or memory goes into structs. It's that simple.

Yes it is - in theory. Practice may dicate otherwise though. -- Derek Parnell Melbourne, Australia
Mar 27 2006
prev sibling next sibling parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Derek Parnell wrote:
 On Mon, 27 Mar 2006 17:12:34 +1100, Hasan Aljudy 
 <hasan.aljudy gmail.com> wrote:

 The program doesn't really do anything, you know.

Wow! Really?! Are you sure?

Yes, I'm sure. In practical situations, allocating stuff on the heap is not really what you want to do in your program; it's just a way to achieve what you want to do. If you don't understand what I mean, take this very simple example: You want to print numbers 1 through 1000 on the screen. you can do it with a while loop: int i = 0; while( i <= 1000 ) { writefln(i); i++; } but, you can also do it with a for loop: for( int i = 0; i <= 1000; i++ ) { writefln(i); } and a goto int i = 0; doit: i++; writefln(i); if( i <= 1000 ) goto doit; and here's another while loop: int i = 0; while( true ) { writefln(i); i++; if( i > 1000 ) break; } or, you can do it sorta like this: writefln(1); writefln(2); writefln(3); . . . writefln(999); writefln(1000); all these programs do the same thing, they just do it differently. Philisophically, you can argue that these programs do different things, the first one does a while loop, the second one does a for loop, the third one does a goto, the last one does it sequentially .. etc. However, practically, you're doing the same thing, in different ways. Some ways can be faster than the others. Here for example, is a stupid way of doing the above program: for( int i = 0; i <= 1000; i++ ) { writefln(i); for( int j = 0; j < 1000000000; j++ ) { //waste time int y = 10; int x = 5; x = x + y; } } However, it still works. Just .... very slow. This applies to the given benchmark. You're not doing anything, besides wasting heap space and wasting time allocating on the heap.
 
 Creating a new instance serves no purpose at all. So, as an 
 optimization, you can do away without constant allocation; just reuse 
 the same object.

I think you are deliberately missing the point, Hasan. I thought the point was to show that heap-allocated things are inherently slower than stack-allocated things. The original example didn't do a good job of that but my subsequent example does I believe. Can you please comment on my example code?

OK, sorry, I didn't check your example yet. This does not affect my point. Memory allocation is not the only thing you do with classes/structs. I'd like to think of it as just an implementation/optimization issue. One thing were classes would win over structs is pass by reference vs. pass by value. Create a huge struct, with say, 40 or 50 fields, instantiate it once, then pass it around by value inside a loop (say, a 1000 times). Then edit the program and change the struct to a class, and when you create it, use "new", and don't change anything else.
 The example serves very well to illustrate how slow it is to allocate 
 millions of objects on the heap in one go.
 However, it provides no proof that, in pratical situations, structs 
 are much much faster than classes. Which is basically what the example 
 tries to hint at.

But I think my example does show that classes (allocated on the heap) are slower than structs whether allocated on the heap or on the stack. Classes are slower than structs. Prove otherwise. I'm happy to be shown that I'm wrong. --Derek Parnell Melbourne, Australia

Mar 27 2006
prev sibling next sibling parent reply Hasan Aljudy <hasan.aljudy gmail.com> writes:
Derek Parnell wrote:
 Classes are slower than structs. Prove otherwise. I'm happy to be shown  
 that I'm wrong.
 

Here's a slightly modified version of Dave's code. ------- import std.stdio, std.date; struct S { char[10000] str; } class C { char[10000] str; } void main() { const int y = 1_000_000_0; //structs { S s; d_time st = getUTCtime(); for(int x = 0; x < y; x++) { fooS(s); } d_time en = getUTCtime(); writef("struct: "); writefln((en-st) / cast(double)TicksPerSecond); } //classes { C c = new C; d_time st = getUTCtime(); for(int x = 0; x < y; x++) { fooC(c); } d_time en = getUTCtime(); writef("class: "); writefln((en-st) / cast(double)TicksPerSecond); } } void fooS(S s) { s.str[9999] = 15; } void fooC(C c) { c.str[9999] = 15; } ---------- on my machine: C:\test\d\struct.vs.class\pass>dave struct: 44.422 class: 0.046 that's about 1000 times faster 44.422/.046 = 965.696 Note: I don't believe this to be a valid benchmark, but I believe it's just as valid as Hong's benchmark. It's designed to make the class version win.
Mar 27 2006
next sibling parent Dave <Dave_member pathlink.com> writes:
In article <e09vl7$2u5f$1 digitaldaemon.com>, Hasan Aljudy says...
Derek Parnell wrote:
 Classes are slower than structs. Prove otherwise. I'm happy to be shown  
 that I'm wrong.
 

Note: I don't believe this to be a valid benchmark, but I believe it's just as valid as Hong's benchmark. It's designed to make the class version win.

That wasn't my intention <g> I just wanted to point out that classes can have an advantage as well. I think Don Clungston's recent post regarding optimizing temp. creation out of things like opAdd could solve many concerns about having to use structs in place of classes because of heap allocation bottlenecks. - Dave
Mar 27 2006
prev sibling next sibling parent reply Ben Phillips <Ben_member pathlink.com> writes:
Basically what your benchmark (along with Hong's) prove is that classes and
structs each have different uses (duh!). Use structs in situations like Hong's
case where classes would require a ton of allocation, and use classes in
situations like your case where passing by value would induce enormous overhead
costs.

In article <e09vl7$2u5f$1 digitaldaemon.com>, Hasan Aljudy says...
Derek Parnell wrote:
 Classes are slower than structs. Prove otherwise. I'm happy to be shown  
 that I'm wrong.
 

Here's a slightly modified version of Dave's code. ------- import std.stdio, std.date; struct S { char[10000] str; } class C { char[10000] str; } void main() { const int y = 1_000_000_0; //structs { S s; d_time st = getUTCtime(); for(int x = 0; x < y; x++) { fooS(s); } d_time en = getUTCtime(); writef("struct: "); writefln((en-st) / cast(double)TicksPerSecond); } //classes { C c = new C; d_time st = getUTCtime(); for(int x = 0; x < y; x++) { fooC(c); } d_time en = getUTCtime(); writef("class: "); writefln((en-st) / cast(double)TicksPerSecond); } } void fooS(S s) { s.str[9999] = 15; } void fooC(C c) { c.str[9999] = 15; } ---------- on my machine: C:\test\d\struct.vs.class\pass>dave struct: 44.422 class: 0.046 that's about 1000 times faster 44.422/.046 = 965.696 Note: I don't believe this to be a valid benchmark, but I believe it's just as valid as Hong's benchmark. It's designed to make the class version win.

Mar 27 2006
parent reply David Medlock <noone nowhere.com> writes:
Ben Phillips wrote:
 Basically what your benchmark (along with Hong's) prove is that classes and
 structs each have different uses (duh!). Use structs in situations like Hong's
 case where classes would require a ton of allocation, and use classes in
 situations like your case where passing by value would induce enormous overhead
 costs.
 

Or if you still want to use classes, you can use an object pool. Since object pools are a nice paradigm in some instances, I wonder if a good standard set of library primitives could be built into phobos? -DavidM
Mar 28 2006
parent Charles <noone nowhere.com> writes:
 Or if you still want to use classes, you can use an object pool.

 Since object pools are a nice paradigm in some instances, I wonder if a
 good standard set of library primitives could be built into phobos?

Good idea , I was wanting this recently too. David Medlock wrote:
 Ben Phillips wrote:
 
 Basically what your benchmark (along with Hong's) prove is that 
 classes and
 structs each have different uses (duh!). Use structs in situations 
 like Hong's
 case where classes would require a ton of allocation, and use classes in
 situations like your case where passing by value would induce enormous 
 overhead
 costs.

Or if you still want to use classes, you can use an object pool. Since object pools are a nice paradigm in some instances, I wonder if a good standard set of library primitives could be built into phobos? -DavidM

Mar 28 2006
prev sibling parent Derek Parnell <derek psych.ward> writes:
On Mon, 27 Mar 2006 17:23:05 -0700, Hasan Aljudy wrote:

 
 Note: I don't believe this to be a valid benchmark, but I believe it's 
 just as valid as Hong's benchmark.
 
 It's designed to make the class version win.

I understand. To make them equivalent, use the 'inout' calling convention for the struct. Change to 'void fooS(inout S s)' and the timings are the same. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocracy!" 28/03/2006 12:51:07 PM
Mar 27 2006
prev sibling parent reply Bruno Medeiros <daiphoenixNO SPAMlycos.com> writes:
Derek Parnell wrote:
 
 But I think my example does show that classes (allocated on the heap) 
 are slower than structs whether allocated on the heap or on the stack.
 
 Classes are slower than structs. Prove otherwise. I'm happy to be shown 
 that I'm wrong.
 
 --Derek Parnell
 Melbourne, Australia

Now this is odd. I didn't expect classes on the heap to be much slower than structs on the heap. After all, what is the extra overhead of classes? It has the size overhead of having 2 more pointers (for ClassInfo and for the monitor) and the overhead of initializing those pointers when instantiating a class. Anything else? I thought not, however, I tried to run a test (see code below), where I would simulate a class creation with a struct, adding that overhead, yet the struct version is still twice as fast (with or without optimization)! Why is that?? The best that I could find was by looking at the assembly code, and that in the class version a _d_newclass function is called instead of _d_new for the struct version, yet does _d_newclass do more than _d_new other than just initialize the pointers? // -------------- code starts ------------------- private import std.c.stdio; private import std.c.time; private import std.stdio; const static uint n = 100000000; struct PointStruct { void * classinfo; void * monitor; void Construct(void *newclassinfo) { this.classinfo = newclassinfo; this.monitor = cast(void *) 0x98765; } } void doitStruct() { PointStruct *p = new PointStruct; p.Construct(cast(void *) 0x12345678); } final class PointClass { } void doitClass() { PointClass p = new PointClass; } int main() { writefln(PointClass.classinfo.init.length," : ", PointStruct.sizeof); clock_t time1 = clock(); for (uint i = 0; i < n; ++i) { version(test_struct_heap) { doitStruct(); } version(test_class_heap) { doitClass(); } } double seconds = (clock() - time1); seconds /= CLOCKS_PER_SEC; writefln("Testing operation took %ss", seconds); return 0; } // -------------- code ends ------------------- sh-2.04$ build main -version=test_struct_heap -Tbtsh -full c:\devel\D.tools\dmd\bin\..\..\dm\bin\link.exe main,btsh.exe,,user32+kernel32,btsh.def/noi; sh-2.04$ build main -version=test_class_heap -Tbtch -full c:\devel\D.tools\dmd\bin\..\..\dm\bin\link.exe main,btch.exe,,user32+kernel32,btch.def/noi; sh-2.04$ time ./btsh.exe 8 - 8 Testing operation took 18.204s real 0m18.328s user 0m0.015s sys 0m0.015s sh-2.04$ time ./btch.exe 8 - 8 Testing operation took 42.516s real 0m42.625s user 0m0.015s sys 0m0.015s -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Apr 02 2006
parent reply Thomas Kuehne <thomas-dloop kuehne.cn> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Bruno Medeiros schrieb am 2006-04-02:
 The best 
 that I could find was by looking at the assembly code, and that in the 
 class version a _d_newclass function is called instead of _d_new for the 
 struct version, yet does _d_newclass do more than _d_new other than just 
 initialize the pointers?

have a look at: src/phobos/internal/gc/gc.d Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFEL8wo3w+/yD4P9tIRAgSsAJ9pC8xp4D8N5+ykxPueNKw0nmm1OwCgvcs1 U2k6rgOjvcYbtgYUJwjUKWY= =97Mb -----END PGP SIGNATURE-----
Apr 02 2006
next sibling parent reply Bruno Medeiros <daiphoenixNO SPAMlycos.com> writes:
Thomas Kuehne wrote:
 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1
 
 Bruno Medeiros schrieb am 2006-04-02:
 The best 
 that I could find was by looking at the assembly code, and that in the 
 class version a _d_newclass function is called instead of _d_new for the 
 struct version, yet does _d_newclass do more than _d_new other than just 
 initialize the pointers?

have a look at: src/phobos/internal/gc/gc.d Thomas

AGH... seems the Windows search doesn't search inside *.d files, thus, I didn't find that before. Way to go Windows :( Seems I'll have to use the command line. -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Apr 02 2006
next sibling parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Bruno Medeiros wrote:
 Thomas Kuehne wrote:
 
 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1

 Bruno Medeiros schrieb am 2006-04-02:

 The best that I could find was by looking at the assembly code, and 
 that in the class version a _d_newclass function is called instead of 
 _d_new for the struct version, yet does _d_newclass do more than 
 _d_new other than just initialize the pointers?

have a look at: src/phobos/internal/gc/gc.d Thomas

AGH... seems the Windows search doesn't search inside *.d files, thus, I didn't find that before. Way to go Windows :( Seems I'll have to use the command line.

I use Visual Studio's "find in files" feature.
Apr 02 2006
prev sibling parent James Dunne <james.jdunne gmail.com> writes:
Bruno Medeiros wrote:
 Thomas Kuehne wrote:
 
 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1

 Bruno Medeiros schrieb am 2006-04-02:

 The best that I could find was by looking at the assembly code, and 
 that in the class version a _d_newclass function is called instead of 
 _d_new for the struct version, yet does _d_newclass do more than 
 _d_new other than just initialize the pointers?

have a look at: src/phobos/internal/gc/gc.d Thomas

AGH... seems the Windows search doesn't search inside *.d files, thus, I didn't find that before. Way to go Windows :( Seems I'll have to use the command line.

Windows Search only works on file extensions that it has search filters defined for. This is why you can search within Word / Excel (and many other binary format) documents as if they were text. I'm sure there's some way (aside from writing a custom search filter) to get Windows Search to treat '.d' files as text. I'd say the search idea was good, but should've defaulted to searching unknown files as UTF-8 encoded text just for a sensible default that could be worked around. -- Regards, James Dunne
Apr 02 2006
prev sibling parent Bruno Medeiros <daiphoenixNO SPAMlycos.com> writes:
Thomas Kuehne wrote:
 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1
 
 Bruno Medeiros schrieb am 2006-04-02:
 The best 
 that I could find was by looking at the assembly code, and that in the 
 class version a _d_newclass function is called instead of _d_new for the 
 struct version, yet does _d_newclass do more than _d_new other than just 
 initialize the pointers?

have a look at: src/phobos/internal/gc/gc.d Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFEL8wo3w+/yD4P9tIRAgSsAJ9pC8xp4D8N5+ykxPueNKw0nmm1OwCgvcs1 U2k6rgOjvcYbtgYUJwjUKWY= =97Mb -----END PGP SIGNATURE-----

by the call: _gc.setFinalizer(p, &new_finalizer); The class test with this line runs in 40 seconds. With just: _gc.setFinalizer(p, null); it runs in 31 seconds. And finally without the line at all it runs in 19 seconds, which is now quite close to the struct version. Since this class has no destructors (in it self, or in a base class), I think setting up a finalization is redundant, unless there is some need related to the monitor releasing (dont know about it). In any case there is some room for compiler optimization. -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Apr 02 2006
prev sibling parent reply "Rioshin an'Harthen" <rharth75 hotmail.com> writes:
[sorry, couldn't resist :P]

"Hasan Aljudy" <hasan.aljudy gmail.com> wrote:
 Heh, this is so flawed.

 it's got nothing to do with classes vs. structs.

 The benchmark is designed so that the struct version runs faster than the 
 class version.

 change the class version to:

 Point p = new Point();
 for (uint i = 0; i < n; ++i)
 { Point.count += p.x;
 // delete p;
 }

 and the struct version to:

 for (uint i = 0; i < n; ++i)
 {
 Point * p = new Point();
 Point.count += p.x;
 }

 These programs don't logically do anything different. However, the class 
 version now runs much much faster than the struct version.

Heh, this is so flawed. The benchmark is designed so that the class version runs faster than the struct version. change class version to: for (uint i = 0; i < n; ++i) { Point p = new Point(); Point.count += p.x; // delete p; } and the struct version to: Point p; for (uint i = 0; i < n; ++i) { Point.count += p.x; }
 on my machine:
class

operation took 0 m 0.25 s
struct

operation took 0 m 15.265 s

on my machine:
class

operation took 0 m 43.594 s
struct

operation took 0 m 0.218 s
 Try to come up with a more realistic example, and optamize both versions. 
 This way, you can prove that it's the class vs. struct issue and not an 
 optimization issue.

Maybe you should try to come up with a more realistic example, and optimize both. Now you optimized the class version, but not the struct version. It's easy to claim classes as faster when you selectively optimize one and not the other. I intentionally optimized the other way around. If your "optimization" proves classes are faster, then mine proves structs are faster. And the code is still the same - there's no logical difference in how they work (except for garbage collecting much more in the class version, as you had in the struct version). To be sure, if both are optimized: // struct Point p; for (uint i = 0; i < n; ++i) { p.count += p.x; } // class Point p = new Point(); for (uint i = 0; i < n; ++i) { p.count += p.x; // delete p; } the times for the class version change to:
class

operation took 0 m 0.25 s which is equivalent with the 0.218 s it took for the struct version. If you want to prove classes faster than structs, please don't go about it optimizing only the class version and not the struct one. Otherwise, you end up looking like a fool.
Mar 27 2006
parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Heheh, how funny!

Dude, you're re-iterating my point exactly.

I was *not* trying to prove that classes are faster than structs; I was 
just proving that the example is flawed, by providing a similarily 
flawed example!!

Rioshin an'Harthen wrote:
 [sorry, couldn't resist :P]
 
 "Hasan Aljudy" <hasan.aljudy gmail.com> wrote:
 Heh, this is so flawed.

 it's got nothing to do with classes vs. structs.

 The benchmark is designed so that the struct version runs faster than the 
 class version.

 change the class version to:

 Point p = new Point();
 for (uint i = 0; i < n; ++i)
 { Point.count += p.x;
 // delete p;
 }

 and the struct version to:

 for (uint i = 0; i < n; ++i)
 {
 Point * p = new Point();
 Point.count += p.x;
 }

 These programs don't logically do anything different. However, the class 
 version now runs much much faster than the struct version.

Heh, this is so flawed. The benchmark is designed so that the class version runs faster than the struct version. change class version to: for (uint i = 0; i < n; ++i) { Point p = new Point(); Point.count += p.x; // delete p; } and the struct version to: Point p; for (uint i = 0; i < n; ++i) { Point.count += p.x; }
 on my machine:
 class

operation took 0 m 0.25 s
 struct

operation took 0 m 15.265 s

on my machine:
 class

operation took 0 m 43.594 s
 struct

operation took 0 m 0.218 s
 Try to come up with a more realistic example, and optamize both versions. 
 This way, you can prove that it's the class vs. struct issue and not an 
 optimization issue.

Maybe you should try to come up with a more realistic example, and optimize both. Now you optimized the class version, but not the struct version. It's easy to claim classes as faster when you selectively optimize one and not the other. I intentionally optimized the other way around. If your "optimization" proves classes are faster, then mine proves structs are faster. And the code is still the same - there's no logical difference in how they work (except for garbage collecting much more in the class version, as you had in the struct version). To be sure, if both are optimized: // struct Point p; for (uint i = 0; i < n; ++i) { p.count += p.x; } // class Point p = new Point(); for (uint i = 0; i < n; ++i) { p.count += p.x; // delete p; } the times for the class version change to:
 class

operation took 0 m 0.25 s which is equivalent with the 0.218 s it took for the struct version. If you want to prove classes faster than structs, please don't go about it optimizing only the class version and not the struct one. Otherwise, you end up looking like a fool.

Mar 27 2006
prev sibling next sibling parent John Reimer <terminal.node gmail.com> writes:
Hong wrote:

 Imho, following are little suggestions for structs to make first class citizens
 of D:
 
 1. have some sort of reference semantic to replace pointers. Pointers are not
 necessary unless structs are used with "new"
 
 2. add constructor for struct, destructors are not necessary.
 
 I use structs because I need efficiency and stack allocated data structures,
 however, it is painful to see a C++ program turns into a C like program when
 ported to D.
 
 

I agree with both these points, especially #1. I wish D had some sort of reference attribute for other types similar to that designed into classes. It's confusing moving back and forth between the practically deprecated pointers and the reference types native to classes. Nor do pointers and references always intermix adequately in ADT's. -JJR
Mar 25 2006
prev sibling next sibling parent reply Alexander Panek <alexander.panek brainsware.org> writes:
Hong wrote:
 Structs are second class citizens of... or maybe refugees of D, they are
victims
 of discrimination.
 Following are the reasons:
 
 1. most D programmers don't use struct, unless they happen to be the poor guy
 writing the C interface.
 

encapsules data and provides an easy access to chunks of data related to one bunch of information / object.
 2. to use structs you have to use pointers, but the spec says pointers are only
 provided for C compatibility, structs are to be avoided as long as the world
 spins.
 

the documentation, sorry.
 3. pointers + overloaded operators make a mess.... this is a less offensive
 example: (*o) = (*t)[(*i) * (*j)] ... the pointers and operators are abusing
 each other.
 
 4. structs cannot have constructor, "static opCall" is best you can do.
 

function/template.
 5. structs are more efficient? Not when structs are passed around by value. To
 change a struct member: 1. make a copy 2. change the copy 3. Copy the copy back
 into the original location. Two damned copies for.... efficiency.
 

not use pointers. "Structs and unions are meant as simple aggregations of data, or as a way to paint a data structure over hardware or an external type. External types can be defined by the operating system API, or by a file format. Object oriented features are provided with the class data type." -- http://www.digitalmars.com//d/struct.html
 6. yes, you can use pointers to change a struct member without copying, if you
 can be bothered to define 2 accessor methods, one returns a copy another one
 returns a pointer.
 

 7. Most standard containers (DTL) do not allow a pointer to a contained struct
 to be retrieved, all changes, or just about anything has to be done via copies.
 
 8. structs have different behaviours between array and Vector, array [] returns
 a reference, Vector [] returns a copy, nice rookie trap.
 

know? ;)
 
 Imho, following are little suggestions for structs to make first class citizens
 of D:
 
 1. have some sort of reference semantic to replace pointers. Pointers are not
 necessary unless structs are used with "new"
 

also provides the ability to write low level code. And structs, as they *are*, are a very important part of this ability.
 2. add constructor for struct, destructors are not necessary.
 

 I use structs because I need efficiency and stack allocated data structures,
 however, it is painful to see a C++ program turns into a C like program when
 ported to D.
 

'ugly' with structs. Also, structs should not be abused. Structs are structs and classes are classes - they both have their specific purposes, one should not forget that. Regards, Alex PS: No offense intended.
Mar 26 2006
parent reply Hong <Hong_member pathlink.com> writes:
I hope you really know what you are talking about.

In article <e06353$7o9$1 digitaldaemon.com>, Alexander Panek says...
Hong wrote:
 Structs are second class citizens of... or maybe refugees of D, they are
victims
 of discrimination.
 Following are the reasons:
 
 1. most D programmers don't use struct, unless they happen to be the poor guy
 writing the C interface.
 

encapsules data and provides an easy access to chunks of data related to one bunch of information / object.
 2. to use structs you have to use pointers, but the spec says pointers are only
 provided for C compatibility, structs are to be avoided as long as the world
 spins.
 

the documentation, sorry.
 3. pointers + overloaded operators make a mess.... this is a less offensive
 example: (*o) = (*t)[(*i) * (*j)] ... the pointers and operators are abusing
 each other.
 
 4. structs cannot have constructor, "static opCall" is best you can do.
 

function/template.
 5. structs are more efficient? Not when structs are passed around by value. To
 change a struct member: 1. make a copy 2. change the copy 3. Copy the copy back
 into the original location. Two damned copies for.... efficiency.
 

not use pointers. "Structs and unions are meant as simple aggregations of data, or as a way to paint a data structure over hardware or an external type. External types can be defined by the operating system API, or by a file format. Object oriented features are provided with the class data type." -- http://www.digitalmars.com//d/struct.html
 6. yes, you can use pointers to change a struct member without copying, if you
 can be bothered to define 2 accessor methods, one returns a copy another one
 returns a pointer.
 

 7. Most standard containers (DTL) do not allow a pointer to a contained struct
 to be retrieved, all changes, or just about anything has to be done via copies.
 
 8. structs have different behaviours between array and Vector, array [] returns
 a reference, Vector [] returns a copy, nice rookie trap.
 

know? ;)
 
 Imho, following are little suggestions for structs to make first class citizens
 of D:
 
 1. have some sort of reference semantic to replace pointers. Pointers are not
 necessary unless structs are used with "new"
 

also provides the ability to write low level code. And structs, as they *are*, are a very important part of this ability.
 2. add constructor for struct, destructors are not necessary.
 

 I use structs because I need efficiency and stack allocated data structures,
 however, it is painful to see a C++ program turns into a C like program when
 ported to D.
 

'ugly' with structs. Also, structs should not be abused. Structs are structs and classes are classes - they both have their specific purposes, one should not forget that. Regards, Alex PS: No offense intended.

Mar 26 2006
parent Alexander Panek <alexander.panek brainsware.org> writes:
Hong wrote:
 I hope you really know what you are talking about.
 

Prove me wrong.
Mar 26 2006
prev sibling next sibling parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
Hong wrote:
 Structs are second class citizens of... or maybe refugees of D, they are
victims
 of discrimination.
 Following are the reasons:
 
 1. most D programmers don't use struct, unless they happen to be the poor guy
 writing the C interface.

Or they are writing or using a D interface to a C or other foreign API. Or they want to save the overhead of memory allocation by using a lightweight alternative for those situations where the power of classes isn't needed. Or they are interfacing a binary file format.
 2. to use structs you have to use pointers, but the spec says pointers are only
 provided for C compatibility, structs are to be avoided as long as the world
 spins.

No I don't. What are you talking about?
 3. pointers + overloaded operators make a mess.... this is a less offensive
 example: (*o) = (*t)[(*i) * (*j)] ... the pointers and operators are abusing
 each other.
 
 4. structs cannot have constructor, "static opCall" is best you can do.

What's wrong with that? Do you simply miss the word "new" in uses of static opCall?
 5. structs are more efficient? Not when structs are passed around by value. To
 change a struct member: 1. make a copy 2. change the copy 3. Copy the copy back
 into the original location. Two damned copies for.... efficiency.

If you want classes, you know where to find them.
 6. yes, you can use pointers to change a struct member without copying, if you
 can be bothered to define 2 accessor methods, one returns a copy another one
 returns a pointer.

Why would anyone want to do that? What's wrong with simply Qwert yuiop = asdfg; // to create a copy Qwert* zxcvb = &asdfg; // to create a pointer
 7. Most standard containers (DTL) do not allow a pointer to a contained struct
 to be retrieved, all changes, or just about anything has to be done via copies.

That's an issue with those containers, rather than with structs themselves.
 8. structs have different behaviours between array and Vector, array [] returns
 a reference, Vector [] returns a copy, nice rookie trap.
 
 Imho, following are little suggestions for structs to make first class citizens
 of D:
 
 1. have some sort of reference semantic to replace pointers. Pointers are not
 necessary unless structs are used with "new"
 
 2. add constructor for struct, destructors are not necessary.
 
 I use structs because I need efficiency and stack allocated data structures,
 however, it is painful to see a C++ program turns into a C like program when
 ported to D.

Maybe you could elaborate on how these reference semantics would work such that they'd be any different from classes. Stewart. -- -----BEGIN GEEK CODE BLOCK----- Version: 3.1 GCS/M d- s:- C++ a->--- UB P+ L E W++ N+++ o K- w++ O? M V? PS- PE- Y? PGP- t- 5? X? R b DI? D G e++>++++ h-- r-- !y ------END GEEK CODE BLOCK------ My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Mar 27 2006
parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Stewart Gordon wrote:
 Hong wrote:
 Structs are second class citizens of... or maybe refugees of D, they 
 are victims
 of discrimination.
 Following are the reasons:


[snip]
 4. structs cannot have constructor, "static opCall" is best you can do.

What's wrong with that? Do you simply miss the word "new" in uses of static opCall?

The problem with not having real constructors is that you can not guarantee that your struct gets initialized correctly.
 5. structs are more efficient? Not when structs are passed around by 
 value. To
 change a struct member: 1. make a copy 2. change the copy 3. Copy the 
 copy back
 into the original location. Two damned copies for.... efficiency.

If you want classes, you know where to find them.

Small structs (the cases were structs are most useful) can often more efficient to pass by value than by reference.
 6. yes, you can use pointers to change a struct member without 
 copying, if you
 can be bothered to define 2 accessor methods, one returns a copy 
 another one
 returns a pointer.

Why would anyone want to do that? What's wrong with simply Qwert yuiop = asdfg; // to create a copy Qwert* zxcvb = &asdfg; // to create a pointer

If I understand correctly, Hong is talking about struct members (i.e. a member variable of a struct type), that should be accessed through an accessor method. I.e: Array!(Position) points = ...; points[5].x = 7; Or: myInstance.pos.x = 5; (Where pos is accessed through a getter method)
 7. Most standard containers (DTL) do not allow a pointer to a 
 contained struct
 to be retrieved, all changes, or just about anything has to be done 
 via copies.

That's an issue with those containers, rather than with structs themselves.

I agree that this is not an issue with structs. But it is an issue with the language. Given: struct Position { int x,y; } There is no way to duplicate the behavior of: Position[] points = ...; points[5].y = 5; points[3].x += 2; With an user defined container type.
 Imho, following are little suggestions for structs to make first class 
 citizens
 of D:

 1. have some sort of reference semantic to replace pointers. Pointers 
 are not
 necessary unless structs are used with "new"

Maybe you could elaborate on how these reference semantics would work such that they'd be any different from classes.

You could consider: struct MyPointContainer { Position[] points; inout Position opIndex(uint ix) { return &points[ix]; } } Where inout denotes that the struct is returned by reference. /Oskar
Mar 27 2006
next sibling parent Dave <Dave_member pathlink.com> writes:
In article <e0948b$1ihl$1 digitaldaemon.com>, Oskar Linde says...
Stewart Gordon wrote:
 Hong wrote:
 Structs are second class citizens of... or maybe refugees of D, they 
 are victims
 of discrimination.
 Following are the reasons:


[snip]
 4. structs cannot have constructor, "static opCall" is best you can do.

What's wrong with that? Do you simply miss the word "new" in uses of static opCall?

The problem with not having real constructors is that you can not guarantee that your struct gets initialized correctly.
 5. structs are more efficient? Not when structs are passed around by 
 value. To
 change a struct member: 1. make a copy 2. change the copy 3. Copy the 
 copy back
 into the original location. Two damned copies for.... efficiency.

If you want classes, you know where to find them.

Small structs (the cases were structs are most useful) can often more efficient to pass by value than by reference.

You know, with all of the talk of cache locality and what-not w.r.t. passing small structs by value vs. reference, I've yet to see the "conventional wisdom" (that passing byref is faster) proven wrong, even in larger programs where there has to be things being constantly being moved in and out of cache, etc. I'm not saying that is always the case, but 'often' above is pretty strong there. I'd say at best "sometimes" is a better phrase. // Yes, yes I know this is may not reflect a typical 'real world' program // But still there is a 4x difference on my AMD64 and P4 ('modern // processor') boxen, with byref being the faster. import std.stdio, std.date; struct S { int i, j, k; } class C { int i, j, k; } void main() { const int y = 100_000_000; { S s; d_time st = getUTCtime(); for(int x = 0; x < y; x++) { fooS(s); } d_time en = getUTCtime(); writefln("struct byval: ",(en-st) / cast(double)TicksPerSecond); } { S s; d_time st = getUTCtime(); for(int x = 0; x < y; x++) { fooS2(&s); } d_time en = getUTCtime(); writefln("struct byref: ",(en-st) / cast(double)TicksPerSecond); } { C c = new C; d_time st = getUTCtime(); for(int x = 0; x < y; x++) { fooC(c); } d_time en = getUTCtime(); writefln("class: ",(en-st) / cast(double)TicksPerSecond); } } void fooS2(S* s) { s.i = s.j = s.k = 0; } void fooS(S s) { s.i = s.j = s.k = 0; } void fooC(C c) { c.i = c.j = c.k = 0; }
Mar 27 2006
prev sibling parent Stewart Gordon <smjg_1998 yahoo.com> writes:
Oskar Linde wrote:
 Stewart Gordon wrote:
 Hong wrote:
 Structs are second class citizens of... or maybe refugees of D, they 
 are victims
 of discrimination.
 Following are the reasons:


[snip]
 4. structs cannot have constructor, "static opCall" is best you can do.

What's wrong with that? Do you simply miss the word "new" in uses of static opCall?

The problem with not having real constructors is that you can not guarantee that your struct gets initialized correctly.

Is the definition of a "real" constructor simply such that one is required to use a constructor rather than a static initialiser or leaving default initialisers to it? <snip>
 6. yes, you can use pointers to change a struct member without 
 copying, if you can be bothered to define 2 accessor methods, one 
 returns a copy another one returns a pointer.

Why would anyone want to do that? What's wrong with simply Qwert yuiop = asdfg; // to create a copy Qwert* zxcvb = &asdfg; // to create a pointer

If I understand correctly, Hong is talking about struct members (i.e. a member variable of a struct type), that should be accessed through an accessor method. I.e: Array!(Position) points = ...; points[5].x = 7;

Note that the OP hasn't mentioned container methods by this point. It therefore appears that this was about properties of the struct itself. But looking down, it now appears that it was *meant* to be about containers. <snip>
 7. Most standard containers (DTL) do not allow a pointer to a 
 contained struct to be retrieved, all changes, or just about
 anything has to be done via copies.

That's an issue with those containers, rather than with structs themselves.

I agree that this is not an issue with structs. But it is an issue with the language. Given: struct Position { int x,y; } There is no way to duplicate the behavior of: Position[] points = ...; points[5].y = 5; points[3].x += 2; With an user defined container type.

You can if you define opIndex to return a pointer. But that would require you to use a * when it's the value you need. Or, as was suggested, define a separate accessor method that returns a pointer. But I see your point now.
 Imho, following are little suggestions for structs to make first 
 class citizens
 of D:

 1. have some sort of reference semantic to replace pointers. Pointers 
 are not
 necessary unless structs are used with "new"

Maybe you could elaborate on how these reference semantics would work such that they'd be any different from classes.

You could consider: struct MyPointContainer { Position[] points; inout Position opIndex(uint ix) { return &points[ix]; } } Where inout denotes that the struct is returned by reference.

Yes, that's a possibility. Though the & ought not to be there - the referencing/dereferencing would be implicit on both sides with inout returns, just as it is with out/inout parameters. And it isn't giving structs reference semantics, it's a way of enabling returns from functions to have reference semantics in the same way as out/inout function parameters have. It would work on any type, not just structs. I suppose that, when this is done container[42] = something; would be interpreted by looking first for an opIndexAssign and then for an opIndex with inout return. But how would overload resolution work between the two forms? Stewart. -- -----BEGIN GEEK CODE BLOCK----- Version: 3.1 GCS/M d- s:- C++ a->--- UB P+ L E W++ N+++ o K- w++ O? M V? PS- PE- Y? PGP- t- 5? X? R b DI? D G e++>++++ h-- r-- !y ------END GEEK CODE BLOCK------ My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Mar 28 2006
prev sibling parent reply Norbert Nemec <Norbert Nemec-online.de> writes:
I partly agree with you.

1) A constructor for structs would definitely be necessary: Currently, a
struct is always initialized to all-zero fields. The "static opCall" is
nice syntactic sugar for changing the contents of the struct afterwards,
but it does not allow a guaranteed initialization, since D does not
force you to call opCall.

2) Reference handling similar to C++ would be very desirable. Of course,
a pointer can always be used instead, but it makes very ugly code and is
probably one of the reasons why structs are used so rarely in D.

3) Yes, classes are inefficient when used for purposes that should be
done with a struct. I have seen implementations of 3D-vector arithmetics
in D where the vector was defined as class. This means: each vector has
to be allocated on the heap!!! The benchmark example that you give may
be artificial but it is not at all unrealistic to have a loop that
allocates and deallocates 100000 small data structures. It makes a
tremendous difference whether this is done on the heap or on the stack!

It is good that D has a clear distinction between classes and structs,
but structs should be viewed as an important concept and given all the
power possible. Saying "use a class instead" is hardly ever an
acceptable excuse for lacking power in structs.

Greetings,
Norbert
Mar 28 2006
parent Stewart Gordon <smjg_1998 yahoo.com> writes:
Norbert Nemec wrote:
 I partly agree with you.
 
 1) A constructor for structs would definitely be necessary: Currently, a
 struct is always initialized to all-zero fields. The "static opCall" is
 nice syntactic sugar for changing the contents of the struct afterwards,
 but it does not allow a guaranteed initialization, since D does not
 force you to call opCall.

Struct members can have default initialisers. The only way in which it "does not allow a guaranteed initialization" is that it's possible to override default initialisers with a static initialiser. But that's just like changing members one-by-one after it's been initialised. Stewart. -- -----BEGIN GEEK CODE BLOCK----- Version: 3.1 GCS/M d- s:- C++ a->--- UB P+ L E W++ N+++ o K- w++ O? M V? PS- PE- Y? PGP- t- 5? X? R b DI? D G e++>++++ h-- r-- !y ------END GEEK CODE BLOCK------ My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Mar 28 2006