c#中类的内存对齐?

(顺便说一句。这是指32位操作系统)

一些更新:

  • 这绝对是一个对齐问题

  • 有时对齐(无论出于何种原因?)是如此糟糕,以至于对双倍的访问速度比其最快访问速度慢50倍。

  • 在64位计算机上运行代码减少了问题,但我认为它仍然在两个时间之间交替(我可以通过将双倍更改为32位机器上的浮点数来获得类似结果)

  • 在mono下运行代码没有问题 – 微软,你有没有机会从那些Novell家伙那里复制一些东西?


有没有办法在c#中记忆对齐类的分配?

以下演示(我认为!)没有正确对齐的双重错误。 它对存储在类中的double执行一些简单的数学运算,对每次运行进行计时,在变量上运行5次定时运行,然后再分配一个新变量并重新执行。

基本上结果看起来你要么具有快速,中速或慢速记忆位置(在我的古代处理器上,这些最终每次运行大约40,80或120ms)

我曾尝试过使用StructLayoutAttribute,但没有任何乐趣 – 也许其他事情正在发生?

class Sample { class Variable { public double Value; } static void Main() { const int COUNT = 10000000; while (true) { var x = new Variable(); for (int inner = 0; inner < 5; ++inner) { // move allocation here to allocate more often so more probably to get 50x slowdown problem var stopwatch = Stopwatch.StartNew(); var total = 0.0; for (int i = 1; i  1) throw new ApplicationException(total.ToString()); Console.Write("{0}, ", stopwatch.ElapsedMilliseconds); } Console.WriteLine(); } } } 

所以我看到很多关于interop结构对齐的网页,那么类的对齐呢?

(或者我的假设是错误的,上面还有另一个问题吗?)

谢谢,保罗。

有趣的看着运行机器的齿轮。 我有一个问题,解释为什么当一个double只能以两种方式对齐时,有多个不同的值(我得到4)。 我认为与CPU缓存行的对齐也起到了作用,尽管这只会增加3个可能的时序。

好吧,你无能为力,CLR只保证4字节值的对齐,以保证32位机器上的primefaces更新。 这不仅仅是托管代码的问题, C / C ++也存在这个问题 。 看起来芯片制造商需要解决这个问题。

如果它很关键,那么你可以用Marshal.AllocCoTaskMem()分配非托管内存,并使用一个不安全的指针,你可以恰到好处地对齐。 如果为使用SIMD指令的代码分配内存,则必须执行相同的操作,它们需要16字节对齐。 考虑一下绝望 – 移动。

为了在.NET中certificate堆上对象未对齐的概念,您可以运行以下代码,您将看到它现在总是快速运行。 请不要拍我,这只是一个PoC,但如果你真的关心性能,你可以考虑使用它;)

 public static class AlignedNew { public static T New() where T : new() { LinkedList candidates = new LinkedList(); IntPtr pointer = IntPtr.Zero; bool continue_ = true; int size = Marshal.SizeOf(typeof(T)) % 8; while( continue_ ) { if (size == 0) { object gap = new object(); } candidates.AddLast(new T()); GCHandle handle = GCHandle.Alloc(candidates.Last.Value, GCHandleType.Pinned); pointer = handle.AddrOfPinnedObject(); continue_ = (pointer.ToInt64() % 8) != 0 || (pointer.ToInt64() % 64) == 24; handle.Free(); if (!continue_) return candidates.Last.Value; } return default(T); } } class Program { [StructLayoutAttribute(LayoutKind.Sequential)] public class Variable { public double Value; } static void Main() { const int COUNT = 10000000; while (true) { var x = AlignedNew.New(); for (int inner = 0; inner < 5; ++inner) { var stopwatch = Stopwatch.StartNew(); var total = 0.0; for (int i = 1; i <= COUNT; ++i) { x.Value = i; total += x.Value; } if (Math.Abs(total - 50000005000000.0) > 1) throw new ApplicationException(total.ToString()); Console.Write("{0}, ", stopwatch.ElapsedMilliseconds); } Console.WriteLine(); } } } 

也许StructLayoutAttribute是您正在寻找的?

使用struct而不是class,使时间不变。 还考虑使用StructLayoutAttribute。 它有助于指定结构的精确内存布局。 对于CLASSES,我认为你没有任何保证他们如何在记忆中放置。

它将正确对齐,否则您将在x64上获得对齐exception。 我不知道你的代码片段显示了什么,但我不会说任何与它对齐的内容。

您无法控制.NET如何在内存中放置您的类。

正如其他人所说,StructLayoutAttribute可以用来强制结构的特定内存布局请注意,这个目的是为了C / C ++互操作,而不是试图微调.NET应用程序的性能。

如果您担心内存对齐问题,那么C#可能是错误的语言选择。


编辑 – 打破了WinDbg并在32位Vista和.NET 2.0上查看运行上述代码的堆。

注意:我没有得到上面显示的时间变化。

 0:003> !dumpheap -type Sample+Variable Address MT Size 01dc2fec 003f3c48 16 01dc54a4 003f3c48 16 01dc58b0 003f3c48 16 01dc5cbc 003f3c48 16 01dc60c8 003f3c48 16 01dc64d4 003f3c48 16 01dc68e0 003f3c48 16 01dc6cd8 003f3c48 16 01dc70e4 003f3c48 16 01dc74f0 003f3c48 16 01dc78e4 003f3c48 16 01dc7cf0 003f3c48 16 01dc80fc 003f3c48 16 01dc8508 003f3c48 16 01dc8914 003f3c48 16 01dc8d20 003f3c48 16 01dc912c 003f3c48 16 01dc9538 003f3c48 16 total 18 objects Statistics: MT Count TotalSize Class Name 003f3c48 18 288 TestConsoleApplication.Sample+Variable Total 18 objects 0:003> !do 01dc9538 Name: TestConsoleApplication.Sample+Variable MethodTable: 003f3c48 EEClass: 003f15d0 Size: 16(0x10) bytes (D:\testcode\TestConsoleApplication\bin\Debug\TestConsoleApplication.exe) Fields: MT Field Offset Type VT Attr Value Name 6f5746e4 4000001 4 System.Double 1 instance 1655149.000000 Value 

在我看来,除非我读错了,否则类的分配地址似乎是对齐的?