按位移位与乘法

Bitwise shift vs Multiplication

我在工作中经常使用按位运算符,写在VB.NET上。 创建System.Byte变量类型时,需要进行类型转换,将字节左移:

Dim a As Byte = &HFF
Dim b As Integer = a * 256
Debug.WriteLine(b.ToString("X")) 
Dim c As Integer = a << 8 'without cast'
Debug.WriteLine(c.ToString("X"))
Dim d As Integer = CInt(a) << 8 'with cast'
Debug.WriteLine(d.ToString("X"))

输出为:

FF00

FF

FF00

使用“*256”允许跳过类型转换,并且代码看起来更短,尤其是在一个表达式中使用少量移位的情况下。但是这样的记录不像shift那样清晰。

所以问题是在相同的情况下最好使用什么类型?乘法是否需要更多处理器时间?

我已将这段 VB 代码加载到反编译器 ILSpy 中,结果显示:

IL_0001: ldc.i4 255
IL_0006: stloc.0

// 255 * 256
IL_0007: ldloc.0
IL_0008: ldc.i4 256
IL_000d: mul.ovf
IL_000e: stloc.1 //equal part starts
IL_000f: ldloca.s 1
IL_0011: ldstr "X"
IL_0016: call instance string [mscorlib]System.Int32::ToString(string)
IL_001b: call void [System]System.Diagnostics.Debug::WriteLine(string)

// 255 << 8
IL_0021: ldloc.0
IL_0022: ldc.i4.8
IL_0023: ldc.i4.7
IL_0024: and
IL_0025: shl
IL_0026: conv.u1
IL_0027: stloc.2 //equal part starts
IL_0028: ldloca.s 2
IL_002a: ldstr "X"
IL_002f: call instance string [mscorlib]System.Int32::ToString(string)
IL_0034: call void [System]System.Diagnostics.Debug::WriteLine(string)

// cint(255) << 8
IL_003a: ldloc.0
IL_003b: ldc.i4.8
IL_003c: shl
IL_003d: stloc.3 //equal part starts
IL_003e: ldloca.s 3
IL_0040: ldstr "X"
IL_0045: call instance string [mscorlib]System.Int32::ToString(string)
IL_004a: call void [System]System.Diagnostics.Debug::WriteLine(string)

如您所见,每条高级指令的最后 5 串 IL 代码是相等的。区别仅在每个部分的顶部。而0xFF*256只包含3个运算符,CInt(0xFF)<<8包含3个运算符,0xFF<<8 包含 6 个运算符,即多了 2 次!没有类型转换的位移位使用位掩码来拒绝可能的溢出,将额外的运算符附加到 IL 代码中。

此外,我在 Francesco Balena "Programming Microsoft Visual Basic 2005: The language" 的书中发现,对于 Long 和 ULong 类型,按位移位速度更快。对于 Integer 和 Short 类型,移位等于乘法。

所以我的结论是:

  • 对于 Long 和 ULong 位移比乘法快;

  • 对于所有其他类型(将类型转换为结果位数)乘法更快或等于按位移位,因此使用第一种或第二种情况只是开发人员的口味。