C# 结构布局优化
字节优化能给您带来多大的性能提升(使它们成为8、32、64等的倍数) 以下是一个示例结构:C# 结构布局优化,c#,opengl,optimization,structure,C#,Opengl,Optimization,Structure,字节优化能给您带来多大的性能提升(使它们成为8、32、64等的倍数) 以下是一个示例结构: [StructLayout(LayoutKind.Explicit)] public struct RenderItem { [FieldOffset(0)] byte[] mCoordinates = new byte[3]; //(x,y,z) [FieldOffset(3)] short mUnitType; } 所以我的问题是,这样做有多重要: [Stru
[StructLayout(LayoutKind.Explicit)]
public struct RenderItem
{
[FieldOffset(0)] byte[] mCoordinates = new byte[3]; //(x,y,z)
[FieldOffset(3)] short mUnitType;
}
所以我的问题是,这样做有多重要:
[StructLayout(LayoutKind.Explicit)]
public struct RenderItem
{
[FieldOffset(0)] byte[] mCoordinates = new byte[3]; //(x,y,z)
[FieldOffset(4)] short mUnitType;
[FieldOffset(6)] byte[] mPadding = new byte[2]; //make total to 8 bytes
}
我确信它是“随大小扩展”的东西之一,因此我特别好奇的是,在创建VertexBuffer对象时,这种结构会被使用大约150000次:
//int objType[,,] 3 dimensional int with object type information stored in it
int i = 0;
RenderItem vboItems[16 * 16 * 16 * 36] //x - 16, y - 16, z - 16, 36 verticies per object
For(int x = 0; x < 16; x++)
{
For(int y = 0; y < 16; y++)
{
For(int z = 0; z < 16; z++)
{
vboItems[i++] = (x,y,z,objType[x,y,z]);
}
}
}
//Put vboItems into a VBO
//int对象类型[,]三维int,其中存储对象类型信息
int i=0;
RenderItem vboItems[16*16*16*36]//x-16,y-16,z-16,每个对象36个垂直方向
对于(int x=0;x<16;x++)
{
对于(int y=0;y<16;y++)
{
对于(intz=0;z<16;z++)
{
vboItems[i++]=(x,y,z,objType[x,y,z]);
}
}
}
//将VBOITEM放入VBO中
它不像你想象的那样工作
[StructLayout(LayoutKind.Explicit)]
public struct RenderItem
{
[FieldOffset(0)] byte[] mCoordinates = new byte[3]; //(x,y,z)
[FieldOffset(3)] short mUnitType;
}
数组是引用类型。mCoordinates
的存储要求是IntPtr.Size(即x86上的4字节和x64上的8字节)。3字节元素存储在堆上
我不知道如果你像那样引用,会(或不会)发生什么坏事
如果您需要具有这种精确布局的结构,则需要创建另一个值类型
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Coordinate
{
byte x;
byte y;
byte z;
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct RenderItem
{
Coordinate coord;
short mUnitType;
}
这并没有回答您关于对齐的问题,但是Matthew提供的链接回答了您的问题。我假设您应用了[MarshalAs]属性使数组成为ByValArray,这是在这样的结构上唯一有意义的事情。实际上,通过将struct变大2字节,可以使它变慢。这将降低处理器缓存的使用效率,在阵列中使用时,可以容纳的结构更少,这对性能非常重要 默认StructLayoutAttribute.Pack值8已优化,以提供结构的最佳布局。事实上,它对您的结构没有任何影响,无论包值如何,成员都已以最佳方式对齐。任何现代处理器获得最佳性能的规则:
- 成员应与可被成员大小整除的地址对齐。这可能会在成员之间添加填充字节。此规则防止处理器必须多路复用内存读取的字节值,或执行两次读取并将字节粘合在一起。结构上没有问题,唯一需要对齐的成员是mUnitType,它必须在2处对齐,并且已经在4处对齐。另外请注意,您不必使用[FieldOffset],默认布局已经很好了
- 在数组中使用结构时,成员应正确对齐。这可能会将打包添加到结构的末尾,以使数组中的下一个元素正确对齐。同样,这不是结构上的问题,它有6个字节长,因此数组中的下一个元素将对齐其mUnitType,因为它只需要2个字节。如果您实际上声明的数组没有[Marshallas],那么抖动将自动添加2个字节的填充,而无需您的帮助来确保数组指针正确对齐
- 成员不应跨越cpu缓存线。在我所知道的任何现代处理器上都是64字节。对性能非常不利,cpu必须读取两条缓存线的数据,并始终将字节粘在一起,性能命中大约慢x3。当结构包含大小为8或更大的成员时,在32位计算机上可能会发生这种情况。所以是一个长的、双精度的或十进制的。不仅成员的对齐很重要,结构在内存中的分配位置也很重要。这在x86版本的.NET上有点问题,它只能保证从堆栈或GC堆分配的数据的起始地址与4的倍数对齐。这对x64来说不是问题。对于您的结构来说,这不是一个问题,它只包含永远不会跨越cpu缓存线的小成员
另一个考虑因素也适用,与对齐无关。短路不是32位或64位处理器的最佳数据类型。如果您做的只是简单的加载和存储,那么将其从16位转换为32位需要额外的开销。背后的背景故事。现在,您需要在CPU缓存使用率提高和运行效率降低之间取得平衡,这是只有使用探查器才能可靠完成的。请查看。另外,如果您没有指定
FieldOffset
,编译器会自动为您对齐结构。它会自动对齐结构,但它能保证它们的顺序吗?我在好几个地方看到它不能保证(因为它进入OpenGL的顺序很重要),这是一个巨大的性能差异。但是,自从C#2以来,我们可以定义一个“不安全”结构,并在数组字段上使用“fixed”关键字,长度在编译时定义。这会产生一个包含数组元素/数据的结构,而不是数组对象的引用。我知道欢呼是不受欢迎的,但答案很好。