由于引用类型开销,在循环中访问Bitmap.Width比访问预先指定的变量要慢吗?

时间:2014-12-11 16:44:18

标签: c# .net winforms

我在C#Windows Forms上编码。

当我调用引用时,它会降低速度,我的意思是它与值类型相比要慢得多,这是因为:

  1. 每次调用.Net Framework。因为它是托管代码?

  2. 因为它是一种引用类型,并且在其他语言中也存在与非托管代码相同的问题。是

  3. 是不是因为它是托管代码?

    下一个代码显示:

            public Form1()
        {
            InitializeComponent();
    
            Method();
        }
    
        private void Method()
        {
            Bitmap bitmap1 = new Bitmap(100, 100);
            int width = 0;
            int w = bitmap1.Width;
    
            for (int i = 0; i < 500000000; i++)
            {
                //width = bitmap1.Width; // slower
                //width = w; // faster
            }
        }
    

2 个答案:

答案 0 :(得分:3)

速度较慢,因为.Width实际上调用了内部GDI方法,后者进行其他计算,并且还会短暂分配内存。

http://referencesource.microsoft.com/#System.Drawing/commonui/System/Drawing/Image.cs,9284bc31cef9a24b,references

请参阅Width属性。

它与值类型与引用类型无关。

答案 1 :(得分:0)

正如@Chris Eelmaa已经有效地表明的那样,给出的例子的问题是副作用:它实际上没有测试通过引用类型访问值带来大量开销的假设。这个假设的一个简单实验是定义一个包含值类型的引用类型:

class Program
{
    static void Main(string[] args)
    {
        var valueType = 100;
        var referenceType = new IntWrapper(100);

        var stopWatch = Stopwatch.StartNew();
        int assignee;

        for (int i = 0; i < 500000000; i++)
        {
            assignee = valueType;
        }
        stopWatch.Stop();
        Console.WriteLine("Accessed valueType 500m times in {0}ms", stopWatch.ElapsedMilliseconds);

        stopWatch.Restart();
        for (int i = 0; i < 500000000; i++)
        {
            assignee = referenceType.IntAccessor;
        }
        stopWatch.Stop();
        Console.WriteLine("Accessed referenceType 500m times in {0}ms", stopWatch.ElapsedMilliseconds);
        Console.ReadLine();
    }
}

public class IntWrapper
{
    private readonly int _intField;

    public IntWrapper(int i)
    {
        _intField = i;
    }

    public int IntAccessor
    {
        get { return _intField; }
    }
}

当我运行此控制台应用程序时,我会看到以下结果:

reftype vs valuetype

因此 似乎是访问引用类型属性的性能损失。然而,这种开销与托管代码和非托管代码无关 - 在.NET程序中使用后者需要特殊的注释,所有上面示例中的代码被“托管”为框架定义了它。正如@hatchet描述的那样,实际差异可能归结为编译器优化。我们可以通过检查编译器为每个循环发出的IL来确定。现在,我不得不将其作为读者的练习。