gpt4 book ai didi

c# - CLR 顺序结构布局 : aligning and size

转载 作者:太空狗 更新时间:2023-10-29 22:20:08 24 4
gpt4 key购买 nike

默认情况下,C# 中的所有 struct 都被视为 [StructLayout(LayoutKind.Sequential)] 标记的值类型。因此,让我们取一些 struct 并检查此 struct 的大小:

using System;
using System.Reflection;
using System.Linq;
using System.Runtime.InteropServices;

class Foo
{
struct E { }
struct S0 { byte a; }
struct S1 { byte a; byte b; }
struct S2 { byte a; byte b; byte c; }
struct S3 { byte a; int b; }
struct S4 { int a; byte b; }
struct S5 { byte a; byte b; int c; }
struct S6 { byte a; int b; byte c; }
struct S7 { int a; byte b; int c; }
struct S8 { byte a; short b; int c; }
struct S9 { short a; byte b; int c; }
struct S10 { long a; byte b; }
struct S11 { byte a; long b; }
struct S12 { byte a; byte b; short c; short d; long e; }
struct S13 { E a; E b; }
struct S14 { E a; E b; int c; }
struct S15 { byte a; byte b; byte c; byte d; byte e; }
struct S16 { S15 b; byte c; }
struct S17 { long a; S15 b; }
struct S18 { long a; S15 b; S15 c; }
struct S19 { long a; S15 b; S15 c; E d; short e; }
struct S20 { long a; S15 b; S15 c; short d; E e; }

static void Main()
{
Console.WriteLine("name: contents => size\n");
foreach (var type in typeof(Foo).GetNestedTypes(BindingFlags.NonPublic))
{
var fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
Console.WriteLine("{0}: {2} => {1}", type.Name, Marshal.SizeOf(type),
string.Join("+", fields.Select(_ => Marshal.SizeOf(_.FieldType))));
}
}
}

输出是(在 x86/x64 上相同):

name: contents => size

E: => 1
S0: 1 => 1
S1: 1+1 => 2
S2: 1+1+1 => 3
S3: 1+4 => 8
S4: 4+1 => 8
S5: 1+1+4 => 8
S6: 1+4+1 => 12
S7: 4+1+4 => 12
S8: 1+2+4 => 8
S9: 2+1+4 => 8
S10: 8+1 => 16
S11: 1+8 => 16
S12: 1+1+2+2+8 => 16
S13: 1+1 => 2
S14: 1+1+4 => 8
S15: 1+1+1+1+1 => 5
S16: 5+1 => 6
S17: 8+5 => 16
S18: 8+5+5 => 24
S19: 8+5+5+1+2 => 24
S20: 8+5+5+2+1 => 24

查看此结果我无法理解用于顺序结构的布局(字段对齐和总大小)规则集 CLR。有人可以向我解释这种行为吗?

最佳答案

所有字段都根据它们的类型对齐。 native 类型(intbyte 等)均按其大小对齐。例如,int 始终是 4 字节的倍数,而字节可以是任何位置。

如果较小的字段出现在 int 之前,将在必要时添加填充以确保 int 正确对齐到 4 字节。这就是为什么 S5 (1+1+4 = 8) 和 S8 (1+2+4 = 8) 会有填充并且最终大小相同:

[1][1][ ][ ][4] // S5
[1][ ][ 2 ][4] // S8

此外,结构体本身继承了其最对齐字段的对齐方式(即对于 S5S8int 是最对齐的-aligned 字段,因此它们的对齐方式均为 4)。对齐是这样继承的,这样当你有一个结构数组时,所有结构中的所有字段都将正确对齐。所以,4+2 = 8。

[4][2][ ][ ] // starts at 0
[4][2][ ][ ] // starts at 8
[4][2][ ][ ] // starts at 16

注意 4 总是按 4 对齐。如果不继承最对齐的字段,数组中的每个其他元素的 int 都会按 6 字节而不是 4 对齐:

[4][2] // starts at 0
[4][2] // starts at 6 -- the [4] is not properly aligned!
[4][2] // starts at 12

这将非常糟糕,因为并非所有架构都允许从未对齐的内存地址读取数据,即使是那些确实有(可能非常大,如果在缓存行或页面边界上)执行此操作的性能损失。

除了基本性能之外,对齐也会影响并发性。 C# 内存模型保证最大 4 字节宽的 native 类型的读/写是原子的,而 .NET 具有原子特性,如 Interlocked 类。像这样的原子操作归结为 CPU 指令,这些指令本身需要对齐的内存访问才能工作。

正确对齐非常重要!

您经常会看到聪明的本地编码人员在布局结构时将所有这些牢记在心,将所有字段从最大到最小排序,以努力将填充和结构大小保持在最小值。

关于c# - CLR 顺序结构布局 : aligning and size,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8263667/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com