Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/274.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么在我的例子中,值类型变量比引用类型慢?_C#_Optimization_Value Type_Reference Type - Fatal编程技术网

C# 为什么在我的例子中,值类型变量比引用类型慢?

C# 为什么在我的例子中,值类型变量比引用类型慢?,c#,optimization,value-type,reference-type,C#,Optimization,Value Type,Reference Type,值类型结构是基于堆栈的变量,驻留在处理器缓存中而不是RAM中。因此,通过避免使用值类型变量通过系统总线从处理器到RAM的跳闸,应该比使用引用类型变量更快。因此,我没有执行stackallock(),而是创建了以下表示基于堆栈的数组的结构: [StructLayout(LayoutKind.Sequential)] public struct ArrayOf16Bytes { public Byte e0; public Byte e1; public Byte e2;

值类型结构是基于堆栈的变量,驻留在处理器缓存中而不是RAM中。因此,通过避免使用值类型变量通过系统总线从处理器到RAM的跳闸,应该比使用引用类型变量更快。因此,我没有执行
stackallock
(),而是创建了以下表示基于堆栈的数组的结构:

[StructLayout(LayoutKind.Sequential)]
public struct ArrayOf16Bytes
{
    public Byte e0;
    public Byte e1;
    public Byte e2;
    public Byte e3;
    public Byte e4;
    public Byte e5;
    public Byte e6;
    public Byte e7;
    public Byte e8;
    public Byte e9;
    public Byte eA;
    public Byte eB;
    public Byte eC;
    public Byte eD;
    public Byte eE;
    public Byte eF;

    public byte this[Int32 index] {
        get {
            switch (index) {
            case 0x0:
                return e0;
            case 0x1:
                return e1;
            case 0x2:   
                return e2;
            case 0x3:
                return e3;
            case 0x4:
                return e4;
            case 0x5:
                return e5;
            case 0x6:
                return e6;
            case 0x7:
                return e7;
            case 0x8:
                return e8;
            case 0x9:
                return e9;
            case 0xA:
                return eA;
            case 0xB:
                return eB;
            case 0xC:
                return eC;
            case 0xD:
                return eD;
            case 0xE:
                return eE;
            case 0xF:
                return eF;
            default:
                throw new IndexOutOfRangeException ();
            }
        }
        set {
            switch (index) {
            case 0x0:
                e0 = value;
                break;
            case 0x1:
                e1 = value;
                break;
            case 0x2:
                e2 = value;
                break;
            case 0x3:
                e3 = value;
                break;
            case 0x4:
                e4 = value;
                break;
            case 0x5:
                e5 = value;
                break;
            case 0x6:
                e6 = value;
                break;
            case 0x7:
                e7 = value;
                break;
            case 0x8:
                e8 = value;
                break;
            case 0x9:
                e9 = value;
                break;
            case 0xA:
                eA = value;
                break;
            case 0xB:
                eB = value;
                break;
            case 0xC:
                eC = value;
                break;
            case 0xD:
                eD = value;
                break;
            case 0xE:
                eE = value;
                break;
            case 0xF:
                eF = value;
                break;
            default:
                throw new IndexOutOfRangeException ();
            }
        }
    }
}
case
应编译为
跳转表
,因为
cmp
jump
是单周期指令(),第一段代码应该比第二段代码快得多

在以下示例中,工作速度比实际阵列慢:

[Test]
public void TestStackArrayPerformance() {
    var random = new Xor128 ();

    byte[] x = new byte[16];

    ArrayOf16Bytes p = new ArrayOf16Bytes ();

    for (int i = 0; i < 16; i++) {
        x [i] = p [i] = random.As<IUniform<byte>> ().Evaluate ();
    }

    var index = random.As<IUniform<Int32>> ().Evaluate (0, 15);

    var timer = DateTime.Now;
    for (int i = 0; i < 1000000000; i++) {
        var t = x [i & 0xF];
        x [i & 0xF] = x [index];
        x [index] = t;
    }
    Console.WriteLine ("Spinup took: {0}", DateTime.Now - timer);

    timer = DateTime.Now;
    for (int i = 0; i < 1000000000; i++) {
        var t = x [i & 0xF];
        x [i & 0xF] = x [index];
        x [index] = t;
    }
    Console.WriteLine ("Operation 1 took: {0}", DateTime.Now - timer);


    timer = DateTime.Now;
    for (int i = 0; i < 100000000; i++) {
        var t = p [i & 0xF];
        p [i & 0xF] = p [index];
        p [index] = t;
    }
    Console.WriteLine ("Operation 2 took: {0}", DateTime.Now - timer);

}

我不是这方面的专家,但我相信你在这里有一些错误的假设:

值类型结构是基于堆栈的变量,驻留在处理器缓存中而不是RAM中。因此,通过避免使用值类型变量通过系统总线从处理器到RAM的跳闸,应该比使用引用类型变量更快

仅仅因为某些东西是引用类型并不意味着CPU缓存将不被使用。堆栈不是唯一可以使用缓存的内存区域。此外,CPU在预取到缓存等方面非常智能,因此您通常不必像这样对内存进行微管理


另外,请记住,当您在循环中访问
结构时,您需要担心的不仅仅是getter和setter中的指令;在任何时候进行方法调用(包括索引器)都会产生开销。方法调用涉及将参数推送到堆栈、跳转到方法指令、将返回值推送到堆栈、执行返回跳转等。因此,毫不奇怪,这比设置数组值的简单指令成本更高。

我已经在为自己的项目工作很长时间了,它的状态是相当优化的。我有一个想法,如何进一步推动它,但没有起作用。至于方法调用,您可能是对的,我认为编译器将内联我计划用[MethodImpl(MethodImplOptions.AggressiveInline)]覆盖的代码,但索引器不能覆盖,我懒得用方法检查它,因为我甚至没有看到一个迹象表明它可以工作,在我的例子中,即使是
stackallock
'ed数组也比普通字节数组慢,所以无论哪种方式,这都是小菜一碟。。。无论如何谢谢你。。。
Spinup took: 00:00:00.3005500
Operation 1 took: 00:00:00.2959800
Operation 2 took: 00:00:04.4344340