我优化了一个扩展方法来比较两个流相等(字节到字节) – 知道这是一个热的方法,我尽可能地优化它(流可以达到多兆字节的长度).我基本上想出了以下方法:
[StructLayout(LayoutKind.Explicit)] struct Converter { [FieldOffset(0)] public Byte[] Byte; [FieldOffset(0)] public UInt64[] UInt64; } /// <summary> /// Compares two streams for byte-by-byte equality. /// </summary> /// <param name="target">The target stream.</param> /// <param name="compareTo">The stream to compare the target to.</param> /// <returns>A value indicating whether the two streams are identical.</returns> public static bool CompareBytes(this Stream target,Stream compareTo) { if (target == null && compareTo == null) return true; if (target == null || compareTo == null) return false; if (target.Length != compareTo.Length) return false; if (object.ReferenceEquals(target,compareTo)) return true; if (!target.CanRead || !target.CanSeek) throw new ArgumentOutOfRangeException("target"); if (!compareTo.CanRead || !compareTo.CanSeek) throw new ArgumentOutOfRangeException("target"); lock (target) { lock (compareTo) { var origa = target.Position; var origb = compareTo.Position; try { target.Position = compareTo.Position = 0; // Shrink the number of comparisons. var arr1 = new byte[4096]; var convert1 = new Converter() { Byte = arr1 }; var arr2 = new byte[4096]; var convert2 = new Converter() { Byte = arr2 }; int len; while ((len = target.Read(arr1,4096)) != 0) { if (compareTo.Read(arr2,4096) != len) return false; for (var i = 0; i < (len / 8) + 1; i++) if (convert1.UInt64[i] != convert2.UInt64[i]) return false; } return true; } finally { target.Position = origa; compareTo.Position = origb; } } } }
问题是convert1.UInt64 [i]!= convert2.UInt64 [i]如果正在评估block(返回false),即使值相等.我单独检查了每一个,然后检查“不平等”的结果.我完全不敢相信
我没有弄错指令指针 – 这是代码执行和手表引脚是活的.
任何想法如何发生?
解决方法
for (var i = 0; i < (len / 8) + 1; i++)
调试器通常与这个联合很困难,当我尝试它时不能显示数组内容.但核心问题无疑是for()结束表达式中的1个.当len可被8除以时,将数组索引到其最后一个元素之外.运行时无法捕获此错误,重叠数组会导致Length属性具有伪造值.接下来发生的是未定义的行为,您正在读取不属于数组的字节.解决方法是使数组7字节更长.
这种代码不是一个优化,阅读和比较uint64在32位机器上是昂贵的,特别是当数组不正确对齐时.大约50%的可能性.更好的捕鼠器是使用C运行时memcmp()函数,可在任何Windows机器上使用:
[DllImport("msvcrt.dll")] private static extern int memcmp(byte[] arr1,byte[] arr2,int cnt);
并使用它:
int len; while ((len = target.Read(arr1,4096)) != 0) { if (compareTo.Read(arr2,4096) != len) return false; if (memcmp(arr1,arr2,len) != 0) return false; } return true;
比较这个的perf和一个比较字节的()循环.这里的最终节流是存储器总线带宽.