2012-09-06 34 views
8

我想了解哪两个解决方案是从性能的角度来看是首选。 例如,我有两段代码:.NET的拳击/拆箱VS铸造性能

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>

+2

我还没有测量过,但我会说原始速度会更快。一个对象有开销。但在这种情况下,我认为可读性是一个更重要的措施。 Cpu周期比大脑周期便宜很多 –

回答

7

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

下面显示两个接近的相同时间;因此,我会说,把它直接/内置的方式。

注意:在发布模式下运行,没有调试器(理想情况下在命令行)。注意第一个电话是在那里预先处理所有事情。

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


} 
+0

谢谢!现在很容易看到:) –

4

那么,什么是速度更快,DIY拳击与IntObj或内置在拳击比赛?

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

确实存在更“快”的方式来执行此类操作吗?

首选的方法是避免大数据集。对于小集合,它根本无关紧要。

+0

我举了一个Session,例如它可能是一个简单的Dictionary / –

+0

我假设问题是关于IntObj的。容器(Session)不是很相关。 –

0

我分类的不同类型的C#转换运算符生成的IL操作的指令:

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

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

+3

那么如何回答这个问题呢? (拳击也在堆上创建一个新的实例) –

2

做某事的最快方法并不是完全没有。尝试重构数据以避免大量拳击,以获得更多的类型安全性,可读性和潜在性能。

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

如果你觉得在你的情况下你确实需要在string => opbject map中存储大量的int(或其他值类型元素),那么使用数据集和你的目标看看其中任何一个版本是否具有显着的优势。如果两者都满足你的目标(比如) - 选择一个产生最可读代码的代码(即,对我而言,它将是第一个变体)。