【游戏编程扯淡精粹】C++对象内存布局
【游戏编程扯淡精粹】C++对象内存布局
引言
CPU给GPU传递buffer,约定好buffer item的struct协议,基本上要求是packed struct
那么packed前后有什么区别呢?知道struct字段对齐,那具体又是怎么对齐的呢?
此外,虚表指针vptr是放在哪里的呢,怎么对齐呢?
稍微想一想,编译器肯定知道这些信息,搜一下就有了
工具
/d1reportAllClassLayout
/d1reportAllClassLayout – 转储对象内存布局 | Ofek 的 Visual C++ 东西
VS可视化插件
Struct Layout - Visual Studio Marketplace
g++ fdump-class-hierarchy // 略
问题清单
- packed struct前后对比?
- vptr放在哪里?占几个字节?
结论
本文以MSVC Win32为例,不同C++编译器仍然有差异,跨平台时要小心避坑
- vfptr,虚表指针,占4字节,放在头部,并且随着基类继承下去
- 按N字节对齐是struct的属性,默认行为和编译器有关,强制指定N可能会影响内存布局和读写性能
alignof(T)
返回N,#pragma pack(N)
指定N
- 字段不对齐会严重影响字段读写速度
- x86默认4字节对齐,并用padding填充来确保每个字段对齐,会占用更多内存
- 使用
#pragma pack(1)
来紧密布局,未对齐的字段会有两倍的读写开销
编译器默认布局
强制紧密布局
后面三个属性被拆到两个字,导致读取需要2个时钟周期,也就是两倍的开销
vfptr
class A
{
int m_a, m_b;
virtual void cookie() {}
virtual void monster() {}
};
class B : public A
{
double m_c;
virtual void cookie() {};
};
/d1reportAllClassLayout
关注点:
- size,总共占据多少字节
- vfptr,虚表指针,占4字节,MSVC放在头部,并且随着基类继承下去
- 字段布局,如何对齐
- 虚表结构,当前类的元信息,然后是虚函数指针列表
class A size(12):
+---
0 | {vfptr}
4 | m_a
8 | m_b
+---
A::$vftable@:
| &A_meta
| 0
0 | &A::cookie
1 | &A::monster
A::cookie this adjustor: 0
A::monster this adjustor: 0
class B size(24):
+---
0 | +--- (base class A)
0 | | {vfptr}
4 | | m_a
8 | | m_b
| +---
| <alignment member> (size=4)
16 | m_c
+---
B::$vftable@:
| &B_meta
| 0
0 | &B::cookie
1 | &A::monster
B::cookie this adjustor: 0
Struct Layout
可视化后更加清楚了
- vfptr,虚表指针,占4字节,MSVC放在头部,并且随着基类继承下去
- 对齐,为了B.double对齐,A末尾加了4字节的padding
packed struct
#ifdef __GNUC__
#define PACKED_STRUCT __attribute__((packed,aligned(1)))
#else
#define PACKED_STRUCT
#endif
struct PACKED_STRUCT gpuvec4 {
float x, y, z, w;
gpuvec4() = default;
explicit gpuvec4(float v) : x(v), y(v), z(v), w(v) {}
gpuvec4(float a, float b, float c, float d) : x(a), y(b), z(c), w(d) {}
};
struct PACKED_STRUCT gpumat4 {
float data_[16];
gpumat4() = default;
};
enum MaterialFlags {
sMaterialFlags_CastShadow = 0x1,
sMaterialFlags_ReceiveShadow = 0x2,
sMaterialFlags_Transparent = 0x4,
};
constexpr const uint64_t INVALID_TEXTURE = 0xFFFFFFFF;
struct PACKED_STRUCT MaterialDescription final {
gpuvec4 emissiveColor_ = { 0.0f, 0.0f, 0.0f, 0.0f };
gpuvec4 albedoColor_ = { 1.0f, 1.0f, 1.0f, 1.0f };
gpuvec4 roughness_ = { 1.0f, 1.0f, 0.0f, 0.0f };
float transparencyFactor_ = 1.0f;
float alphaTest_ = 0.0f;
float metallicFactor_ = 0.0f;
uint32_t flags_ = sMaterialFlags_CastShadow | sMaterialFlags_ReceiveShadow;
#pragma region maps
uint64_t ambientOcclusionMap_ = INVALID_TEXTURE;
uint64_t emissiveMap_ = INVALID_TEXTURE;
uint64_t albedoMap_ = INVALID_TEXTURE;
uint64_t metallicRoughnessMap_ = INVALID_TEXTURE;
uint64_t normalMap_ = INVALID_TEXTURE;
uint64_t opacityMap_ = INVALID_TEXTURE;
#pragma endregion maps
};
默认对齐
四字节对齐
—
class Base
{
public:
int a = 1;
virtual void print() {};
};
class Derived : public Base {
public:
int b = 3;
virtual void print() {};
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!