【问题标题】:Why isn't string assignment optimised when the length is known to the compiler?当编译器知道长度时,为什么不优化字符串赋值?
【发布时间】:2011-01-11 12:54:57
【问题描述】:

我今天正在玩一些计时代码,发现当将字符串文字分配给 std::string 时,它的速度提高了大约 10%(使用较短的 12 字符字符串,因此对于大字符串可能会有更大的差异)使用已知长度的文字(使用 sizeof 运算符)而不是这样做。 (只用VC9编译器测试过,所以我猜其他编译器可能会更好)。

std::string a("Hello World!");
std::string b("Hello World!", sizeof("Hello World!");//10% faster in my tests

现在我怀疑的原因是它必须调用 strlen(VC9 进入汇编程序,这不是我的强项,所以我不能 100% 确定)来获取字符串长度,然后与第二种情况相同无论如何。

考虑到 std::string 已经存在了多长时间,以及第一种情况在现实世界的程序中有多常见(特别是如果您包含 +、=、+= 等运算符和等效方法),它为什么没有优化第一个案例变成第二个?如果它是一个 std::basic_string 对象和一个字面量,就好像它像 b 一样编写,这似乎也很简单?

【问题讨论】:

  • 你是如何测试它的?启用优化?确定你的测试没有缺陷?
  • 测试关于更长字符串的假设听起来有缺陷。准备好被 Neil Butterworth 敲打。
  • 它进行了全面优化,包括启用链接时间生成和内在功能。在这两种情况下,它都是完全相同的一段代码,我只是将字符串从 a 初始化为 b 的方式进行了更改,然后重新编译。我运行了几次,所以我认为任何其他因素都不太可能对其产生如此大的影响。
  • 嗯,您需要将执行时间提高到可以可靠测量的值的循环在哪里?如前所述,代码执行将由 CPU 缓存效应主导。
  • 在每种情况下都在约 30 秒的时间内进行了测量,尽管来自测试的确切数字并不是真正重要的 IMO。检查组件将是显示它没有优化它的更好方法,但正如我所说,这不是我的强项,所以我永远无法找到反汇编的相关位,并且证明的机会更少它在做什么或不做什么。

标签: c++ visual-c++ string compiler-optimization stdstring


【解决方案1】:

第一个不能优化成第二个。第一个字符串的长度是未知的,所以必须计算,第二个你告诉它有多长,所以不需要计算。

并且使用 sizeof() 没有区别 - 这也是在编译时计算的。第一种情况使用的构造函数是:

 string( const char * s );

这个构造函数无法检测到它被赋予一个字符串文字,更不用说在编译时计算它的长度了。

此外,从 C 风格的字符串文字构造字符串在实际代码中相对很少发生 - 它根本不值得优化。如果你确实需要优化它,只需重写:

while( BIGLOOP ) {
   string s( "foobar" );
   ...
}

作为:

string f( "foobar" );
while( BIGLOOP ) {
   string s( f );
   ...
}

【讨论】:

  • 编译器应该能够在编译时算出字符串的长度,就像它可以算出 sizeof(...) 一样,因为字符串是常量
  • 我认为他的观点是有效的,在第一种情况下,编译器确实知道字符串文字的长度。如果您还不知道 std::string 和编译器是如何工作的,则无法知道编译器无法利用其对文字大小的了解。
  • 好吧,编译器可以。但是编译器编写者需要付出很多努力,并且会将编译器紧密绑定到库 - 我想编译器编写者不认为这是值得的,正确的恕我直言。
  • 编译器当然可以,它只需要扩展标准即可。 字符串常量不能是模板,所以你不能只做一个模板化的构造函数。 默认参数不能依赖于其他参数,所以你不能偷偷调用strlen() in。在 C++0x 中,你可能有一个包含字符串长度的文字类,但是会有无法从普通的字符串文字构造它。这在语言层面上确实是一次失败,虽然不是很严重。
  • 这个扩展确实会导致std::string("a\0b")出现“不正确”的行为。
【解决方案2】:

编译器无疑可以做这样的事情,实际上你可以自己做:

template<size_t SIZE>
std::string f(const char(&c)[SIZE]) {
    return std::string(c, SIZE);
}

int main() {
    std::string s = f("Hello");
    cout << s;
}

甚至使用自定义派生类型(尽管没有理由 std::string 不能有这个构造函数):

class mystring : public string {
public:
    template<size_t SIZE>
    mystring(const char(&c)[SIZE]) : string(c, SIZE) {}
};

int main() {
    mystring s("Hello");
    cout << s;
}

一个很大的缺点是每个不同的字符串大小都会生成一个函数/构造函数的版本,如果编译器不能很好地处理模板提升,整个类甚至可能被复制......这些可以处理-某些情况下的断路器。

【讨论】:

  • SIZE 应该是 size_t,而不是 int。我们不能有 -1 长度的字符串。
  • 如果你坚持!我没有意识到我打开了 -pedantic :)
  • @joshperry - 我总是打开 -pendantic。 :P
  • 天啊!我忘记了字符串文字是数组。 §2.13.4/1:“字符串文字的类型为“n const char 数组””擦除我的答案……
  • 编译器似乎不太可能为此生成额外的代码。请记住,在class{} 中声明的函数是内联的,并且您没有声明string 的新特化,因此string 方法不会重复。在大多数平台上,这应该是零开销。
猜你喜欢
  • 2010-12-16
  • 2017-03-11
  • 1970-01-01
  • 2013-06-07
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 2021-12-15
相关资源
最近更新 更多