讀書筆記-C#中裝箱拆箱性能


 

前言

  最近在看王濤大神的《你必須知道的.NET(第二版)》一書,嗯,首先膜拜一下….

  在書中的第五章-品味類型中,對裝箱與拆箱一節感觸很深,概念本身相信每一個程序猿都不陌生,裝箱是將值類型轉換為引用類型 ,拆箱是將引用類型轉換為值類型(ps:不小心又背了一下書),也知道裝箱與拆箱過程中將帶來性能上的問題,但是在實際的項目中往往會忽略這個問題,將可能帶來極大的效率上的問題。問題有多大,反正我哭過。

簡單對比測試

  在工作之余寫了個簡單的測試例子,以HashTable、ArraryList、List<T>進行了簡單的對比。

  運行環境:Windows7_64(Cpu:i5; Ram:6GB)。

  代碼如下:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;

namespace Test
{
    /// <summary>
    /// 裝箱拆箱(HashTable ArraryList List<T>)對比
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                Console.WriteLine("循環次數:");
                string strcCycleNum = Console.ReadLine();
                int cycleNum = 0;
                if (!int.TryParse(strcCycleNum, out cycleNum))
                {
                    Console.WriteLine("無效輸入!");
                    continue;
                }
                HashTableCost(cycleNum);
                ArraryListCost(cycleNum);
                GenericCost(cycleNum);
            }
        }

        /// <summary>
        /// HashTable 開銷測試
        /// </summary>
        /// <param name="cycleNum">循環次數</param>
        static void HashTableCost(int cycleNum)
        {
            Stopwatch sw = new Stopwatch();
            Hashtable hs_Test = new Hashtable();
            sw.Start();
            for (int i = 0; i < cycleNum; i++)
            {
                hs_Test.Add(i, i);
            }
            sw.Stop();
            ConsoleInfo(cycleNum, "HashTableCost", sw.ElapsedMilliseconds);
        }

        /// <summary>
        /// ArraryList 開銷測試
        /// </summary>
        /// <param name="cycleNum">循環次數</param>
        static void ArraryListCost(int cycleNum)
        {
            Stopwatch sw = new Stopwatch();
            ArrayList al_Test = new ArrayList();
            sw.Start();
            for (int i = 0; i < cycleNum; i++)
            {
                al_Test.Add(i);
            }
            sw.Stop();
            ConsoleInfo(cycleNum, "ArraryListCost", sw.ElapsedMilliseconds);
        }

        /// <summary>
        /// 泛型 開銷測試
        /// </summary>
        /// <param name="cycleNum">循環次數</param>
        static void GenericCost(int cycleNum)
        {
            Stopwatch sw = new Stopwatch();
            List<int> lTest = new List<int>();
            sw.Start();
            for (int i = 0; i < cycleNum; i++)
            {
                lTest.Add(i);
            }
            sw.Stop();
            ConsoleInfo(cycleNum, "GenericCost", sw.ElapsedMilliseconds);
        }

        /// <summary>
        /// 打印信息
        /// </summary>
        /// <param name="cycleNum">循環次數</param>
        /// <param name="methodName">方法名稱</param>
        /// <param name="cost">開銷</param>
        static void ConsoleInfo(int cycleNum, string methodName, long cost)
        {
            Console.WriteLine(methodName + " 循環次數:" + cycleNum.ToString() + "  開銷(毫秒):" + cost.ToString());
        }

    }
}

測試結果:

測試結果值

對於測試結果還是很驚訝,循環添加1000次,3者都為0毫秒,在10W與100W次的結果出現了鮮明的對比分別為305ms,86ms,9ms。差距不是一般的大。

對代碼進行分析:

// HashTable : public virtual void Add(object key, object value); 
// 在Add的過程中進行了2次裝箱
 hs_Test.Add(i, i);

HashTable.add()都會產生2次裝箱。

// public virtual int Add(object value);
// 產生了一次裝箱
 al_Test.Add(i);

ArraryList.add產生2次裝箱。

// 由於lTest 指定了類型(List<int>)並沒有產生裝箱
lTest.Add(i);

List<int>沒有產生裝箱。

也可以通過IL對代碼進行分析,這里就不再列出了。

總結

在對大量數據進行操作的時候一定要注意裝箱與拆箱操作,可以用泛型代替的地方還是使用泛型吧。

在平時的一般項目中的細節也需要注意,畢竟細節決定成敗,再NB的框架,也經不起這一片片的裝箱與拆箱的轟炸。

(ps:隱式轉換的時候要細心,在轉換成字符串的時候習慣使用ToString(),畢竟是不產生裝箱的。)


免責聲明!

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



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