本文將使用一個gitHub開源的組件技術來實現這個功能
github地址:https://github.com/dathlin/HslCommunication
如果喜歡可以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; }
相關的話題,后續補充。