简体   繁体   English

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

[英]If Statement True Block Executed When Condition is False

I optimized an extension method to compare two streams for equality (byte-for-byte) - knowing that this is a hot method I tried to optimize it as far as possible (the streams can reach into multi-megabyte lengths). 我优化了一个扩展方法来比较两个流的相等性(逐字节) - 知道这是一个热门的方法,我试图尽可能地优化它(流可以达到数兆字节的长度)。 I essentially came up with the following approach: 我基本上想出了以下方法:

[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;
            }
        }
    }
}

The problem is that the convert1.UInt64[i] != convert2.UInt64[i] if block (returning false ) is being evaluated even when the values are the equal. 问题是即使值相等,也会评估convert1.UInt64[i] != convert2.UInt64[i] if block(返回false )。 I checked each on individually, then checked the outcome of the 'not equals'. 我单独检查每个,然后检查'不等于'的结果。 I am in pure disbelief : 我完全不相信

价值观不相等

I have not messed with the instruction pointer - this is how the code executed and the watch pin is live. 我没有搞乱指令指针 - 这是执行代码和监视引脚的实时方式。

Any ideas how this could happen? 任何想法如何发生这种情况?

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

The debugger in general has a hard time with this union, it can't display the array content when I try it. 调试器通常很难用这个联合,它在我尝试时无法显示数组内容。 But the core problem is no doubt the +1 in the for() end expression. 但核心问题无疑是for()结束表达式中的+1。 That indexes the array beyond its last element when len is divisible by 8. The runtime cannot catch this mistake, overlapping the arrays causes the Length property to have a bogus value. len可被8整除时,将数组索引到其最后一个元素之外。运行时无法捕获此错误,重叠数组会导致Length属性具有伪值。 What happens next is undefined behavior, you are reading bytes that are not part of the array. 接下来发生的是未定义的行为,您正在读取不属于该数组的字节。 A workaround is to make the array 7 bytes longer. 解决方法是使数组长7个字节。

This kind of code is not exactly an optimization, reading and comparing uint64 on a 32-bit machine is expensive, especially when the array isn't aligned correctly.. About 50% odds for that. 这种代码并不完全是一种优化,在32位机器上读取和比较uint64是很昂贵的,特别是当阵列没有正确对齐时。大约50%的可能性。 A better mousetrap is to use the C runtime memcmp() function, available on any Windows machine: 更好的捕鼠器是使用任何Windows机器上可用的C运行时memcmp()函数:

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

And use it like this: 并像这样使用它:

    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;

Do compare the perf of this with a plain for() loop that compares bytes. 比较它的perf与用于比较字节的plain for()循环。 The ultimate throttle here is the memory bus bandwidth. 这里的最终节流是内存总线带宽。

Problems like this are commonly issues with understanding of how optimizations work. 这样的问题通常是理解优化如何工作的问题。 This line of code could very well be being executed because both return false clauses are combined into one set of instructions at the lower level. 这行代码很可能正在被执行,因为返回的错误子句被合并到较低级别的一组指令中。 Other causes for issues like this is if the architecture you are on allows for conditional execution in which certain instructions are hit in the debugger but the results are never committed to registers at the architecture level. 造成这种问题的其他原因是,如果您所使用的体系结构允许条件执行,其中某些指令在调试器中被命中,但结果永远不会提交给体系结构级别的寄存器。

Verify that the code works in debug mode first. 首先验证代码是否在调试模式下工作。 Then when you are convinced the outcome is the same as the release mode, look at the underlying instructions to figure out the compiler optimization at hand. 然后,当您确信结果与发布模式相同时,请查看基础指令以确定手头的编译器优化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM