命运最迷人的地方从来都不是你生下来就有什么,而是你改变的那一部分~

__builtin_ 系列函数是 GCC 提供的一些内置函数,旨在优化位运算和其他低级操作。这些函数通常直接映射到底层硬件指令,使得某些操作更为高效。以下是一些常用的位运算相关的 __builtin_ 函数及其示例:

  1. __builtin_popcount / __builtin_popcountl / __builtin_popcountll
  • 功能: 计算整数的二进制表示中 1 的个数(即汉明重量)。

  • 示例: 假设我们需要统计一个整数的二进制表示中有多少个 1

    1
    2
    3
    4
    5
    6
    7
    8
    #include <iostream>

    int main() {
    unsigned int x = 0b110110; // 二进制表示:110110
    int count = __builtin_popcount(x);
    std::cout << "Number of 1s in " << x << " is: " << count << std::endl;
    return 0;
    }

    输出:

    1
    Number of 1s in 54 is: 4

    解释: 110110 中有四个 1

  1. __builtin_ctz / __builtin_ctzl / __builtin_ctzll
  • 功能: 计算整数二进制表示下从右往左数连续 0 的个数(Count Trailing Zeros)。

  • 示例: 找出一个整数尾部连续 0 的数量。

    1
    2
    3
    4
    5
    6
    7
    8
    #include <iostream>

    int main() {
    unsigned int x = 0b1001000; // 二进制表示:1001000
    int trailing_zeros = __builtin_ctz(x);
    std::cout << "Number of trailing zeros in " << x << " is: " << trailing_zeros << std::endl;
    return 0;
    }

    输出:

    1
    2

    Number of trailing zeros in 72 is: 3

    解释: 1001000 尾部有三个 0

  1. __builtin_clz / __builtin_clzl / __builtin_clzll
  • 功能: 计算整数二进制表示下从左往右数的前导 0 的个数(Count Leading Zeros)。

  • 示例: 找出一个整数前导连续 0 的数量。

    1
    2
    3
    4
    5
    6
    7
    8
    #include <iostream>

    int main() {
    unsigned int x = 0b00001000; // 二进制表示:00001000
    int leading_zeros = __builtin_clz(x);
    std::cout << "Number of leading zeros in " << x << " is: " << leading_zeros << std::endl;
    return 0;
    }

    输出:

    1
    2

    Number of leading zeros in 8 is: 28

    解释: 对于 32 位整数,00001000 前导有 28 个 0

  1. __builtin_parity / __builtin_parityl / __builtin_parityll
  • 功能: 计算整数二进制表示中 1 的个数的奇偶性。如果 1 的个数是奇数,返回 1;否则返回 0

  • 示例: 检查整数的奇偶性。

    1
    2
    3
    4
    5
    6
    7
    8
    #include <iostream>

    int main() {
    unsigned int x = 0b1011; // 二进制表示:1011
    int parity = __builtin_parity(x);
    std::cout << "Parity of " << x << " is: " << parity << std::endl;
    return 0;
    }

    输出:

    1
    2

    Parity of 11 is: 1

    解释: 1011 中有三个 1,为奇数,故返回 1

  1. __builtin_bswap16 / __builtin_bswap32 / __builtin_bswap64
  • 功能: 交换字节顺序,用于大小端转换。

  • 示例: 将 32 位整数的字节顺序进行交换。

    1
    2
    3
    4
    5
    6
    7
    8
    #include <iostream>

    int main() {
    uint32_t x = 0x12345678;
    uint32_t swapped = __builtin_bswap32(x);
    std::cout << "Original: 0x" << std::hex << x << ", Swapped: 0x" << swapped << std::endl;
    return 0;
    }

    输出:c

    1
    2

    Original: 0x12345678, Swapped: 0x78563412

    解释: 0x12345678 经过字节顺序交换变为 0x78563412

  1. __builtin_ffs / __builtin_ffsl / __builtin_ffsll
  • 功能: 找到整数中最低位 1 的位置,返回从 1 开始的位置索引,如果没有找到则返回 0

  • 示例: 找到整数中最低位 1 的位置。

    1
    2
    3
    4
    5
    6
    7
    8
    #include <iostream>

    int main() {
    int x = 0b101000; // 二进制表示:101000
    int position = __builtin_ffs(x);
    std::cout << "First set bit in " << x << " is at position: " << position << std::endl;
    return 0;
    }

    输出:

    1
    2

    First set bit in 40 is at position: 4

    解释: 101000 中最低位的 1 在第 4 位。

    7.__builtin_frame_address

