【问题标题】:C# Struct Layouts and unexpected bench marking result?C# 结构布局和意外的基准测试结果?
【发布时间】:2012-03-16 03:18:45
【问题描述】:

我并不是真正对应用程序的性能进行微观管理,但我对以下场景很感兴趣。

对于 Structs,默认情况下,C# 编译器会生成布局 LayoutType。顺序的。这意味着字段应保持程序员定义的顺序。我相信这是为了支持与非托管代码的互操作性。然而,大多数用户定义的结构与互操作性无关。我读过,为了获得更好的性能,我们可以显式指定 LayoutKind.Auto,并让 CLR 决定可能的最佳布局。为了对此进行测试,我想对这两种布局进行快速基准测试。但是我的结果表明默认布局(LayoutType.Sequnetial)比显式布局(LayoutType.Auto)快一点。我期待相反的结果。

以下是我在我的机器上运行的测试(x86 运行 .NET 4)

//uses LayoutKind.Sequence by default
public struct StructSeq
{
    private readonly Byte mb;
    private readonly Int16 mx;
    public string a;
    public string b;
    public string c;
    public string d;
}

[StructLayout(LayoutKind.Auto)]
public struct StructAuto
{
    private readonly Byte mb;
    private readonly Int16 mx;
    public string a;
    public string b;
    public string c;
    public string d;
}

public sealed class Program
{
    public static void Main()
    {
        StructSeq sq = new StructSeq();
        Stopwatch sw1 = new Stopwatch();
        sw1.Start();
        for (int i = 0; i < 10000; i++)
        {
            sq = ProcessStructSeq(sq);
        }
        sw1.Stop();
        Console.WriteLine("Struct LayoutKind.Sequence (default) {0}", sw1.Elapsed.TotalMilliseconds);

        StructAuto so = new StructAuto();
        Stopwatch sw2 = new Stopwatch();
        sw2.Start();
        for (int i = 0; i < 10000; i++)
        {
            so = ProcessStructAuto(so);
        }
        sw2.Stop();
        Console.WriteLine("Struct LayoutKind.Auto (explicit) {0}", sw2.Elapsed.TotalMilliseconds);
        Console.ReadLine();
    }

    public static StructSeq ProcessStructSeq(StructSeq structSeq)
    {
        structSeq.a = "1";
        structSeq.b = "2";
        structSeq.c = "3";
        structSeq.d = "4";
        return structSeq;
    }        

    public static StructAuto ProcessStructAuto(StructAuto structAuto)
    {
        structAuto.a = "1";
        structAuto.b = "2";
        structAuto.c = "3";
        structAuto.d = "4";
        return structAuto;
    }
}

下面是我在我的机器上得到的示例结果(x86 运行 .NET 4)

Struct LayoutKind.Sequence(默认)0.7488

Struct LayoutKind.Auto(显式)0.7643

我多次运行此测试,我总是得到 Struct LayoutKind.Sequence (default)

即使是微毫秒的差异,我希望 Struct LayoutKind.Auto(显式)低于 Struct LayoutKind.Sequence(默认)。

有人知道这是什么原因吗?还是我的基准测试不够准确,无法给我正确的结果?

【问题讨论】:

    标签: c# performance c#-4.0 clr


    【解决方案1】:

    我相信由于您的字段的布局方式没有区别。您声明它们的方式,填充将是相同的。如果您尝试交错不同大小的字段,您应该会看到差异,至少在大小上,如果不是在速度上。

    此外,根据this blog post,具有引用字段的结构更改为自动布局(这意味着您实际上是在做同样的事情!)。

    public struct MyStruct
    {
        private byte b1;
        public long a;
        private byte b2;
        public long b;
        private byte b3;
        public long c;
        private byte b4;
        public long d;
    }
    

    【讨论】:

      【解决方案2】:

      我已经在我的系统上测试了你的代码,发现当测试运行很多次时,平均花费的时间是相同的,每次测试运行都会略微偏向一个或另一个替代方案。这适用于调试和发布版本。

      另外,作为快速检查,我查看了调试器中的 x86 代码,我发现生成的代码没有任何区别。因此,对于您的程序,您在测量中观察到的差异本质上似乎是噪声。

      【讨论】:

        【解决方案3】:

        老实说,它非常接近,除非您处理几百万个这样的结构,否则它不会产生任何明显的差异。事实上,多次运行它可能会产生不同的结果。我会增加迭代次数并尝试在没有附加调试器的情况下运行程序,看看是否有任何变化。

        不过,仅仅使用结构体并不能立即让您的代码更快,有许多缺陷会导致结构体比它们的等效类慢得多。

        如果你想优化这个基准,你应该将结构体作为引用传递给过程方法,而不是返回另一个结构体(避免为该方法创建 2 个额外的结构体),这应该比其他结构体提供更大的加速布局种类:

        public static void ProcessStructSeq(ref StructSeq structSeq)
        {
            structSeq.a = "1";
            structSeq.b = "2";
            structSeq.c = "3";
            structSeq.d = "4";
        }        
        
        public static void ProcessStructAuto(ref StructAuto structAuto)
        {
            structAuto.a = "1";
            structAuto.b = "2";
            structAuto.c = "3";
            structAuto.d = "4";
        }
        

        此外,结构体在某一点上变得比它们的类对应物慢,根据this MSDN article 估计大约为 16 个字节,this StackOverflow question 中进一步解释了这一点。

        【讨论】:

        • 谢谢罗伯特。正如您所建议的,我已经优化了基准测试,并且看到了预期的结果。变化是:将迭代次数增加到几百万,在 x86 发布版本中运行,并将结构作为 ref 传递。
        猜你喜欢
        • 2021-12-03
        • 2019-10-19
        • 2021-11-22
        • 1970-01-01
        • 1970-01-01
        • 2018-02-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多