March 27, 2006
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
March 27, 2006
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
100000000
operation took 0 m 0.25 s

>struct
100000000
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.
>>
> 
> 
> 

March 27, 2006
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
>100000000
>operation took 0 m 0.25 s
>
> >struct
>100000000
>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.
>>>
>> 
>> 
>> 
>


March 27, 2006
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
>100000000
>operation took 0 m 0.25 s
>
> >struct
>100000000
>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.
>>>
>> 
>> 
>> 
>


March 27, 2006
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
March 27, 2006
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
March 27, 2006
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.
March 27, 2006
[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
> 100000000
> operation took 0 m 0.25 s
>
> >struct
> 100000000
> operation took 0 m 15.265 s

on my machine:
>class
100000000
operation took 0 m 43.594 s

>struct
100000000
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
100000000
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.


March 27, 2006
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.
>>   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.

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
March 27, 2006
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