C# 结构布局优化

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

字节优化能给您带来多大的性能提升(使它们成为8、32、64等的倍数)

以下是一个示例结构:

[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缓存线的小成员

因此,根据这些规则,即使没有LayoutKind.Explicit,结构也已经是最优的了


另一个考虑因素也适用,与对齐无关。短路不是32位或64位处理器的最佳数据类型。如果您做的只是简单的加载和存储,那么将其从16位转换为32位需要额外的开销。背后的背景故事。现在,您需要在CPU缓存使用率提高和运行效率降低之间取得平衡,这是只有使用探查器才能可靠完成的。

请查看。另外,如果您没有指定
FieldOffset
,编译器会自动为您对齐结构。它会自动对齐结构,但它能保证它们的顺序吗?我在好几个地方看到它不能保证(因为它进入OpenGL的顺序很重要),这是一个巨大的性能差异。但是,自从C#2以来,我们可以定义一个“不安全”结构,并在数组字段上使用“fixed”关键字,长度在编译时定义。这会产生一个包含数组元素/数据的结构,而不是数组对象的引用。我知道欢呼是不受欢迎的,但答案很好。