技術(shù)頻道導(dǎo)航
HTML/CSS
.NET技術(shù)
IIS技術(shù)
PHP技術(shù)
Js/JQuery
Photoshop
Fireworks
服務(wù)器技術(shù)
操作系統(tǒng)
網(wǎng)站運(yùn)營

贊助商

分類目錄

贊助商

最新文章

搜索

11個提高C#代碼性能的技巧

作者:admin    時間:2023-4-28 16:20:18    瀏覽:

為什么要關(guān)注代碼性能?因?yàn)榇a性能會影響程序的執(zhí)行效率,在客戶端就表現(xiàn)為等待時間的長短,這就影響到用戶的使用體驗(yàn),因此,我們開發(fā)人員不得不對代碼性能給予足夠的重視。本文中,我將總結(jié)11個提高C#代碼性能的技巧。

1、循環(huán)并行化

foreach 是當(dāng)今最常用的循環(huán),僅次于 for 循環(huán)。它提供了靈活性,不用擔(dān)心循環(huán)計(jì)數(shù),它會運(yùn)行到集合的長度。

foreach循環(huán)中,迭代在同一線程中一個接一個地執(zhí)行,因此總執(zhí)行時間將隨集合大小線性增長。

使用 .Net Framework 4.0 提供給開發(fā)者的并行版本的foreach循環(huán),性能可以有很大的提升。

Parallel.ForEach 可用于實(shí)現(xiàn) IEnumerable<T> 接口的任何集合,就像常規(guī)的 foreach 循環(huán)一樣。Parallel.ForEach 將代替開發(fā)人員做很多工作:它根據(jù)機(jī)器上可用的核心將集合分成塊,在單獨(dú)的線程中調(diào)度和執(zhí)行塊,然后組合結(jié)果。

以下是 foreach 循環(huán)的并行版本與普通版本之間的性能比較:

如果集合很小并且單次迭代的執(zhí)行時間很快,則將 foreach 切換為 Parallel。Parallel.ForEach 可能使性能變差,因?yàn)樗ㄟ^拆分和收集結(jié)果增加了管理循環(huán)的成本。

2、使用 StringBuilder 而不是字符串連接

在 C# 中,字符串連接在每次調(diào)用時都會創(chuàng)建一個新的字符串對象。如果你連接大量字符串,這可能效率低下。為避免這種情況,請改用 StringBuilder。

例子:

var sb = new StringBuilder();
sb.Append("Hello ");
sb.Append("World");
var result = sb.ToString();

3、使用 LINQ 進(jìn)行過濾和排序

在 C# 中,LINQ 提供了用于篩選、排序和操作集合的強(qiáng)大工具。LINQ 比手動遍歷集合并對每個項(xiàng)目執(zhí)行操作更有效。

例子:

var numbers = new List<int> { 1, 2, 3, 4, 5 };
var result = numbers.Where(n => n % 2 == 0).OrderByDescending(n => n);

4、一次性物化 LINQ 查詢

何為一次性物化 LINQ 查詢?即是不要使用表達(dá)式樹來保存結(jié)果,而是盡可能通過執(zhí)行來提取結(jié)果。

使用 IEnumerableIQueryable 接口編寫 LINQ 查詢時,開發(fā)人員可以具體化(調(diào)用 ToListToArray 類似方法)或不立即具體化查詢(延遲加載)。

如果查詢不是通過調(diào)用 ToList/ToArray 方法實(shí)現(xiàn)的,多次迭代集合將影響應(yīng)用程序的性能。

這是一個示例,我們在其中測試延遲加載 LINQ 查詢與物化查詢的性能。

[MemoryDiagnoser]
public class LinqTest {
    [Benchmark]
    public void NoMaterializedTest() {
            var list = Enumerable.Range(0, 50000000);
            var filteredlist = list.Where(element => element % 100000 == 0);
            foreach(var element in filteredlist) {
                //some logic here
            }
            foreach(var element in filteredlist) {
                // some logic here
            }
            foreach(var element in filteredlist) {
                // some logic here
            }
        }
        [Benchmark]
    public void MaterializedTest() {
        var list = Enumerable.Range(0, 5000000);
        var filteredlist = list.Where(element => element % 10000 == 0).ToList();
        //ToList() method will execute the expression tree and get the result for future purpose.
        foreach(var element in filteredlist) {
            // some logic here
        }
        foreach(var element in filteredlist) {
            // some logic here
        }
        foreach(var element in filteredlist) {
            // some logic here
        }
    }
    public static void Main() {
        //Execute LinqTest class methods to check the benchmark
        var result = BenchmarkRunner.Run < LinqTest > ();
    }

在這里可以看到差異,與延遲加載 LINQ 查詢相比,物化查詢的性能提高了三倍。盡管這種方法并不總是有效,因?yàn)槲覀兛赡懿粫啻螆?zhí)行相同的結(jié)果集。在具體化 LINQ 查詢之前始終檢查大小寫。

5、異步編程

異步編程是一種允許你的代碼在等待長時間運(yùn)行的操作完成時繼續(xù)執(zhí)行的技術(shù)。這在處理 I/O 操作時特別有用,例如讀取和寫入文件或發(fā)出 Web 請求。

在 C# 中,異步編程是使用 asyncawait 關(guān)鍵字實(shí)現(xiàn)的。

例子:

async Task<string> ReadFileAsync(string filePath)
{
    using (var reader = new StreamReader(filePath))
    {
        return await reader.ReadToEndAsync();
    }
}

