为什么struct慢于浮动?

时间:2010-10-05 20:12:23

标签: c# performance struct

如果我有结构数组MyStruct[]

struct MyStruct 
{
    float x;
    float y;
}

它比我浮动[] - >慢x =>一世; y => i + 1(所以这个数组比结构大2倍。)

10,000件物品的时差相互比较(内部两件):结构500毫秒,只有浮子的阵列 - 78毫秒

我想,那个结构看起来就像是。 float,int等(在堆上)。

5 个答案:

答案 0 :(得分:2)

首先,结构不一定出现在堆上 - 它们可以并且经常出现在堆栈上。

关于您的性能测量,我认为您必须对其进行错误的测试。使用此基准测试代码,我得到两种类型的几乎相同的性能结果:

TwoFloats[] a = new TwoFloats[10000];
float[] b = new float[20000];

void test1()
{
    int count = 0;
    for (int i = 0; i < 10000; i += 1)
    {
        if (a[i].x < 10) count++;
    }
}

void test2()
{
    int count = 0;
    for (int i = 0; i < 20000; i += 2)
    {
        if (b[i] < 10) count++;
    }
}

结果:

Method   Iterations per second
test1                 55200000
test2                 54800000

答案 1 :(得分:1)

我得到的结果似乎与你同意(并且不同意马克)。我很好奇我是否在构建这个(尽管是粗略的)基准时犯了错误,或者是否还有另一个因素在起作用。

使用VS2008在面向.NET 3.5框架的MS C#上编译。发布模式,未附加调试器。

这是我的测试代码:

class Program {
    static void Main(string[] args) {
        for (int i = 0; i < 10; i++) {
            RunBench();
        }

        Console.ReadKey();
    }

    static void RunBench() {
        Stopwatch sw = new Stopwatch();

        const int numPoints = 10000;
        const int numFloats = numPoints * 2;
        int numEqs = 0;
        float[] rawFloats = new float[numFloats];
        Vec2[] vecs = new Vec2[numPoints];

        Random rnd = new Random();
        for (int i = 0; i < numPoints; i++) {
            rawFloats[i * 2] = (float) rnd.NextDouble();
            rawFloats[i * 2 + 1] = (float)rnd.NextDouble();
            vecs[i] = new Vec2() { X = rawFloats[i * 2], Y = rawFloats[i * 2 + 1] };
        }

        sw.Start();
        for (int i = 0; i < numFloats; i += 2) {
            for (int j = 0; j < numFloats; j += 2) {
                if (i != j &&
                    Math.Abs(rawFloats[i] - rawFloats[j]) < 0.0001 &&
                    Math.Abs(rawFloats[i + 1] - rawFloats[j + 1]) < 0.0001) {
                    numEqs++;
                }
            }
        }
        sw.Stop();

        Console.WriteLine(sw.ElapsedMilliseconds.ToString() + " : numEqs = " + numEqs);

        numEqs = 0;
        sw.Reset();
        sw.Start();
        for (int i = 0; i < numPoints; i++) {
            for (int j = 0; j < numPoints; j++) {
                if (i != j &&
                    Math.Abs(vecs[i].X - vecs[j].X) < 0.0001 &&
                    Math.Abs(vecs[i].Y - vecs[j].Y) < 0.0001) {
                    numEqs++;
                }
            }
        }
        sw.Stop();

        Console.WriteLine(sw.ElapsedMilliseconds.ToString() + " : numEqs = " + numEqs);
    }
}

struct Vec2 {
    public float X;
    public float Y;
}
编辑:啊!我没有迭代适当的金额。使用更新的代码,我的时间看起来像我预期的那样:

269 : numEqs = 8
269 : numEqs = 8
270 : numEqs = 2
269 : numEqs = 2
268 : numEqs = 4
270 : numEqs = 4
269 : numEqs = 2
268 : numEqs = 2
270 : numEqs = 6
270 : numEqs = 6
269 : numEqs = 8
268 : numEqs = 8
268 : numEqs = 4
270 : numEqs = 4
269 : numEqs = 6
269 : numEqs = 6
268 : numEqs = 2
270 : numEqs = 2
268 : numEqs = 4
270 : numEqs = 4

