Struct vs Class: In memory

The Partridge Family were neither partridges nor a family. Discuss.
Post Reply
albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Struct vs Class: In memory

Post by albinopapa » August 16th, 2016, 12:00 am

I have a class with an XMFLOAT4, two XMFLOAT3 and two floats

Code: Select all

class MyClass
{
public:
    MyClass() = default;

private:
    // Trying to use this in a shader, that's why the alignments is as such
    XMFLOAT4 color;
    XMFLOAT3 position;
    float innerangle;
    XMFLOAT3 direction;
    float outerangle;
};
and a struct with the same members and same layout

Code: Select all

struct MyStruct
{
    XMFLOAT4 color;
    XMFLOAT3 position;
    float innerangle;
    XMFLOAT3 direction;
    float outerangle;
};
Now, when I pass the class to the struct, everything is shifted. The position has the values for color, the direction has the values for position. Also, when I create my constant buffer with the class, sizeof returns 64 bytes. On the flip side, if I use the struct, all values line up correctly and sizeof returns 48, the actual size of the struct.

So to those that might know, does VS or C++ treat classes any differently than structs where allocating memory is concerned? Would it matter that MyClass is a child of two other Interface classes ( ie. No data members, only virtual methods ). I thought the C++ standard was that sizeof would only return size of the data members. If it is, then the class is being 32 byte aligned by default, because 48 is a multiple of 16, so there shouldn't be a need for any padding. The versions of xmfloat I'm using aren't the aligned versions.

I'll keep working at it, just wanted to see if anyone had any ideas as to what is causing me to have to copy all data from class to struct before updating constant buffer.


EDIT: Ok, so apparently it does matter about inheritance, even if the parent(s) are just interfaces. Using just the samples above, making MyClass inherit from just one parent causes the sizeof to return 8 more bytes than without totaling 56, so with the two parents I mentioned, if each parent uses an extra 8 bytes, then that would explain the 64 bytes sizeof is returning on my child class.

This kind of makes me want to start using classes for interfaces and just have the struct be for the data, that way I don't have to copy to the struct, then copy to the GPU.

Code: Select all

class IMyInterface
{
	// All virtual methods setters, getters = 0; for pure virtual
};

class MyBase :public IMyInterface // haven't figure when to use other types of inheritance
{
	struct MyStruct
	{
		// Common data for this base type
	};
public:
	MyBase() = default;
	// Implement all virtual getter/setter methods
	// For shaders stuff, return just the struct object
protected:
	MyStruct m_myStruct;
};

class MyClass :public MyBase
{
public:
	MyClass() = default;
	// Implementation specific functions
};
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

User avatar
chili
Site Admin
Posts: 3948
Joined: December 31st, 2011, 4:53 pm
Location: Japan
Contact:

Re: Struct vs Class: In memory

Post by chili » August 16th, 2016, 1:48 am

albinopapa wrote:If it is, then the class is being 32 byte aligned by default, because 48 is a multiple of 16, so there shouldn't be a need for any padding. The versions of xmfloat I'm using aren't the aligned versions.
Alignment in a class is based on the largest data member.
albinopapa wrote:EDIT: Ok, so apparently it does matter about inheritance, even if the parent(s) are just interfaces. Using just the samples above, making MyClass inherit from just one parent causes the sizeof to return 8 more bytes than without totaling 56, so with the two parents I mentioned, if each parent uses an extra 8 bytes, then that would explain the 64 bytes sizeof is returning on my child class.
You're gonna get a pointer to a virtual function table for each interface you inherit from.
albinopapa wrote:This kind of makes me want to start using classes for interfaces and just have the struct be for the data, that way I don't have to copy to the struct, then copy to the GPU.
I too have considered something similar to this, albeit for SIMD purposes.
Chili

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Struct vs Class: In memory

Post by albinopapa » August 16th, 2016, 3:18 am

chili wrote: Alignment in a class is based on the largest data member.


You're gonna get a pointer to a virtual function table for each interface you inherit from.
I knew about the largest member alignment, I usually try to remember to put the largest member at the top of the list. Use to think it mattered, but I ran some tests that made it see class/struct organization doesn't seem affect size as some of the information on the net has suggested.

I thought the standard was that only data members counted toward size, didn't realize the v-table counted as a member.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

User avatar
LuisR14
Posts: 1248
Joined: May 23rd, 2013, 3:52 pm
Location: USA
Contact:

Re: Struct vs Class: In memory

Post by LuisR14 » August 16th, 2016, 8:30 am

