考虑以下实现小字符串优化(基本上是为了MCVE的类)的类(假设有小端字节序,64位指针等):
class String {
char* data_;
bool sso() const { return reinterpret_cast<uintptr_t>(data_) & 1; }
public:
String(const char * arg = "") {
auto len = strlen(arg);
if (len > 6) {
data_ = new char[len + 1];
memcpy(data_, arg, len + 1);
}
else {
data_ = reinterpret_cast<char*>((uintptr_t)1);
memcpy(reinterpret_cast<char*>(&data_) + 1, arg, len + 1);
}
}
~String() { if (sso() == false) delete data_; }
// ~String() { if (reinterpret_cast<uintptr_t>(data_) & 1 == 0) delete data_; }
};
请注意,有两个版本的析构函数。当我使用Quick C ++ Benchmark测量这两个版本之间的差异时:
static void CreateShort(benchmark::State& state) {
for (auto _ : state) {
String s("hello");
benchmark::DoNotOptimize(s);
}
}
我快5.7倍与GCC第二种情况下的运行时间。我不明白为什么编译器在这里不能生成相同的优化程序集。在按位AND运算的结果另外转换为bool的情况下,是什么阻碍了编译器的优化呢?(尽管我不是汇编专家,但我可以看到两种变体在汇编输出中的一些差异,但无法弄清楚为什么会有这种差异。)
使用Clang,没有区别,并且两种变体都很快。
问题在于转换为bool
,而不是内联。以下形式的析构函数会导致相同的问题:
~String() { if ((bool)(reinterpret_cast<uintptr_t>(data_) & 1) == false) delete data_; }
对于此代码:
if (reinterpret_cast<uintptr_t>(data_) & 1 == 0) delete data_;
可以完全优化它:1 == 0
始终为0
,并且x & 0
对所有人始终为x
。第一种情况比较慢,因为它实际上在做某事。
我想你的意思是:
if ( (reinterpret_cast<uintptr_t>(data_) & 1) == 0) delete data_;
我优先使用的助记符&
|
是回想一下,在C的前体中,没有单独的运算符&
和&&
;。该&
运营商满足这两个角色(你手动转换为布尔值范围内,如果你想要一个合乎逻辑的比较)。x == y & z == w
检查这两个等式是否成立的普通代码也是如此。
在&&
引入时,为了避免破坏现有代码,&&
给它的优先级比&
;低。但&
保持不变==
。
C ++语言也没有改变这些优先顺序,大概是为了最大程度地减小两种语言之间的不兼容性。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句