通常,默认构造函数应该是创建空容器的最快方法。这就是为什么我惊讶地发现它比初始化为空字符串字面量更糟糕:
#include <string>
std::string make_default() {
return {};
}
std::string make_empty() {
return "";
}
这编译为:(clang 16,libc++)
make_default():
mov rax, rdi
xorps xmm0, xmm0
movups xmmword ptr [rdi], xmm0
mov qword ptr [rdi + 16], 0
ret
make_empty():
mov rax, rdi
mov word ptr [rdi], 0
ret
请注意,返回{}
总共需要清零24个字节,而返回""
只需要清零2个字节。为什么return "";
会更好?
1条答案
按热度按时间w41d8nur1#
这是libc的
std::string
实现中有意做出的决定。首先,
std::string
具有所谓的 * 小字符串优化(SSO)*,这意味着对于非常短(或空)的字符串,它将直接将其内容存储在容器中,而不是分配动态内存。这就是为什么我们在这两种情况下都看不到任何分配。在libc中,
std::string
的“简短表示”包括:| 含义| Meaning |
| --| ------------ |
| “短标志”表示它是一个短字符串(零表示是)| "short flag" indicating that it is a short string (zero means yes) |
| 字符串的长度,不包括空结束符| length of the string, excluding null terminator |
| 填充字节以对齐字符串数据(
basic_string<char>
无)| padding bytes to align string data (none forbasic_string<char>
) || 字符串数据,包括空终止符| string data, including null terminator |
对于空字符串,我们只需要存储两个字节的信息:
接受
const char*
的构造函数将只写入这两个字节,这是最低限度。默认构造函数 “不必要地” 将std::string
包含的所有24个字节归零。**这可能总体上更好,因为它使编译器可以发出std::memset
或其他SIMD并行方式来批量清零字符串数组。有关完整的解释,请参见下文:
初始化
""
/调用string(const char*)
为了理解发生了什么,让我们看看libc++ source code for
std::basic_string
:最后调用
__init(__s, 0)
,其中0
是从std::char_traits<char>
获得的字符串长度:__set_short_size
最终只会写入一个字节,因为字符串的简短表示是:编译器优化后,将
__is_long_
、__size_
和__data_
的一个字节归零编译为:初始化
{}
/调用string()
相比之下,默认构造函数更浪费:
这最终调用了
__default_init()
,它执行以下操作:__rep()
的值初始化会导致24个零字节,因为:结论
如果为了一致性,你想在任何地方都进行值初始化,不要让这一点妨碍你。不必要地将几个字节清零并不是您需要担心的大性能问题。
实际上,当初始化大量字符串时,它是有用的,因为可以使用
std::memset
,或者其他一些SIMD方式来清零内存。