这篇文章主要围绕c–为什么__builtin_prefetch在这里没有任何影响?和为什么c++中用不了cin展开,旨在为您提供一份详细的参考资料。我们将全面介绍c–为什么__builtin_pref
这篇文章主要围绕c – 为什么__builtin_prefetch在这里没有任何影响?和为什么c++中用不了cin展开,旨在为您提供一份详细的参考资料。我们将全面介绍c – 为什么__builtin_prefetch在这里没有任何影响?的优缺点,解答为什么c++中用不了cin的相关问题,同时也会为您带来c – __builtin_unreachable有哪些优化方便?、c – 如何实施“__builtin_va_list”?、C++ 中的 __builtin_expect、C++ 的位运算:__builtin, bitset的实用方法。
本文目录一览:- c – 为什么__builtin_prefetch在这里没有任何影响?(为什么c++中用不了cin)
- c – __builtin_unreachable有哪些优化方便?
- c – 如何实施“__builtin_va_list”?
- C++ 中的 __builtin_expect
- C++ 的位运算:__builtin, bitset
c – 为什么__builtin_prefetch在这里没有任何影响?(为什么c++中用不了cin)
char c; char * ptr; for( size_t i = 0; i < size ; ++i ) { ptr = ( static_cast<char*>(sentenceMap) + i ); c = *ptr; __builtin_prefetch( ptr + i + 1 ); // some treatment on ptr and c }
如您所见,我添加了一条builtin_prefetch指令,希望在我的循环的下一次迭代中放入缓存.我尝试了不同的值:ptr i 1,ptr i 2,ptr i 10但似乎没有任何改变.
为了测量性能,我使用valgrind的工具cachegrind,它给出了缓存未命中数的指示.在行c = * ptr上,当未设置__builtin_prefetch时,cachegrind记录632,378 DLmr(L3缓存未命中).但奇怪的是,无论我设置为__builtin_prefetch的参数如何,此值都不会更改.
对此有何解释?
解决方法
总结
以上是小编为你收集整理的c – 为什么__builtin_prefetch在这里没有任何影响?全部内容。
如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。
c – __builtin_unreachable有哪些优化方便?
If control flow reaches the point of the
__builtin_unreachable
,the program is undefined.
我认为__builtin_unreachable可以用作各种创造性方式的优化器提示.所以我做了一个小实验
void stdswap(int& x,int& y) { std::swap(x,y); } void brswap(int& x,int& y) { if(&x == &y) __builtin_unreachable(); x ^= y; y ^= x; x ^= y; } void rswap(int& __restrict x,int& __restrict y) { x ^= y; y ^= x; x ^= y; }
gets compiled to(g -O2)
stdswap(int&,int&): mov eax,DWORD PTR [rdi] mov edx,DWORD PTR [rsi] mov DWORD PTR [rdi],edx mov DWORD PTR [rsi],eax ret brswap(int&,DWORD PTR [rdi] xor eax,eax xor eax,DWORD PTR [rsi] mov DWORD PTR [rsi],eax xor DWORD PTR [rdi],eax ret rswap(int&,DWORD PTR [rsi] mov edx,DWORD PTR [rdi] mov DWORD PTR [rdi],eax mov DWORD PTR [rsi],edx ret
我假设从优化器的角度来看,stdswap和rswap是最佳的.为什么不将brswap编译成同一个东西?我能用__builtin_unreachable将它编译成同样的东西吗?
解决方法
void exit_if_true(bool x); int foo1(bool x) { if (x) { exit_if_true(true); //__builtin_unreachable(); // we do not enable it here } else { std::puts("reachable"); } return 0; } int foo2(bool x) { if (x) { exit_if_true(true); __builtin_unreachable(); // Now compiler kNows exit_if_true // will not return as we are passing true to it } else { std::puts("reachable"); } return 0; }
生成的代码:
foo1(bool): sub rsp,8 test dil,dil je .L2 ; that jump is going to change as branches will be swapped mov edi,1 call exit_if_true(bool) xor eax,eax ; that tail is going to be removed add rsp,8 ret .L2: mov edi,OFFSET FLAT:.LC0 call puts xor eax,eax add rsp,8 ret foo2(bool): sub rsp,dil jne .L9 mov edi,8 ret .L9: mov edi,1 call exit_if_true(bool)
注意差异:
> xor eax,eax和ret被删除,因为现在编译器知道这是一个死代码.
>编译器交换了分支的顺序:现在首先是分支与puts调用,因此条件跳转可以更快(即使预测,未采用的分支也更快).
这里的假设是以noreturn函数调用或__builtin_unreachable结尾的分支将只执行一次或导致longjmp调用或异常抛出,这两种情况都很少见,并且在优化期间不需要优先处理.
您正在尝试将其用于不同的目的 – 通过提供有关别名的编译器信息(您可以尝试对齐进行相同操作).不幸的是,GCC不理解这种地址检查.
正如您所注意到的那样,添加__restrict__会有所帮助.所以__restrict__适用于别名,__ builtin_unreachable不适用.
请看以下使用__builtin_assume_aligned的示例:
void copy1(int *__restrict__ dst,const int *__restrict__ src) { if (reinterpret_cast<uintptr_t>(dst) % 16 == 0) __builtin_unreachable(); if (reinterpret_cast<uintptr_t>(src) % 16 == 0) __builtin_unreachable(); dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; } void copy2(int *__restrict__ dst,const int *__restrict__ src) { dst = static_cast<int *>(__builtin_assume_aligned(dst,16)); src = static_cast<const int *>(__builtin_assume_aligned(src,16)); dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3]; }
生成的代码:
copy1(int*,int const*): movdqu xmm0,XMMWORD PTR [rsi] movups XMMWORD PTR [rdi],xmm0 ret copy2(int*,int const*): movdqa xmm0,XMMWORD PTR [rsi] movaps XMMWORD PTR [rdi],xmm0 ret
您可以假设编译器可以理解dst%16 == 0表示指针是16字节对齐的,但事实并非如此.因此使用未对齐的存储和加载,而第二个版本生成更快的指令,需要对齐地址.
c – 如何实施“__builtin_va_list”?
那么,我真的好奇__builtin_va_list是如何实现的?我知道它是特定于编译器的.我在哪里可以找到__builtin_va_list的定义?我应该下载clang编译器的源代码吗?
解决方法
So,I am really curIoUs about how the __builtin_va_list is implemented?
__builtin_va_list在GCC编译器(或Clang/LLVM编译器)中实现.因此,您应该研究GCC编译器源代码以了解详细信息.
请看gcc/builtins.def& gcc/builtins.c更多.
我不熟悉Clang,它实现了相同的内置.
但是GCC和GCC都是Clang是开源或免费软件.它们是复杂的野兽(每行数百万行代码),因此您可能需要多年的工作才能理解它们.
请注意,编译器的ABI很重要.有关详细信息,请查看X86 psABI中的示例.
BTW,Grady Player评论说:
Pops the correct number of bytes off of the stack for each of those tokens…
不幸的是,今天它要复杂得多.在当前的处理器和ABI上,calling conventions确实使用processor registers来传递一些参数(而且细节中存在邪恶).
Should I download the source code of clang compiler?
是的,您还需要分配几年的工作来了解细节.
几年前,我确实编写了一些教程幻灯片和关于GCC实现的外部文档的链接,请参阅我的GCC MELT documentation页面(有点烂).
C++ 中的 __builtin_expect
这个指令是 gcc 引入的,作用是允许程序员将最有可能执行的分支告诉编译器。这个指令的写法为:
__builtin_expect(EXP, N)
。
意思是:EXP==N 的概率很大。
一般的使用方法是将__builtin_expect
指令封装为 likely
和 unlikely
宏。这两个宏的写法如下.
#define likely(x) __builtin_expect(!!(x), 1) //x很可能为真
#define unlikely(x) __builtin_expect(!!(x), 0) //x很可能为假
内核中的 likely () 与 unlikely ()
首先要明确:
if(likely(value)) //等价于 if(value)
if(unlikely(value)) //也等价于 if(value)
__builtin_expect()
是 GCC (version>= 2.96)提供给程序员使用的,目的是将 “分支转移” 的信息提供给编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。
__builtin_expect((x),1)
表示 x 的值为真的可能性更大;
__builtin_expect((x),0)
表示 x 的值为假的可能性更大。
也就是说,使用 likely()
,执行 if 后面的语句的机会更大,使用 unlikely()
,执行 else 后面的语句的机会更大。通过这种方式,编译器在编译过程中,会将可能性更大的代码紧跟着起面的代码,从而减少指令跳转带来的性能上的下降。
例子
int x, y;
if(unlikely(x > 0))
y = 1;
else
y = -1;
上面的代码中 gcc 编译的指令会预先读取 y = -1 这条指令,这适合 x 的值大于 0 的概率比较小的情况。如果 x 的值在大部分情况下是大于 0 的,就应该用 likely (x> 0),这样编译出的指令是预先读取 y = 1 这条指令了。这样系统在运行时就会减少重新取指了。
C++ 的位运算:__builtin, bitset
int __builtin_ffs (unsigned int x)
返回二进制表示中x
的最后一位 $1$(最右边的)是从后向前第几位,比如 $7368(1110011001000)$ 返回 $4$ 。int __builtin_clz (unsigned int x)
返回二进制表示中前导 $0$ 的个数。int __builtin_ctz (unsigned int x)
返回二进制表示中末尾 $0$ 的个数。int __builtin_popcount (unsigned int x)
返回二进制表示中 $1$ 的个数。int __builtin_parity (unsigned int x)
返回x
的奇偶校验位,也就是x
的 $1$ 的个数模 $2$ 的结果。
这些函数都有相应的 unsigned long
和 unsigned long long
版本,只需在函数名后面加上 l
或 ll
即可,如 int __builtin_clzll
bitset 定义在 <bitset>
库中。
bitset<8> b1; // [0,0,0,0,0,0,0,0]
bitset<8> b2(42); // [0,0,1,0,1,0,1,0]
bitset<17> bs(0xfff0); // [1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0]
string bit_string = "110010";
bitset<8> b3(bit_string); // [0,0,1,1,0,0,1,0]
bitset<8> b4("110010"); // [0,0,1,1,0,0,1,0]
cout << b4[0] << b4[1] << b4[2] << b4[3] << b4[4] << endl; // 0,1,0,0,1
cout << b3 << endl; // 00110010 (1. 不是 50! 2. 会输出前导 0! )
b4[0] = true; // b4 = [0,0,1,1,0,0,1,1]
b4.set(3); // [0,0,1,1,1,0,1,1]
b4.set(5); // [0,0,1,1,1,0,1,1] , 并没有变化
b4.unset(4); // [0,0,1,0,1,0,1,1]
b4.flip(1); // [0,0,1,0,1,0,0,1]
b4.flip(1); // [0,0,1,0,1,0,1,1]
b4.flip(2); // [0,0,1,0,1,1,1,1]
b4.flip(0); // [0,0,1,0,1,1,1,0]
cout << b2.size() << '','' << bs.size << endl;// 8,17
cout << b2.count() << endl;// 3
cout << bs.count() << endl;// 13
string
另外,bitset 支持类似一个整数的操作,可以比较相等还是不等(但不能比较谁大谁小),可以左移右移,可以按位取与或非异或。但是一个 bitset 不能和一个真正的整数(如 int 型整数)进行这些操作。
今天关于c – 为什么__builtin_prefetch在这里没有任何影响?和为什么c++中用不了cin的讲解已经结束,谢谢您的阅读,如果想了解更多关于c – __builtin_unreachable有哪些优化方便?、c – 如何实施“__builtin_va_list”?、C++ 中的 __builtin_expect、C++ 的位运算:__builtin, bitset的相关知识,请在本站搜索。
本文标签: