C#中誰最快:結構還是類?


前言

在內存當道的日子里,無論什么時候都要考慮這些代碼是否會影響程序性能呢?
在現在的世界里,幾乎不會去考慮用了幾百毫秒,可是在特別的場景了,往往這幾百毫米確影響了整個項目的快慢。
通過了解這兩者之間的性能差異,希望幫助大家在合適的場景里選擇正確的編碼。

實例

public class PointClass
{
    public int X { get; set; }
    public int Y { get; set; }
    public PointClass(int x, int y)
    {
        X = x;
        Y = y;
    }
}

public class PointClassFinalized : PointClass
{
    public PointClassFinalized(int x, int y) : base(x, y)
    {
    }
    ~PointClassFinalized()
    {
        // added a finalizer to slow down the GC

    }
}

public struct PointStruct
{
    public int X { get; set; }
    public int Y { get; set; }
    public PointStruct(int x, int y)
    {
        X = x;
        Y = y;
    }
}

public class StructsTest : PerformanceTest
{
    protected override bool MeasureTestA()
    {
        // access array elements
        var list = new PointClassFinalized[Iterations];
        for (int i = 0; i < Iterations; i++)
        {
            list[i] = new PointClassFinalized(i, i);
        }
        return true;
    }

    protected override bool MeasureTestB()
    {
        // access array elements
        var list = new PointClass[Iterations];
        for (int i = 0; i < Iterations; i++)
        {
            list[i] = new PointClass(i, i);
        }
        return true;
    }

    protected override bool MeasureTestC()
    {
        // access array elements
        var list = new PointStruct[Iterations];
        for (int i = 0; i < Iterations; i++)
        {
            list[i] = new PointStruct(i, i);
        }
        return true;
    }
}

有一個PointClass 和一個 PointStruct
,這兩者用於存放X 和Y 兩個變量,而且還有一個 PointClassFinalized

方法 MeasureTestA 創建了100萬個 PointClassFinalized 實例

方法 MeasureTestB 創建了100萬個 PointClass 實例

方法 MeasureTestC 創建了100萬個 PointStruct 實例

您認為哪種方法最快?

MeasureTestBMeasureTestC 這兩個方法的唯一不同在於一個是創建類 一個是創建結構。

MeasureTestC 僅在17毫秒內完成分配並運行,比 MeasureTestB 方法快8.6倍!

為什么會出現這樣的事情,這里發生了什么?

不同的在於結構和類如何存儲在內存中。

下面是 PointClass 實例 內存布局:

該列表是一個局部變量,存放在堆棧中。引用堆上的一組 PointClass實例

PointClass 是一個引用類型,存放在堆上。

該列表僅維護一個數組,指向存儲在堆上 PointClass 實例。

觀察到上圖的黃色箭頭,在堆上引用了很多實例。

數組是一組相同的對象,MeasureTestB 這個方法是將一組相同的對象存放在數組中。

當訪問指定數組元素時,.NET運行時需要檢索對象引用,然后“跟隨”引用以獲取PointClass實例。

當數組元素超出范圍時,.NET垃圾收集器就會開始回收PointClass對象內存,在 MeasureTestA 方法中 的PointClassFinalized類 其實增加了額外時間。

.NET Framework在單個線程上運行所有終結器,線程必須在垃圾回收器可以回收內存之前依次處理1,000,000個對象。

可以看到MeasureTestAMeasureTestB慢1.7倍。

我們來看看 PointStruct 的內存布局:

結構是值類型,所有 PointStruct 實例都存儲在數組本身中。堆上只有一個對象。

初始化數組,.NET運行庫可以將X和Y值直接寫入數組里。無需在堆上創建新對象,也不需要引用它。

當訪問指定數組元素時,.NET運行時可以直接檢索結構。

當超出范圍時,.NET垃圾回收器只需要處理單個對象。

總結

我們總要使用結構嗎?要分情況看:

  • 當您存儲超過30-40個字節的數據時,請使用類。
  • 存儲引用類型時,請使用類。
  • 當您存儲多於幾千個實例時,請使用類。
  • 如果列表是長的生命周期的,請使用類。
  • 在所有其他情況下,使用結構。

相關鏈接:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM