当条件为假时执行 If 语句 True 块

Posted

技术标签:

【中文标题】当条件为假时执行 If 语句 True 块【英文标题】:If Statement True Block Executed When Condition is False 【发布时间】:2012-02-15 00:19:05 【问题描述】:

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

[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)正在被评估,即使值相等。我分别检查了每个,然后检查了“不等于”的结果。 我完全不相信

我没有弄乱指令指针——这就是代码的执行方式和监视引脚的运行方式。

有什么想法会发生这种情况吗?

【问题讨论】:

看起来像是在进行参考比较(不同的对象,始终为假)而不是值比较 我很困惑,两个struct属性的FieldOffset都为0,你怎么知道你在比较苹果和苹果? @mtijn 见this thread。数组将占用相同的内存空间 - 因此写入其中一个将更新另一个。 【参考方案1】:
  for (var i = 0; i < (len / 8) + 1; i++)

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

这种代码并不完全是一种优化,在 32 位机器上读取和比较 uint64 的成本很高,尤其是当数组没有正确对齐时。大约有 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, 0, 4096)) != 0) 
        if (compareTo.Read(arr2, 0, 4096) != len) return false;
        if (memcmp(arr1, arr2, len) != 0) return false;
    
    return true;

将 this 的性能与比较字节的普通 for() 循环进行比较。这里的最终限制是内存总线带宽。

【讨论】:

谢谢汉斯。我想过在Max 电话中加入+1。您已经回答了我的问题,并且超越并为我提供了更好的优化。当之无愧的滴答声!【参考方案2】:

此类问题通常是对优化工作原理的理解问题。这行代码很可能会被执行,因为两个 return false 子句都在较低级别组合成一组指令。导致此类问题的其他原因是,如果您所在的体系结构允许条件执行,其中某些指令在调试器中被命中,但结果永远不会提交到体系结构级别的寄存器。

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

【讨论】:

代码目前正在调试中——即便如此,我认为 JITter 不会以会破坏它的方式优化它。我会检查拆解,看看幕后发生了什么。

以上是关于当条件为假时执行 If 语句 True 块的主要内容,如果未能解决你的问题,请参考以下文章

当条件为假时不执行其他函数的`if`语句

Python条件语句

4. 流程控制语句

第六章 控制流程

流程控制——判断,选择,循环。

JavaScript条件语句