6、對大型集合使用 IEnumerable 而不是列表

在 C# 中,列表是存儲和操作對象集合的有效方式。但是,將 IEnumerable 用于廣泛的集合會更有效,因?yàn)樗苊饬藙?chuàng)建和維護(hù) List 對象的開銷。

例子:

public IEnumerable<int> GetNumbers()
{
    for (int i = 0; i < 1000000; i++)
    {
        yield return i;
    }
}

foreach (var number in GetNumbers())
{
    Console.WriteLine(number);
}

7、使用枚舉標(biāo)志進(jìn)行位運(yùn)算

在 C# 中,枚舉可以用作標(biāo)志,這使您可以對它們執(zhí)行按位運(yùn)算。這在將多個值存儲在單個變量中時非常有用。

例子:

[Flags]
public enum Colors
{
    None = 0,
    Red = 1,
    Green = 2,
    Blue = 4
}

var colors = Colors.Red | Colors.Green;

8、對小型數(shù)據(jù)結(jié)構(gòu)使用結(jié)構(gòu)而不是類

在 C# 中,結(jié)構(gòu)是值類型,類是引用類型。結(jié)構(gòu)分配在棧上,而類分配在堆上。對于小型數(shù)據(jù)結(jié)構(gòu),使用結(jié)構(gòu)可以更有效,因?yàn)樗苊饬硕逊峙浜屠占拈_銷。

例子:

public struct Point
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }
}

var p = new Point(10, 20);

9、使用泛型

泛型是 C# 中的一項(xiàng)強(qiáng)大功能,可讓你創(chuàng)建可重用的代碼。通過使用泛型類型參數(shù)定義類或方法,你可以創(chuàng)建類型安全、可重用的組件,該組件可以處理各種數(shù)據(jù)類型。這可以通過減少你需要編寫的重復(fù)代碼來幫助提高性能。

下面是使用泛型創(chuàng)建可重用排序方法的示例:

public static void Sort<T>(T[] array) where T : IComparable<T>
{
    for (int i = 0; i < array.Length; i++)
    {
        for (int j = i + 1; j < array.Length; j++)
        {
            if (array[j].CompareTo(array[i]) < 0)
            {
                T temp = array[i];
                array[i] = array[j];
                array[j] = temp;
            }
        }
    }
}

10、使用接口實(shí)現(xiàn)松散耦合

在C#中,接口為組件之間的松散耦合提供了一種強(qiáng)大的機(jī)制。通過定義服務(wù)或功能的接口,您可以確保組件可以輕松換出或更換,而不會影響系統(tǒng)的其余部分。這種方法可以通過最小化更改對整個系統(tǒng)的影響來提高代碼性能。

例如,考慮以下定義用于訪問數(shù)據(jù)的通用存儲庫的接口:

interface IRepository<T> {
    void Save(T entity);
    T Get(int id);
}

這個接口可以定義一個通用的數(shù)據(jù)訪問層,可以很容易地替換或更新而不影響系統(tǒng)的其余部分。

11、盡可能使用 Struct 而不是 Class

大多數(shù)時候我看到開發(fā)人員創(chuàng)建類時沒有對 ClassStruct 進(jìn)行很好的比較。開發(fā)人員通常可能需要分配一個數(shù)組或 List<T> 在內(nèi)存中存儲數(shù)萬個對象。這個任務(wù)可以使用類或結(jié)構(gòu)來解決。

正如我們所看到的, ListOfClassObjectTestListOfStructsObjectTest之間的唯一區(qū)別,是第一個測試創(chuàng)建類的實(shí)例,而第二個測試創(chuàng)建結(jié)構(gòu)的實(shí)例。

如下示例中,PointClass 和  PointStruct 的代碼是相同的:

//Reference type. Stored on Heap.
public class PointClass {
    public int X {
        get;
        set;
    }
    public int Y {
        get;
        set;
    }
}
//Value type. Stored on Stack
public struct PointStruct {
    public int X {
        get;
        set;
    }
    public int Y {
        get;
        set;
    }
}
[MemoryDiagnoser]
public class ClassVsStruct {
    [Benchmark]
    public void ListOfClassObjectsTest() {
            const int length = 1000000;
            var items = new List < PointClass > (length);
            for (int i = 0; i < length; i++) {
                items.Add(new PointClass() {
                    X = i, Y = i
                });
            }
        }
        [Benchmark]
    public void ListOfStructsObjectTest() {
        const int length = 1000000;
        var items = new List < PointStruct > (length);
        for (int i = 0; i < length; i++) {
            items.Add(new PointStruct() {
                X = i, Y = i
            });
        }
    }
}

 

可以看到,使用結(jié)構(gòu)的代碼比使用類的代碼運(yùn)行速度快 15 倍。

對于類,CLR 必須將一百萬個對象分配給托管堆并將它們的引用存儲回List<T>集合。在結(jié)構(gòu)的情況下,將有唯一的對象分配到托管堆中 ,它是List<T>集合的實(shí)例。一百萬個結(jié)構(gòu)將嵌入到該單個集合實(shí)例中。

總結(jié)

以上11點(diǎn)希望對各位開發(fā)者提升代碼質(zhì)量有所幫助,通過這些開發(fā)技巧,開發(fā)人員可以創(chuàng)建執(zhí)行速度更快、占用系統(tǒng)資源更少的應(yīng)用程序。

標(biāo)簽: asp.net  CSharp  代碼性能  優(yōu)化  
x
  • 站長推薦
/* 左側(cè)顯示文章內(nèi)容目錄 */