C++ 在没有分支的情况下比较字符是否相等

C++ 在没有分支的情况下比较字符是否相等,c++,performance,optimization,comparison,branch-prediction,C++,Performance,Optimization,Comparison,Branch Prediction,在一个我想优化此功能的地方: static lvh_distance levenshtein_distance( const std::string & s1, const std::string & s2 ) { const size_t len1 = s1.size(), len2 = s2.size(); std::vector<unsigned int> col( len2+1 ), prevCol( len2+1 ); const

在一个我想优化此功能的地方:

static
lvh_distance levenshtein_distance( const std::string & s1, const std::string & s2 )
{
    const size_t len1 = s1.size(), len2 = s2.size();
    std::vector<unsigned int> col( len2+1 ), prevCol( len2+1 );

    const size_t prevColSize = prevCol.size();
    for( unsigned int i = 0; i < prevColSize; i++ )
        prevCol[i] = i;

    for( unsigned int i = 0, j; i < len1; ++i )
    {
        col[0] = i+1;
        const char s1i = s1[i];
        for( j = 0; j < len2; ++j )
        {
            const auto minPrev = 1 + std::min( col[j], prevCol[1 + j] );
            col[j+1] = std::min( minPrev, prevCol[j] + (  s1i == s2[j] ? 0 : 1   ) );

        }
        col.swap( prevCol );
    }
    return prevCol[len2];
}
静态
lvh_距离levenshtein_距离(常数标准::字符串和s1,常数标准::字符串和s2)
{
常量size_t len1=s1.size(),len2=s2.size();
std::向量列(len2+1),prevCol(len2+1);
常量大小\u t prevColSize=prevCol.size();
for(无符号整数i=0;i
一位用户评论说我可以替换
s1i==s2[j]?0:1
((s1i-s2[j])&0x80)>>7
以防止条件跳转。这个技巧是错误的,用户删除了他的评论,但我想知道是否真的有办法做到这一点。

假设代码

s1i == s2[j] ? 0 : 1
如果您确实希望避免分支操作,您只需尝试以下操作:

!(s1i == s2[j])
这应该会产生相同的效果,并且可能有助于编译器删除分支。或者,你可以颠倒逻辑,写下

s1i != s2[j]

像往常一样,这种类型的优化永远不能保证它会真正达到您希望的结果。优化器做了很多聪明的事情,试图预测他们对您的技巧的反应通常是非常困难的。因此,即使在最好的情况下,您所能做的就是尝试不同的解决方案并比较结果的二进制代码。

为什么不使用以下命令:
!(s1i==s2[j])
(s1i!=s2[j])
,因为布尔到整数的转换是隐式的

不是一个实际的答案,而是为了解决一个难题。
创建一个数组
one\u或zero[UCHAR\u MAX+1]
用1s填充,并且
one\u或zero[0]=0
现在您可以执行类似于
prevCol[j]+one\u或_zero[s1i^s2[j]]


这将导致0在
s1i==s2[j]
上,1被添加到
prevCol[j]

发表评论的用户是否查看编译器的优化输出,以查看您的代码是否真的有条件跳转?@SteveJessop添加了指向其他问题的链接:是,这里显示了程序集输出。小型局部优化是编译器最擅长的。我更担心大小和数组元素的所有额外副本。拥有更多的变量真的能让代码更快吗?@rhalbersma:谢谢你的链接。不幸的是,你不能用向量来做这件事
reserve
不会更改向量的大小,只更改容量,因此init循环将访问超出范围。不过,您可以使用
boost::counting_迭代器或等效工具初始化向量,以避免对向量数据进行两次传递。@SteveJessop为什么不让
for
的第一次
循环一个
reserve
,然后是
prevCol.push_back(i)
?同样,对于
,也可以在循环内使用
推回
,并在之前使用
保留
无符号整数
,而不是
字符
。原理是合理的,但没有理由假设编译器在这里生成的代码与OP所做的不同…@KonradRudolph,谢谢。我误读了这个例子。当然,没有保证,但我以前看到过它有预期的效果。有了这些问题,就没有任何保证了。我会把它加到我的答案中。谢谢你的提示。我用它来改进我的答案,所以你用+1。我得试试。我怀疑它会更快,因为它意味着去引用,可能有缓存丢失,并计算XOR,但这是一种有趣的方法。