使用了 __builtin_frame_address(0) 来获取当前栈帧的栈指针地址。不同的编译器可能有不同的方式获取栈指针地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <thread>

void threadFunction(int threadID) {
int localVariable = threadID;

// 获取当前栈指针的地址
void* stackPointer = __builtin_frame_address(0);

std::cout << "Thread ID: " << threadID
<< ", Local Variable Address: " << &localVariable
<< ", Stack Pointer Address: " << stackPointer
<< std::endl;
}

int main() {
std::thread t1(threadFunction, 1);
std::thread t2(threadFunction, 2);

t1.join();
t2.join();

return 0;
}

栈指针与栈区的关系

  • 栈指针(Stack Pointer, SP)是一个寄存器,它持有当前栈顶的地址。每个线程都有自己的栈指针,这个指针在函数调用、局部变量分配等操作时不断变化。
  • 栈区是线程独立的内存区域,用于存储该线程的局部变量、函数调用信息等。栈指针指向当前线程栈区的顶部。

8.__builtin_expect

分支预测优化,这是现代处理器用来提高程序执行效率的一种技术。

在现代处理器中,当遇到一个分支(如 if-else)时,处理器会尝试“猜测”哪个分支更有可能被执行,并提前加载该分支的指令以减少等待时间。这种“猜测”被称为分支预测

  • 预测正确:处理器可以直接执行已加载的指令,提升效率。
  • 预测错误:处理器需要丢弃已经加载的指令,重新加载正确分支的指令,这会导致性能损失。

__builtin_expect 的作用

__builtin_expect 是 GCC 和 Clang 编译器提供的一个内建函数,用来显式告诉编译器某个条件更可能为真或假,从而帮助编译器优化分支预测。

  • __builtin_expect(!!(x), 1):表示 x 更可能为 true
  • __builtin_expect(!!(x), 0):表示 x 更可能为 false

在你的代码中:

1
2
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
  • likely(x):表示 x 更可能为真。
  • unlikely(x):表示 x 更可能为假。
1
2
3
4
5
6
7
8
9
bool stealTask(UTaskWrapperRef task) {
if (unlikely(pool_threads_->size() < CGRAPH_DEFAULT_THREAD_SIZE)) {
// 线程池还未初始化完毕的时候⽆法进⾏steal。确保程序安全运⾏
return false;
}

// do something
return true;
}

在这个例子中,unlikely(pool_threads_->size() < CGRAPH_DEFAULT_THREAD_SIZE) 表示线程池未初始化完毕的情况是极少发生的。

  • 目的:告诉编译器,这个 if 条件极少会成立(即 false 的概率非常高),因此编译器可以优化为更倾向于执行 else 分支(即 return true; 的部分)。
  • 好处:编译器可以根据这个提示,优化指令的预加载顺序,以更好地适应实际运行时的情况,减少分支预测错误带来的开销。

4. !!(x) 的作用

!!(x) 的作用是将 x 转换为布尔值:

  • 第一个 !x 转换为 falsetrue 的相反值。
  • 第二个 ! 再次取反,将其转换回 truefalse

所以 !!(x) 就是为了确保 x 被转换为 bool 类型。这在一些情况下很有用,因为你希望传递给 __builtin_expect 的是真正的布尔值,而不是某个可以隐式转换为 bool 的值。

总结

__builtin_ 系列函数是 C++ 中非常高效的位运算工具,能显著提高特定操作的性能。通过这些示例,我们可以看到它们在各种实际应用场景中的效果