天天看點

C# 位元組數組 byte[] 快速比較算法

注:代碼原創轉載或使用請标明出處。

目前C#最高版本net6 preview 7,我沒有找到快速比較兩個位元組數組 byte[]的api,如若誰知道請在下方留言,謝謝。

在沒有使用某些cpu特有的比較指令實作的純C#的快速比較byte[]的思路:

把byte[]轉成byte*,然後利用64位cpu一次可比較64位資料的特點把byte強制轉換成long進行比較理論上速度可以快一倍。

原本32位程式應該使用int比較,但經測試發現在64系統上32位程式使用long會比使用int*快一點。

代碼如下:

//需要在項目屬性的“生成”勾上“允許不安全代碼”
//public static class ArrayExt ...
public unsafe static bool EqualsAll(this byte[] arr1, byte[]? arr2)	//如果代碼不是nullable就去掉"?"
{
	if (Object.ReferenceEquals(arr1, arr2)) return true;

	int length = arr1.Length;
	if (arr2 == null || length != arr2.Length)
		return false;

	if (length < 4)
	{
		for (int i = 0; i < arr1.Length; i++)
		{
			if (arr1[i] != arr2[i])
				return false;
		}
		return true;
	}
	else
	{
		fixed (void* voidby1 = arr1)
		{
			fixed (void* voidby2 = arr2)
			{
				const int cOneCompareSize = 8;

				var blkCount = length / cOneCompareSize;
				var less = length % cOneCompareSize;

				byte* by1, by2;

				long* lby1 = (long*)voidby1;
				long* lby2 = (long*)voidby2;
				while (blkCount > 0)
				{
					if (*lby1 != *lby2)
						return false;
					lby1++; lby2++;
					blkCount--;
				}

				if (less >= 4) //此if和true的代碼可以不要,性能差異不大
				{
					if (*((int*)lby1) != *((int*)lby2))
						return false;

					by1 = ((byte*)lby1 + 4);
					by2 = ((byte*)lby2 + 4);

					less = less - 4;
				}
				else
				{
					by1 = (byte*)lby1;
					by2 = (byte*)lby2;
				}

				while (less-- > 0)
				{
					if (*by1 != *by2)
						return false;
					by1++; by2++;
				}
				return true;
			}
		}
	}
}
           
測試資料:

測試環境:win7 x64, net5,100MB的byte[]

注:由于是64位系統,是以以下的64位和Any CPU基本上是一樣的

---參考資料---
64位debug直接byte比較
335.0191
335.0192
64位release直接byte比較
63.0036	//release 有優化
64.0036
--
64位release使用EqualsAll比較
13.0007
13.0007
---參考資料結束---

32位debug使用long比較
55.0031
56.0032

32位debug使用int比較
74.0042
76.0044

64位debug使用long比較 與“32位使用int比較”時間少(75 - 41)/75≈45%
41.0024
39.0022
           

以上是多次測試取合适的一組的結果,不同電腦測試結果會不同,但結論是差不多的,包括release也是。

最終結果:

在64位系統Any CPU,100MB資料量下,

debug 模式下,使用EqualsAll和直接byte[]比較,快335/((41 + 39)/2) - 1≈ 7倍

release 模式下,使用EqualsAll和直接byte[]比較,快(63+64)/2/(13) - 1 ≈4倍

上面差異比較大,實際上EqualsAll沒有快多少,是因為一般比較少出現100MB資料比較的情況,

附部分測試代碼:

byte[] arr1, arr2;
arr1 = new byte[100 * 1024 * 1024];	//100MB
for (int i = 0; i < arr1.Length; i++)
	arr1[i] = (byte)i;
arr2 = new byte[arr1.Length];
Buffer.BlockCopy(arr1, 0, arr2, 0, arr1.Length);
DateTime now = DateTime.Now;
Assert.IsTrue(ArrayExt.EqualsAll(arr1, arr2));  //如果不是測試環境去掉Assert.IsTrue
Trace.WriteLine((DateTime.Now - now).TotalMilliseconds);
arr2[arr2.Length - 1] = (byte)(arr2[arr2.Length - 1] + 1);
now = DateTime.Now;
Assert.IsTrue(!ArrayExt.EqualsAll(arr1, arr2));
Trace.WriteLine((DateTime.Now - now).TotalMilliseconds);
           

繼續閱讀