当条件为假时执行 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 块的主要内容,如果未能解决你的问题,请参考以下文章