数学运算比IF要快

问题

虽然很早就知道,CPU在处理 if 这样的判断语句时,使用了预测的技术,所以如果条件总是一个结果,效率就很好。反过来说,如果你使用数学运算避免 if 判断,那么就意味着性能一定比 if 要好。

方案1

今天正好有个函数遇到这个问题,所以我就正好测试以下。

待测试的方法是获取一个int32的数据中,有多少个位是 1,我的方案是将 一个int32拆开成 4个字节,然后一一判断。下面是使用 if 判断的方案 (那个  ? : 三元运算符就是 if 语句)。

 1 static int getInt32TrueCount(int value)
 2 {
 3     if (value == 0)
 4     {
 5         return 0;
 6     }
 7 
 8     return getByteTrueCount(value & 0xFF) +
 9         getByteTrueCount((value >> 8) & 0xFF) +
10         getByteTrueCount((value >> 16) & 0xFF) +
11         getByteTrueCount((value >> 24) & 0xFF);
12 }
13 
14 static int getByteTrueCount(int value)
15 {
16     if (value == 0)
17     {
18         return 0;
19     }
20 
21     int a = (value & 0x1) == 0 ? 0 : 1;
22     int b = (value & 0x2) == 0 ? 0 : 1;
23     int c = (value & 0x4) == 0 ? 0 : 1;
24     int d = (value & 0x8) == 0 ? 0 : 1;
25 
26     int e = (value & 0x10) == 0 ? 0 : 1;
27     int f = (value & 0x20) == 0 ? 0 : 1;
28     int g = (value & 0x40) == 0 ? 0 : 1;
29     int h = (value & 0x80) == 0 ? 0 : 1;
30 
31     return a + b + c + d + e + f + g + h;
32 }

可以看到, 每运算一个位都有一个 if 判断,而要命的是这个 if 判断的结果是不稳定的,随机性极大。我写了一个计时程序,在我计算机中需要 12秒。(i5 6500 Release .net core 2.0 )

 1 static void GetBitCountTest()
 2 {
 3     var wathch = Stopwatch.StartNew();
 4             
 5     var rand = new Random();
 6 
 7     for (int i = 0; i < 10000_0000; i++)
 8     {
 9         int value = rand.Next();
10         int p = getInt32TrueCount(value);
11     }
12 
13     wathch.Stop();
14     Console.WriteLine("GetBitCount 耗时:" + wathch.Elapsed.ToString());
15 
16 }

方案2

第二种方法就是将 if 判断改为数学运算,方法是将 and 运算后的位 移动到 0位,这样就是 0 或 1 了。

 1 static int getInt32TrueCount2(int value) {
 2     if (value == 0) {
 3         return 0;
 4     }
 5 
 6     return getByteTrueCount2(value & 0xFF) +
 7         getByteTrueCount2((value >> 8) & 0xFF) +
 8         getByteTrueCount2((value >> 16) & 0xFF) +
 9         getByteTrueCount2((value >> 24) & 0xFF);
10 }
11 
12 static int getByteTrueCount2(int value) {
13     return (value & 0x1) +
14             ((value & 0x2) >> 1) +
15             ((value & 0x4) >> 2) +
16             ((value & 0x8) >> 3) +
17 
18             ((value & 0x10) >> 4) +
19             ((value & 0x20) >> 5) +
20             ((value & 0x40) >> 6) +
21             ((value & 0x80) >> 7);
22 }

再次运行测试用例,执行时间提高到 2 秒!提高了6倍。

总结:

高性能计算时,避免使用 分支 指令,尽量使用数学运算符。

猜你喜欢

转载自www.cnblogs.com/tansm/p/AvoidTheUseOfIf.html
今日推荐