C# 高性能的數組 高性能數組隊列實戰 HslCommunication的SharpList類詳解


本文將使用一個gitHub開源的組件技術來實現這個功能

github地址:https://github.com/dathlin/HslCommunication      fork      star                 如果喜歡可以star或是fork,還可以打賞支持。

官網地址:http://www.hslcommunication.cn/         打賞請認准官網

 

 

 

 

場景需求


 

我們會有對緩存數據的需求。C#本身提供了固定長度的數組 T[]  , 可變長度的List<T>  當然還有先入先出,后入后出的隊列。

 

通常實際中,我們需要維護一個緩存的數組隊列,比如一個int數組,長度為1000個,當我們讀取到數據后,需要往里面添加數據,然后所有的數據都是往左挪動。最后這個數據是線程安全的操作。

 

第一種寫法,就是循環挪動數據:

int[] buffer = new int[1000];

                hybirdLock.Enter( );

                for (int j = 0; j < buffer.Length - 1; j++)
                {
                    buffer[j] = buffer[j + 1];
                }

                buffer[999] = 100;

                hybirdLock.Leave( );

    

 

第二種寫法,批量挪動數據

int[] buffer = new int[1000];

                hybirdLock.Enter( );

                int[] newbuffer = new int[1000];
                Array.Copy( buffer, 0, newbuffer, 0, 999 );
                newbuffer[999] = 100;
                buffer = newbuffer;

                hybirdLock.Leave( );

  

第三種寫法,就是List泛型類,和先入先出的用法差不多

int[] buffer = new int[1000];
List<int> list = new List<int>( buffer );

 hybirdLock.Enter( );

                list.Add( 100 );
                list.RemoveAt( 0 );

                hybirdLock.Leave( );

  

 

第四種寫法:SharpList<T> 類型實現

 

            SharpList<int> sharpList = new SharpList<int>( 1000, true );
            sharpList.Add( 1000 );

 

 

 

初步對比,SharpList代碼上更加精簡。因為內置了線程安全,自動挪動數據。

上述代碼我們定義了一個長度為1000的int類型的數組對象。實例化之后,其本身就是一個1000個長度的 int[] 數組,當 Add(100);時,最右側就多了一個100的數據。

 

SharpList<T> 提供了幾個方法來方便快捷的操作數據。比如根據索引為訪問:

int value = sharpList[0];  // 得到0
int value2 = sharpList[999]; // 得到100


int[] tmp = sharpList.ToArray();        // 得到數組數據的副本。[0,,,,,,,,,,999]

  

當新增數據的時候,也支持批量的新增。

 

 

性能對比


 

我們將上述的四種方式各自運行100W次,查看下各自的性能差異,具體運行時間取決於cpu型號,內存,等因數,此處僅僅是一個參考。

 

            SimpleHybirdLock hybirdLock = new SimpleHybirdLock( );

            int[] buffer = new int[1000];
            DateTime start = DateTime.Now;
            for (int i = 0; i < 1000000; i++)
            {
                hybirdLock.Enter( );

                for (int j = 0; j < buffer.Length - 1; j++)
                {
                    buffer[j] = buffer[j + 1];
                }

                buffer[999] = i;

                hybirdLock.Leave( );
            }

            Console.WriteLine( (DateTime.Now - start).TotalMilliseconds );



            start = DateTime.Now;
            for (int i = 0; i < 1000000; i++)
            {
                hybirdLock.Enter( );

                int[] newbuffer = new int[1000];
                Array.Copy( buffer, 0, newbuffer, 0, 999 );
                newbuffer[999] = i;
                buffer = newbuffer;

                hybirdLock.Leave( );
            }

            Console.WriteLine( (DateTime.Now - start).TotalMilliseconds );


            List<int> list = new List<int>( buffer );
            start = DateTime.Now;
            for (int i = 0; i < 1000000; i++)
            {
                hybirdLock.Enter( );

                list.Add( i );
                list.RemoveAt( 0 );

                hybirdLock.Leave( );
            }

            Console.WriteLine( (DateTime.Now - start).TotalMilliseconds );


            SharpList<int> sharpList = new SharpList<int>( 1000, true );
            start = DateTime.Now;
            for (int i = 0; i < 1000000; i++)
            {
                sharpList.Add( i );
            }
            Console.WriteLine( (DateTime.Now - start).TotalMilliseconds );

            int[] data = sharpList.ToArray( );

            Console.ReadLine( );

  

我們跑三次,對比結果。

 

 

 

 

 

我們看到SharpList<T> 類僅僅消耗了37ms,完成了100W次數據的新增和挪動。

為什么會有那么大的性能差異呢?就要深入源代碼查看了。

 

 

深度剖析


 

 超高的性能的本質在於減少大塊的數據移動,先內部實例化一個遠比需求還大的多的數據對象

array = new T[capacity + count];

  

 當有數據新增進來的時候,實際不需要移動

        /// <summary>
        /// 新增一個數據值
        /// </summary>
        /// <param name="value">數據值</param>
        public void Add( T value )
        {
            hybirdLock.Enter( );

            if(lastIndex < (capacity + count))
            {
                array[lastIndex++] = value;
            }
            else
            {
                // 需要重新挪位置了
                T[] buffer = new T[capacity + count];
                Array.Copy( array, capacity, buffer, 0, count );
                array = buffer;
                lastIndex = count;
            }

            hybirdLock.Leave( );
        }

  先進行自然的賦值,這時的性能就非常快了,然后提高游標的索引。當緩存都不夠時,再去復制挪動一次數據。

 

 

當然,當要獲取數據時,就需要進行根據當前的活動游標進行獲取到正確的數據。

        /// <summary>
        /// 獲取數據的數組值
        /// </summary>
        /// <returns>數組值</returns>
        public T[] ToArray( )
        {
            T[] result = null;
            hybirdLock.Enter( );

            if (lastIndex < count)
            {
                result = new T[lastIndex];
                Array.Copy( array, 0, result, 0, lastIndex );
            }
            else
            {
                result = new T[count];
                Array.Copy( array, lastIndex - count, result, 0, count );
            }
            hybirdLock.Leave( );
            return result;
        }

  

 相關的話題,后續補充。


免責聲明!

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



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