hpc优化for循环分支预测2

上一篇中提到分支预测的优化方法,主要是通过预先排序分支条件的方式提高分支预测命中率的。除此外,还有替代和消除ifelse分支的优化思路可以采用。

比如有如下的for循环中存在分支判断

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
std::vector<int> a(test_size);
std::vector<int> b(test_size);
std::vector<int> c(test_size);
for(int i = 0; i < test_size; ++i)
{
    if(a[i] > b[i])
    {
        c[i] = a[i] - b[i];
    }else
    {
        c[i] = b[i] - a[i];
    }
}

三目运算符替代if-else

编程语言除了ifelse的分支判断写法外,还有三目运算符可以完成分支判断的写法,基于三目运算的分支跳转在一些编译器优化下往往会比ifelse的效率有所提高,将上述例子改为三目运算符为:

1
2
3
4
5
//使用三目运算符替代if-else
for(int i = 0; i < test_size; ++i)
{
    c[i] = a[i] > b[i] ? a[i] - b[i] : b[i] - a[i];
}

调整程序逻辑消除if-else

三目运算符本质上还是要有分支跳转,其实很多程序在保证结果不变的情况下,有时候是可以通过调整程序实现逻辑从而避免使用分支跳转的。比如以上例子,不难看出实际上就是计算a与b的差值的绝对值而已,那么程序就可以改由为以下不包含分支跳转的。

1
2
3
4
 for(int i = 0; i < test_size; ++i)
{
    c[i] = std::abs(a[i] - b[i]);
}

测试程序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/**
 * @file loop_for01.cpp
 * @author mango ([email protected])
 * @brief 高性能优化-分支预测-for循环
 * @version 0.1
 * @date 2022-05-19
 * 
 * @copyright Copyright (c) 2022
 * 
 */

 #include <vector>
 #include <random>
 #include <algorithm>
 #include <chrono>
 #include <iostream>


 int main(int argc, char** argv)
 {
     std::random_device rd;
     std::mt19937 gen(rd());
     std::uniform_int_distribution<int> dist(0, 255);
    
     for(int k = 2; k <= 7; ++k)
     {
        int test_size = std::pow(10, k);
        std::cout << "-------------------------test size :" << test_size << " -----------------------" << std::endl;
        std::vector<int> a(test_size);
        std::vector<int> b(test_size);
        std::vector<int> c(test_size);

        for(int i = 0; i < test_size; ++i)
        {
            a[i] = dist(gen);
            b[i] = dist(gen);
        }

        auto t0 = std::chrono::system_clock::now();
        //原始条件判断运算
        for(int i = 0; i < test_size; ++i)
        {
            if(a[i] > b[i])
            {
                c[i] = a[i] - b[i];
            }else
            {
                c[i] = b[i] - a[i];
            }
        }
        auto t1 = std::chrono::system_clock::now();
        //使用三目运算符替代if-else
        for(int i = 0; i < test_size; ++i)
        {
            c[i] = a[i] > b[i] ? a[i] - b[i] : b[i] - a[i];
        }

        auto t2 = std::chrono::system_clock::now();
        //改变程序逻辑完成if-else替代
        for(int i = 0; i < test_size; ++i)
        {
            c[i] = std::abs(a[i] - b[i]);
        }
        auto t3 = std::chrono::system_clock::now();
        
        auto dt1 = std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0).count();
        auto dt2 = std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count();
        auto dt3 = std::chrono::duration_cast<std::chrono::microseconds>(t3 - t2).count();

        std::cout << "dt1 = "<< dt1 << " us"<< std::endl;
        std::cout << "dt1 = "<< dt2 << " us"<< std::endl;
        std::cout << "dt3 = "<< dt3 << " us"<< std::endl;
     }
   

     return 0;
 }

在releae模式下运行结果如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-------------------------test size :100 -----------------------
dt1 = 0 us
dt1 = 0 us
dt3 = 0 us
-------------------------test size :1000 -----------------------
dt1 = 0 us
dt1 = 0 us
dt3 = 0 us
-------------------------test size :10000 -----------------------
dt1 = 4 us
dt1 = 3 us
dt3 = 3 us
-------------------------test size :100000 -----------------------
dt1 = 103 us
dt1 = 83 us
dt3 = 102 us
-------------------------test size :1000000 -----------------------
dt1 = 1015 us
dt1 = 910 us
dt3 = 999 us
-------------------------test size :10000000 -----------------------
dt1 = 9203 us
dt1 = 9156 us
dt3 = 9091 us

可以看到相比原始使用ifelse的方法效率有小幅提升


微信公众号