无分支溢出处理

时间:2016-10-04 20:25:33

标签: c assembly branch-prediction

我正在尝试创建一种安全缓冲区,它可以自动处理溢出而不需要任何分支。缓冲区大小是2的幂,并且只能具有有效的正(即不包括零)索引。它还允许检查删除,如果存储在该索引处的元素等于搜索关键字,则删除给定索引。

我本来就是为了这样的事情

Element *buffer[256];

inline void buffer_insert(size_t index, Element *elem){
  buffer[index < 256 && index] = elem;
}

//Optional: checked insert to prevent overwrite. Will only insert
//if the buffer holds NULL at index.
inline void buffer_checkedInsert(size_t index, Element * elem){
  buffer[index && !buffer[index < 256 && index]] = elem;  
}

inline void buffer_checkedRemove(size_t index, Element *elem){
  buffer[0] = NULL; //Maybe useful if buffer[0] stores elem
  buffer[((elem == buffer[index < 256 && index)) && index] = NULL;
}

因此,当传入的索引超出范围时,我基本上想要访问索引0,因为buffer[0]不是有效的缓冲区索引。而且每当要删除的元素不等于传递给删除的元素时,我也想访问索引0,如果缓冲区包含索引处的内容,我可能还想访问索引0。

我的问题是:

  • 我真的没有分支吗?因为如果C编译器决定在&amp;&amp;上使用短路,代码可能会分支。
  • 如果&amp;&amp;导致分支,是否有一种替代方案在这种情况下具有相同的行为,不涉及分支?
  • 这比基本溢出检查更快吗?或者C编译器能否以某种方式提供if(index < 256) buffer[index] = elem的无分支版本?

1 个答案:

答案 0 :(得分:2)

  

我真的没有分支吗?因为如果C编译器决定在&amp;&amp;上使用短路,代码可能会被分支。

也许。在这些情况下,编译器可能足够聪明地发出无分支机器代码,但你不能依赖它。

  

如果&amp;&amp;导致分支,是否有一种替代方案在这种情况下具有相同的行为,不涉及分支?

你的问题有点困惑。编译器可以发出分支代码来实现&&操作的事实遵循该操作的已定义行为。任何具有相同行为的替代方案都必须提供相同的分支可能性。

另一方面,如果你想询问是否存在在所有情况下计算相同结果的替代方案,那么是的,你可以重写这些表达式而不用分支的可能性。例如,您可以使用&*运算符,如下所示:

buffer[(index < 256) & (index != 0)] = elem;

或者,您可以实现您真正想要的行为:

buffer[(index < 256) * index] = elem;

没有理由认为编译器会为这些计算中的任何一个发出分支指令;如果确实如此,那可能是因为它认为可以提高目标架构的性能。

  

这比基本的溢出检查更快吗?或者C编译器能以某种方式给出if(index&lt; 256)buffer [index] = elem?

的无分支版本

无分支版本肯定可以更快。在非(非)分支执行很多的工作负载上,它们最有可能显着更快,并且没有易于辨别的模式可供选择。但是,如果(非)分支主要遵循规则模式,特别是如果它几乎总是单向,那么CPU的分支预测单元可以进行普通有效性检查,至少与无分支分配一样快。 / p>

最终,如果不对代码在实际数据上的实际性能或其良好的传真进行基准测试,就没有理由担心这一点。结果很可能与数据有关,而且它是否重要取决于程序运行时间在您询问的函数中花费了多少。除非您有其他要求的良好基准,否则您应该编写清晰度和可维护性的代码。