albinopapa wrote: I thought the standard was that only data members counted toward size, didn't realize the v-table counted as a member.
considering the way you implement interfaces in C you'd see that it makes sense
always available, always on, about ~10 years c/c++, java[script], win32/directx api, [x]html/css/php/some asp/sql experience. (all self taught)
Knows English, Spanish and Japanese.
[url=irc://irc.freenode.net/#pchili]irc://irc.freenode.net/#pchili[/url] [url=irc://luisr14.no-ip.org/#pchili]alt[/url] -- join up if ever want real-time help or to just chat :mrgreen: --

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Struct vs Class: In memory

Post by albinopapa » August 17th, 2016, 1:37 am

LuisR14 wrote:
albinopapa wrote: I thought the standard was that only data members counted toward size, didn't realize the v-table counted as a member.
considering the way you implement interfaces in C you'd see that it makes sense
What do you mean?
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

User avatar
cyboryxmen
Posts: 190
Joined: November 14th, 2014, 2:03 am

Re: Struct vs Class: In memory

Post by cyboryxmen » August 18th, 2016, 4:14 pm

The size of a type with inheritance thing was something I was aware of when I found a bug in my shader code. The fact is that the v-table pointer is actually part of the type itself and is the hidden member in all types that inherit from polymorphic types and you'll have one for each polymorphic type you inherited. V-tables are created at the beginning of the program and pointers to them are given to their objects at the beginning of the object's lifespan. The v-table pointers will stay consistent and will not get copied over to other objects with copy operations and copy construction(though dangerous hacks could be done by casting them to arrays of chars and doing the copying then). When C++ passes a polymorphic object with multiple v-table pointers to a pointer/reference to a polymorphic type, it passes the appropriate v-table pointer(s) to the pointers/references depending on their type.

The program uses the v-table pointer for virtual dispatch by incrementing the pointer by a known fixed amount to get the memory location of the function it wants like how switch statements are implemented(virtual dispatch and switch statements are pretty much the same in concept and implementation). When doing a virtual call, the program can simply fetch the v-table pointer from the pointer/reference and increment it by 4 to get function#4 but that may not be the case if the v-table is combined with another one. Suddenly, doing this won't get you function#4 of the interface you expected and you instead get function#4 of some other interface. This is why types that inherit from multiple polymorphic types have separate v-table pointers for each of those types. The type itself may only have one v-table but the v-table pointers will point to the individual v-table components pertaining to them.

It's because of this reason is why, when wanting to save data in memory in a specific way, you should always just use POD structs or just structs with no virtual functions. These invisible members can be placed anywhere in your type depending on how your compiler implements polymorphism so it's better to just not use them at all.

Do note that v-tables are not the only way to do dynamic dispatch but are just the simplest. You should look into how Rust implements polymorphism if you want to learn more about this.
Zekilk

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Struct vs Class: In memory

Post by albinopapa » August 18th, 2016, 6:29 pm

cyboryxmen wrote:The program uses the v-table pointer for virtual dispatch by incrementing the pointer by a known fixed amount to get the memory location of the function it wants like how switch statements are implemented(virtual dispatch and switch statements are pretty much the same in concept and implementation)
Do you have any sources to back this statement? To me they are contradictory. The first part you say that it's an offset from base address, which means it can find the address of a function or member, just using some basic arithmetic. The switch statement would be more closely related to an if/else if, where it has to compare this address to the next address in the list.
cyboryxmen wrote:Do note that v-tables are not the only way to do dynamic dispatch but are just the simplest. You should look into how Rust implements polymorphism if you want to learn more about this.
If you are referring to Static Dispatch, I believe C++ templates are pretty close to that.

Thanks for the info, it's something I wasn't aware of. The regular member functions have their own address, but isn't considered in the size of the type, so I kind of figured same would go for virtual methods, and the v-table was just in some global memory pool for your applications...like a look up table. That would make the switch statement above seem more relevant.

Did't think about this, but on the subject of the switch statement, are you talking about how the CPU knows where to jump for each case in the switch? Figure the compiler would do that statically, so the CPU wouldn't have to do it during runtime.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

User avatar
chili
Site Admin
Posts: 3948
Joined: December 31st, 2011, 4:53 pm
Location: Japan
Contact:

Re: Struct vs Class: In memory

Post by chili » August 19th, 2016, 2:14 am

albinopapa wrote:
cyboryxmen wrote:The program uses the v-table pointer for virtual dispatch by incrementing the pointer by a known fixed amount to get the memory location of the function it wants like how switch statements are implemented(virtual dispatch and switch statements are pretty much the same in concept and implementation)
Do you have any sources to back this statement? To me they are contradictory. The first part you say that it's an offset from base address, which means it can find the address of a function or member, just using some basic arithmetic. The switch statement would be more closely related to an if/else if, where it has to compare this address to the next address in the list.
The switch statement can translate to a jump table if certain criteria are met. It is of course completely compiler dependent, and the same can be done with an ifelse chain, just depends on the compiler. In the old days when compilers where dumb as fuck, the switch was the way to tell the compiler "make a fucken jump table yo". It's still your best bet if that's what you're aiming for, just gotta know how to convince the compiler to do it, and inspect the disassembly afterwards to make sure.
Chili

Post Reply