【发布时间】:2017-02-13 03:25:14
【问题描述】:
我正在尝试创建一种安全缓冲区,它可以自动处理溢出而无需任何分支。缓冲区大小是 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 编译器决定对 && 使用短路,代码可能会分支。
- 如果 && 导致分支,在这种情况下是否有替代方案具有相同行为但不涉及分支?
- 这能比基本的溢出检查更快吗?或者 C 编译器能否以某种方式提供
if(index < 256) buffer[index] = elem的无分支版本?
【问题讨论】:
-
&&设计为短路。它的使用通常会发出一个分支。使用比较运算符的结果作为值也可能导致发出分支,具体取决于架构(在 x86 上不会)。 -
作为一个概念性问题:想想是否让越界读取和写入静默不做任何事情而不是崩溃,这是否真的更好。还要考虑无分支代码是否真的值得额外的长度。几乎从不进行的跳转非常便宜,我认为您不会偶尔触发溢出检查。
-
&&不会像您认为的那样做。例如&&的结果只能是0或1。 -
你想要
(index < 256) * index -
@Navneeth:答案完全取决于架构、全局数组是否为
const、编译器实现、代码的热度(确定数组是否保存在缓存行中)等. 除了实际测试之外,没有办法知道。奇怪的是,乘法不会变慢;整数乘法在现代芯片上速度很快,只要您没有在附近执行其他乘法运算,乘法将在(摊销的)1-2 个周期内完成,这与按位与(通常可以分派具有更多并行性,但负载可能会花费您)。
标签: c assembly branch-prediction