如果条件为假则执行语句True Block

时间:2012-01-20 12:14:47

标签: c# logic conditional

我优化了一个扩展方法来比较两个流是否相等(逐字节) - 知道这是一个热门方法,我试图尽可能地优化它(流可以达到数兆字节的长度)。我基本上想出了以下方法:

[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, 0, 4096)) != 0)
                {
                    if (compareTo.Read(arr2, 0, 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] if块(返回false)。我单独检查每个,然后检查'不等于'的结果。 我纯属怀疑

Values are not equal

我没有弄乱指令指针 - 这是执行代码和监视引脚的实时方式。

任何想法如何发生这种情况?

2 个答案:

答案 0 :(得分:11)

  for (var i = 0; i < (len / 8) + 1; i++)

调试器通常很难使用此联合,但在尝试时它无法显示数组内容。但核心问题无疑是for()结束表达式中的+1。当 len 可被8整除时,将数组索引到其最后一个元素之外。运行时无法捕获此错误,重叠数组会导致Length属性具有伪值。接下来发生的是未定义的行为,您正在读取不属于该数组的字节。解决方法是使数组长7个字节。

这种代码并不完全是一种优化,在32位机器上读取和比较uint64是很昂贵的,特别是当阵列没有正确对齐时。大约50%的可能性。更好的捕鼠器是使用任何Windows机器上提供的C运行时memcmp()函数:

    [DllImport("msvcrt.dll")]
    private static extern int memcmp(byte[] arr1, byte[] arr2, int cnt);

并像这样使用它:

    int len;
    while ((len = target.Read(arr1, 0, 4096)) != 0) {
        if (compareTo.Read(arr2, 0, 4096) != len) return false;
        if (memcmp(arr1, arr2, len) != 0) return false;
    }
    return true;

将它的perf与比较字节的plain for()循环进行比较。这里的最终节流是内存总线带宽。

答案 1 :(得分:1)

这样的问题通常是理解优化如何工作的问题。这行代码很可能正在被执行,因为返回的错误子句被合并到较低级别的一组指令中。造成这种问题的其他原因是,如果您所使用的体系结构允许条件执行,其中某些指令在调试器中被命中,但结果永远不会提交给体系结构级别的寄存器。

首先验证代码是否在调试模式下工作。然后,当您确信结果与发布模式相同时,请查看基础指令以了解手头的编译器优化。