答案 2 :(得分:1)

如果你有这样的时间,你正在做一些严重的错误。浮点数比较非常快,我用2 纳秒计算它们的循环开销。制作这样的测试很棘手,因为如果你不使用比较结果,JIT编译器会优化你的东西。

对于笔记本电脑上的float [],结构稍快,1.96纳秒对2.20纳秒。这就是应该的方式,访问结构的Y成员不需要额外的数组索引。

测试代码:

using System;
using System.Diagnostics;

class Program {
    static void Main(string[] args) {
        var test1 = new float[100000000];  // 100 million
        for (int ix = 0; ix < test1.Length; ++ix) test1[ix] = ix;
        var test2 = new Test[test1.Length / 2];
        for (int ix = 0; ix < test2.Length; ++ix) test2[ix].x = test2[ix].y = ix;
        for (int cnt = 0; cnt < 20; ++cnt) {
            var sw1 = Stopwatch.StartNew();
            bool dummy = false;
            for (int ix = 0; ix < test1.Length; ix += 2) {
                dummy ^= test1[ix] >= test1[ix + 1];
            }
            sw1.Stop();
            var sw2 = Stopwatch.StartNew();
            for (int ix = 0; ix < test2.Length; ++ix) {
                dummy ^= test2[ix].x >= test2[ix].y;
            }
            sw2.Stop();
            Console.Write("", dummy);
            Console.WriteLine("{0} {1}", sw1.ElapsedMilliseconds, sw2.ElapsedMilliseconds);
        }
        Console.ReadLine();
    }
    struct Test {
        public float x;
        public float y;
    }
}

答案 3 :(得分:0)

最可能的原因是,当您使用具有完整结构的浮点数时,C#运行时优化程序执行的工作更好,可能是因为优化程序将x和y映射到寄存器,或者同样的更改没有使用完整结构。

在你的特定例子中,似乎没有任何根本原因,当你使用结构时它不能表现得很好(没有看到你真正的基准测试代码很难确定),但事实并非如此。但是,将编译后的结果代码的性能与其他C#实现进行比较会很有趣(我想在Linux上使用mono)。

我用单声道测试了Ron Warholic基准测试,结果与Mark的结果一致,两种类型的访问之间的差异似乎很小(带有浮点数的版本快1%)。然而,我仍然应该做更多的测试,因为像Math.Abs​​这样的库调用花费大量时间并且它可能隐藏真正的差异并不出乎意料。

删除对Math.Abs​​的调用并执行rawFloats[i] < rawFloats[j]之类的测试后,结构版本比两个浮点数组略快(约5%)。

答案 4 :(得分:0)

以下代码基于不同的迭代方式。在我的机器上,Test1b几乎是Test1a的两倍。我想知道这是否与你的问题有关。

class Program
{
    struct TwoFloats
    {
        public float x;
        public float y;
    }

    static TwoFloats[] a = new TwoFloats[10000];

    static int Test1a()
    {
        int count = 0;
        for (int i = 0; i < 10000; i += 1)
        {
            if (a[i].x < a[i].y) count++;
        }
        return count;
    }

    static int Test1b()
    {
        int count = 0;
        foreach (TwoFloats t in a)
        {
            if (t.x < t.y) count++;
        }
        return count;
    }

    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (int j = 0; j < 5000; ++j)
        {
            Test1a();
        }
        sw.Stop();
        Trace.WriteLine(sw.ElapsedMilliseconds);
        sw.Reset();
        sw.Start();
        for (int j = 0; j < 5000; ++j)
        {
            Test1b();
        }
        sw.Stop();
        Trace.WriteLine(sw.ElapsedMilliseconds);
    }

}