assembly 如何进行无分支边界检查?

jmp7cifd  于 5个月前  发布在  其他
关注(0)|答案(1)|浏览(52)

我正在尝试创建一种安全的缓冲区,它可以自动处理溢出而不需要任何分支。缓冲区的大小是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一个无分支的版本吗?
9ceoxa92

9ceoxa921#

因为如果C编译器决定在&&上使用短路,代码可能会分支。
也许吧。编译器可能足够聪明,在这些情况下发出无分支的机器码,但你不能依赖它。
如果&&导致了分支,那么在这种情况下,是否有一个替代方案具有相同的行为,但不涉及分支?
你的问题有点混乱。事实上,编译器可以发出分支代码来实现&&操作,这是根据该操作的定义行为。任何具有相同行为的替代方案都必须提供相同的分支可能性。
另一方面,如果你想问是否有一个替代方案,在所有情况下 * 计算相同的结果 *,那么是的,你可以重写这些表达式来这样做,而不可能分支。例如,你可以使用&*运算符,如下所示:

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

字符串
或者,你可以实现你实际想要的行为:

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


没有理由认为编译器会为这两种计算发出一条分支指令;如果它发出了,那可能是因为它认为这会提高目标体系结构的性能。
这能比基本的溢出检查更快吗?或者C编译器能以某种方式给予if(index < 256)buffer[index] = elem的无分支版本吗?
无分支版本当然可以更快。在(非)分支执行次数很多的工作负载上,它们最有可能明显更快,并且没有容易识别的模式来选择替代方案。但是如果(非)分支大多遵循规则模式,特别是如果它几乎总是单向的,则CPU的分支预测单元可以至少与无分支分配一样快地进行普通有效性检查。
最后,如果没有基于真实的数据或其良好的复制品对代码的实际性能进行基准测试,就没有理由担心这一点。结果很可能是数据依赖的,它是否重要取决于程序的运行时间有多少花在了你所要求的函数上。除非你有一个好的基准测试,否则你应该为了清晰性和可维护性而编写代码。

相关问题