我的教授说,人们可以从一个“基”运算符中导出迭代器的“所有”运算符,而不会因为内联而导致速度下降。
他说:
// 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”。在我的直觉中,除了代码重用之外,这是一个更糟糕的实现,因为与“直接”实现“==”运算符相比,可能会发生速度损失。
这不是对或错的问题-我想知道为什么他是正确的
3条答案
按热度按时间ckocjqey1#
您的问题似乎主要是关于将
a==b
实现为!(a>b)&&!(a<b)
。然而,这两个概念略有不同:等式和equivalence。在很多情况下,两者是相同的,但情况并非总是如此。如果对于这个特定的数据类型,相等和等价是相同的,那么
!(a>b)&&!(a<b)
(a>b
实现为b<a
)可能需要对<
运算符进行两次潜在的内联调用。但是,是什么让您如此确定两次调用<
操作符比一次调用==
操作符花费更多的时间呢?这完全取决于数据类型,当然可以创建一个operator==
比operator<
慢得多的类。因此,假设
a
和b
的类型是int
的一个薄 Package (因此操作符尚未定义)。还有两件事要考虑。首先,编译器生成的机器码可能与您编写的代码大相径庭。它可以利用许多优化,除非您查看生成的汇编代码,否则无法准确地判断一段代码的性能。其次,在现代处理器中,使用pipelining和其他复杂的硬件系统,您无法仅从指令数量来预测性能。总之,虽然您可能会说,对于一个简单的数据类型,
a==b
的“直接”实现不会比!(a>b)&&!(a<b)
的实现慢,但您不能真的说它会更快。如果是的话,也很可能只是一点点。xpszyzbs2#
C++实现不必发出与您编写的表达式完全对应的机器指令,只需发出具有相同可观察行为的机器指令。
例如,this显示它们都是由
cmp
实现的,并询问标志ee7vknir3#
这正是编译器资源管理器的用途。
https://godbolt.org/z/9rfxKj1WY
使用
!(>)&&!(<)
实现:使用
==
实现:在gcc 13.1 -O3上得到的组装体:
这两个运算符编译为同一个程序集 *
请注意,这在这里是真的,因为对于整数,“equal”与“equivalent”是相同的-您可以拥有不为真的类型。
(虽然,出于某种原因,他们以相反的顺序使用
rsi
和rdi
?没什么区别,对我来说还是有点奇怪)