c#中类的内存对齐?

Pau*_*ott 14 c# memory

(顺便说一句.这是指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 <= 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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢,保罗.

Han*_*ant 6

运行机器的齿轮看起来很有趣。当双精度数只能以两种方式对齐时,我在解释为​​什么有多个不同的值(我有 4 个)时遇到了一些问题。我认为与 CPU 缓存行的对齐也发挥了作用,尽管这只增加了 3 个可能的时序。

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

如果很重要,那么您可以使用 Marshal.AllocCoTaskMem() 分配非托管内存,并使用可以正确对齐的不安全指针。如果您为使用 SIMD 指令的代码分配内存,您也必须做同样的事情,它们需要 16 字节对齐。不过,请认为这是绝望之举。


小智 6

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

public static class AlignedNew
{
    public static T New<T>() where T : new()
    {
        LinkedList<T> candidates = new LinkedList<T>();
        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<Variable>();


            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();
        }

    }
}
Run Code Online (Sandbox Code Playgroud)