繁体   English   中英

.NET装箱/拆箱与铸造性能

[英].NET boxing / unboxing vs casting performance

我试图从性能的角度理解两种解决方案中哪一种更受欢迎。 例如,我有两段代码:

1)拳击/拆箱

int val = 5;
Session["key"] = val; 
int val2 = (int)Session["key"];

2)强制转换(IntObj有int属性来存储int)

IntObj val = new IntObj(5);  
Session["key"] = val;
int val2 = ((IntObj )Session["key"]).Value;

这些例子之间的内存管理差异是什么? 有没有更快的方法来执行此类操作?

注意: Session只是例如,它可以是任何Dictionary<string, object>

看起来你在这里真正做的是比较手动拳击与内置拳击。 内置拳击已经高度优化 - 所以我不希望在这里看到巨大的差异,但我们可以检查。 重要的是,请注意两者都具有相同的内存影响:一个堆对象包含一个int字段,每个int boxed / wrapped。

以下显示两个接近的时间非常相同; 因此,我会说,只是直接/内置的方式。

注意:在发布模式下运行它,没有调试器(理想情况下在命令行)。 请注意,第一个调用是预先JIT所有内容。

using System;
using System.Diagnostics;
public sealed class IntObj
{
    public readonly int Value;
    public IntObj(int value)
    {
        Value = value;
    }
}
static class Program
{
    static void Main()
    {
        Run(1, 0, false);
        Run(100000, 500, true);
        Console.ReadKey();
    }
    static void Run(int length, int repeat, bool report)
    {
        var data = new object[length];

        int chk = 0;
        var watch = Stopwatch.StartNew();
        for (int j = 0; j < repeat; j++)
        {
            for (int i = 0; i < data.Length; i++)
            {
                data[i] = i;
                chk += i;
            }
        }
        watch.Stop();
        if(report) Console.WriteLine("Box: {0}ms (chk: {1})", watch.ElapsedMilliseconds, chk);
        chk = 0;
        watch = Stopwatch.StartNew();
        for (int j = 0; j < repeat; j++)
        {
            for (int i = 0; i < data.Length; i++)
            {
                chk += (int) data[i];
            }
        }
        watch.Stop();
        if (report) Console.WriteLine("Unbox: {0}ms (chk: {1})", watch.ElapsedMilliseconds, chk);

        chk = 0;
        watch = Stopwatch.StartNew();
        for (int j = 0; j < repeat; j++)
        {
            for (int i = 0; i < data.Length; i++)
            {
                data[i] = new IntObj(i);
                chk += i;
            }
        }
        watch.Stop();
        if (report) Console.WriteLine("Wrap: {0}ms (chk: {1})", watch.ElapsedMilliseconds, chk);
        chk = 0;
        watch = Stopwatch.StartNew();
        for (int j = 0; j < repeat; j++)
        {
            for (int i = 0; i < data.Length; i++)
            {
                chk += ((IntObj)data[i]).Value;
            }
        }
        watch.Stop();
        if (report) Console.WriteLine("Unwrap: {0}ms (chk: {1})", watch.ElapsedMilliseconds, chk);
    }


}

那么什么是更快,DIY拳击与IntObj或内置拳击?

我的猜测是内置的多样性。 有可能两个编译器都经过优化以处理它。

是否存在更“快速”的方式来执行此类操作?

对于大型数据集,首选方法是避免使用它。 对于小套装而言,这无关紧要。

做最快的事情就是不要做。 尝试重构您的数据,以避免大量的拳击,以同时获得更多的类型安全性,可读性和潜在的性能。

我发现你不太可能需要在无类字典中存储大量不相关的整数(或其他值类型)元素。 通常将值组织成一些对象组合在一起,在这种情况下,您将顶级对象存储在无类型字典中,并且只需要一个强制转换。 对于更深层次的元素,您可以使用强类型类(如Dictionary<string,int> ),因为这个问题已经解决,因为不需要装箱。

如果你觉得你的情况,你真的需要存储在字符串大量的int(或其他值类型元素)=> opbject映射它应该是很容易使用你的数据集ADN自己的目标,看看如果任一版本进行测量自己有很大的好处。 如果两者都满足你的目标(喜欢) - 选择一个产生最可读代码的(即对我来说它将是第一个变体)。

我对C#cast运算符生成的不同类型的IL指令进行了分类:

拳击(框IL指令)和拆箱(unbox IL指令)通过inhertiance层次结构(如C ++中的dynamic_cast,使用castclass IL指令进行验证)在原始类型之间进行转换(如C ++中的static_cast,有大量不同类型的IL指令原始类型之间的强制转换)调用用户定义的转换运算符(在IL级别,它们只是对相应op_XXX方法的方法调用)。

不同之处在于,在创建新引用类型时,cast会分配额外的内存。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM