x64 位 Azure 机器上 DateTimeOffset 的平均大小是多少?
What is the average size of a DateTimeOffset on a x64 bit Azure machine?
我一直在尝试获取用于此目的的 DateTimeOffset
struct in my code so that I can compute the size of a parent object. The problem is that neither the sizeof
operator nor the Marshal.SizeOf
函数的大小。
sizeof 将无法工作,因为我必须使用不安全标志进行编译,而此功能不足以作为这样做的理由。 Marshal.SizeOf 抛出异常:
Type 'System.DateTimeOffset' cannot be marshaled as an unmanaged
structure; no meaningful size or offset can be computed.
我已经放弃尝试像这样计算结构的大小,因为 attack/blog post 的每一行都会出现一个或另一个错误。
谁能告诉我 64 位 Azure Web 服务器上 DateTimeOffset 的平均大小是多少?
占用10个字节
查看 source,它有一个 Int16
和一个 DateTime
; DateTime
有一个 UInt64
.
我不知道为什么Marshal.SizeOf
不能测量它。
接受的答案不正确,它忽略了对齐。结构的大小不仅仅是其成员的总和。可能需要在字段之间以及结构末尾添加额外的 space 以帮助处理器有效读取字段并实现 .NET 内存模型提供的原子性保证。
DateTimeOffset 更加复杂,编写 DateTimeOffset 结构的 Microsoft 程序员犯了一个大错误,开始使用 copy/pasting DateTime 结构进行编码。它有一个历史错误,对 DateTimeOffset 来说很重要,因为它有两个字段而不是一个。它使用 LayoutKind.Auto 而不是顺序。在 Reference Source
中很容易看到
这为 CLR 提供了在任何模式下将字段安排为最佳的余地 运行。在 32 位模式下,它将 不 对齐一个Int64 到 8 个字节,和通常一样,但是到 4 个字节。这减少了字段之间的填充,大小为 12 字节。
同样,在 64 位模式下,它喜欢将字段对齐到 8。这会在字段之间产生更多的填充。
查看此信息的唯一好方法是使用调试器。 运行 这段代码:
static void Main(string[] args) {
var arr = new DateTimeOffset[] {
new DateTimeOffset(0x123456789abcdef0, TimeSpan.FromMinutes(60)),
new DateTimeOffset(0x123456789abcdef0, TimeSpan.FromMinutes(60)),
};
System.Diagnostics.Debugger.Break();
}
当断点命中时,使用 Debug > Windows > Memory > Memory1 并在地址框中键入 &arr[0]
查看数组内容。您会看到类似于:
0x00000115DC4FBA30 3c 00 00 00 00 00 00 00 f0 76 f8 38 70 56 34 12 <.......ðvø8pV4.
0x00000115DC4FBA40 3c 00 00 00 00 00 00 00 f0 76 f8 38 70 56 34 12 <.......ðvø8pV4.
0x00000115DC4FBA50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
您可以很容易地看到偏移字段(60 = 0x003c) 和日期时间字段。我调整了 window 的大小,使这 2 个元素变得明显,它在您的机器上看起来不会那么干净 :) 但只需计算字节直到它们重复,DateTimeOffset 需要 16 个字节 在 64 位模式下。
32 位和 64 位模式之间的大小不同这一事实通常会让您很担心。这个错误永远不需要修复,DateTimeOffset 没有合理的互操作故事,它永远不会匹配等效的非托管类型。它是 WinRT(又名 UWP)中指定的互操作类型,但 CLR 中内置的语言投影隐藏了这个问题。
这并不完全可靠(并且假设数组开销是恒定的,这在理智的条件下可能是正确的),但下面是根据经验回答这个问题的简单方法。如果出于某种原因您希望在缺少调试器的环境中进行测试,这种方法可能适用于健全性检查:
void Main()
{
PrintSize<DateTimeOffset>();
}
public static void PrintSize<T>()
{
GC.Collect();
long gc1 = GC.GetTotalMemory(true);
const int sz = 100000;
T[] foo = new T[sz];
long gc2 = GC.GetTotalMemory(true);
GC.KeepAlive(foo);
Console.WriteLine($"{(gc2 - gc1) / (double)sz} bytes per {typeof(T)}");
}
当我测试为 32 位时,它打印了 12 (+/- 0.001)。
当我测试为 64 位时,它打印了 16 (+/- 0.001)。
请注意,此类测试最好作为独立应用程序进行;更复杂的应用程序更有可能在 RAM 使用方面发生不可预测的变化。
我一直在尝试获取用于此目的的 DateTimeOffset
struct in my code so that I can compute the size of a parent object. The problem is that neither the sizeof
operator nor the Marshal.SizeOf
函数的大小。
sizeof 将无法工作,因为我必须使用不安全标志进行编译,而此功能不足以作为这样做的理由。 Marshal.SizeOf 抛出异常:
Type 'System.DateTimeOffset' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.
我已经放弃尝试像这样计算结构的大小,因为 attack/blog post 的每一行都会出现一个或另一个错误。
谁能告诉我 64 位 Azure Web 服务器上 DateTimeOffset 的平均大小是多少?
占用10个字节
查看 source,它有一个 Int16
和一个 DateTime
; DateTime
有一个 UInt64
.
我不知道为什么Marshal.SizeOf
不能测量它。
接受的答案不正确,它忽略了对齐。结构的大小不仅仅是其成员的总和。可能需要在字段之间以及结构末尾添加额外的 space 以帮助处理器有效读取字段并实现 .NET 内存模型提供的原子性保证。
DateTimeOffset 更加复杂,编写 DateTimeOffset 结构的 Microsoft 程序员犯了一个大错误,开始使用 copy/pasting DateTime 结构进行编码。它有一个历史错误,对 DateTimeOffset 来说很重要,因为它有两个字段而不是一个。它使用 LayoutKind.Auto 而不是顺序。在 Reference Source
中很容易看到这为 CLR 提供了在任何模式下将字段安排为最佳的余地 运行。在 32 位模式下,它将 不 对齐一个Int64 到 8 个字节,和通常一样,但是到 4 个字节。这减少了字段之间的填充,大小为 12 字节。
同样,在 64 位模式下,它喜欢将字段对齐到 8。这会在字段之间产生更多的填充。
查看此信息的唯一好方法是使用调试器。 运行 这段代码:
static void Main(string[] args) {
var arr = new DateTimeOffset[] {
new DateTimeOffset(0x123456789abcdef0, TimeSpan.FromMinutes(60)),
new DateTimeOffset(0x123456789abcdef0, TimeSpan.FromMinutes(60)),
};
System.Diagnostics.Debugger.Break();
}
当断点命中时,使用 Debug > Windows > Memory > Memory1 并在地址框中键入 &arr[0]
查看数组内容。您会看到类似于:
0x00000115DC4FBA30 3c 00 00 00 00 00 00 00 f0 76 f8 38 70 56 34 12 <.......ðvø8pV4.
0x00000115DC4FBA40 3c 00 00 00 00 00 00 00 f0 76 f8 38 70 56 34 12 <.......ðvø8pV4.
0x00000115DC4FBA50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
您可以很容易地看到偏移字段(60 = 0x003c) 和日期时间字段。我调整了 window 的大小,使这 2 个元素变得明显,它在您的机器上看起来不会那么干净 :) 但只需计算字节直到它们重复,DateTimeOffset 需要 16 个字节 在 64 位模式下。
32 位和 64 位模式之间的大小不同这一事实通常会让您很担心。这个错误永远不需要修复,DateTimeOffset 没有合理的互操作故事,它永远不会匹配等效的非托管类型。它是 WinRT(又名 UWP)中指定的互操作类型,但 CLR 中内置的语言投影隐藏了这个问题。
这并不完全可靠(并且假设数组开销是恒定的,这在理智的条件下可能是正确的),但下面是根据经验回答这个问题的简单方法。如果出于某种原因您希望在缺少调试器的环境中进行测试,这种方法可能适用于健全性检查:
void Main()
{
PrintSize<DateTimeOffset>();
}
public static void PrintSize<T>()
{
GC.Collect();
long gc1 = GC.GetTotalMemory(true);
const int sz = 100000;
T[] foo = new T[sz];
long gc2 = GC.GetTotalMemory(true);
GC.KeepAlive(foo);
Console.WriteLine($"{(gc2 - gc1) / (double)sz} bytes per {typeof(T)}");
}
当我测试为 32 位时,它打印了 12 (+/- 0.001)。
当我测试为 64 位时,它打印了 16 (+/- 0.001)。
请注意,此类测试最好作为独立应用程序进行;更复杂的应用程序更有可能在 RAM 使用方面发生不可预测的变化。