c++ 从“基本”运算符派生运算符-速度损失

wvyml7n5  于 2023-05-13  发布在  其他
关注(0)|答案(3)|浏览(169)

我的教授说,人们可以从一个“基”运算符中导出迭代器的“所有”运算符,而不会因为内联而导致速度下降。
他说:

// To implement ==, != , >, <, >=, <= only only a few cases based on attrbiutes are required
// a == b       ==      !(a>b)&&!(a<b)
// a != b       ==      !(a==b)
// a < b        ==      Implemented as "actual" operator
// a > b        ==      b < a //Argument swap!
// a >= b       ==      a > b || a == b
// a <= b       ==      a < b || a == b

与以下相比,不会引入速度损失:

// To implement ==, != , >, <, >=, <= only only a few cases based on attrbiutes are required
// a == b       ==      Implemented as "actual" operator
// a != b       ==      !(a==b)
// a < b        ==      Implemented as "actual" operator
// a > b        ==      Implemented as "actual" operator
// a >= b       ==      !(a < b )
// a <= b       ==      !(a > b )

我明白了,“内联”避免了函数调用以及指令和内存中的相关开销。
但是,我有点确定-这是一个问题-评估一个动态逻辑语句a la a && b && c(例如)不能被优化,首先a,然后b,然后c将被评估(评估停止,只要一个语句为假)。
因此,!(a>b)&&!(a<b)需要在最坏的情况下调用两次“<-code”。在我的直觉中,除了代码重用之外,这是一个更糟糕的实现,因为与“直接”实现“==”运算符相比,可能会发生速度损失。

这不是对或错的问题-我想知道为什么他是正确的

ckocjqey

ckocjqey1#

您的问题似乎主要是关于将a==b实现为!(a>b)&&!(a<b)。然而,这两个概念略有不同:等式和equivalence。在很多情况下,两者是相同的,但情况并非总是如此。
如果对于这个特定的数据类型,相等和等价是相同的,那么!(a>b)&&!(a<b)a>b实现为b<a)可能需要对<运算符进行两次潜在的内联调用。但是,是什么让您如此确定两次调用<操作符比一次调用==操作符花费更多的时间呢?这完全取决于数据类型,当然可以创建一个operator==operator<慢得多的类。
因此,假设ab的类型是int的一个薄 Package (因此操作符尚未定义)。还有两件事要考虑。首先,编译器生成的机器码可能与您编写的代码大相径庭。它可以利用许多优化,除非您查看生成的汇编代码,否则无法准确地判断一段代码的性能。其次,在现代处理器中,使用pipelining和其他复杂的硬件系统,您无法仅从指令数量来预测性能。
总之,虽然您可能会说,对于一个简单的数据类型,a==b的“直接”实现不会比!(a>b)&&!(a<b)的实现慢,但您不能真的说它会更快。如果是的话,也很可能只是一点点。

xpszyzbs

xpszyzbs2#

C++实现不必发出与您编写的表达式完全对应的机器指令,只需发出具有相同可观察行为的机器指令。
例如,this显示它们都是由cmp实现的,并询问标志

ee7vknir

ee7vknir3#

这正是编译器资源管理器的用途。
https://godbolt.org/z/9rfxKj1WY
使用!(>)&&!(<)实现:

struct Int1
{
    int i;
};

bool operator<(Int1 const & lhs, Int1 const & rhs)
{
    return lhs.i < rhs.i;
}

bool operator>(Int1 const & lhs, Int1 const & rhs)
{
    return lhs.i > rhs.i;
}

bool operator==(Int1 const & lhs, Int1 const & rhs)
{
    return !(lhs>rhs) && !(lhs<rhs);
}

使用==实现:

struct Int2
{
    int i;
};

bool operator==(Int2 const & lhs, Int2 const & rhs)
{
    return lhs.i == rhs.i;
}

在gcc 13.1 -O3上得到的组装体:

operator==(Int1 const&, Int1 const&):
    mov     eax, DWORD PTR [rdi]
    cmp     DWORD PTR [rsi], eax
    sete    al
    ret
operator==(Int2 const&, Int2 const&):
    mov     eax, DWORD PTR [rsi]
    cmp     DWORD PTR [rdi], eax
    sete    al
    ret

这两个运算符编译为同一个程序集 *
请注意,这在这里是真的,因为对于整数,“equal”与“equivalent”是相同的-您可以拥有不为真的类型。
(虽然,出于某种原因,他们以相反的顺序使用rsirdi?没什么区别,对我来说还是有点奇怪)

相关问题