编译器通常对字符串有特殊的优化吗?
Posted
技术标签:
【中文标题】编译器通常对字符串有特殊的优化吗?【英文标题】:Do compilers usually have special optimizations for strings? 【发布时间】:2011-08-31 20:01:28 【问题描述】:你经常会看到类似的东西
std::map<std::string, somethingelse> m_named_objects;
或
std::string state;
//...
if(state == "EXIT")
exit();
else if(state == "california")
hot();
人们纯粹使用字符串来提高可读性。使用整数 ID 之类的东西可以轻松实现相同的目标。
现代编译器(msvc、g++ 等)通常可以对这些类型的情况进行特殊优化吗?还是应该因为性能不佳或其他原因而避免这种情况?
【问题讨论】:
我不确定在单独编译的情况下是否存在任何有效的优化。 当人们想要提高可读性时,我通常会看到枚举而不是这些映射。枚举状态退出,加利福尼亚,(等);然后你可以有 "if (state == EXIT)" 等等。 你认为比较“EXIT”的四个字节需要多长时间? @Vanessa:这对于代码内的可读性来说很好。但是,如果您正在调试并且想知道当前的state
是什么,该怎么办?从数量上看不是很明显;您必须在枚举中查找它(假设您的调试器/IDE 无法为您查找)。而使用字符串,每个调试器都可以向您显示值。
@Nicol Bolas:然后获得更好的调试器。至少对于 Visual Studio,调试器确实会显示与整数值关联的枚举器的名称。
【参考方案1】:
现代编译器(msvc、g++ 等)通常可以针对这些类型的情况进行特殊优化吗?
据我所知,编译器不会进行此类优化。这绝对不是“标准”优化。
...人们使用字符串纯粹是为了提高可读性。
至少对于您的第二种情况,在我看来枚举更具可读性并且可以更快(因为整数比较相对于字符串比较而言相当便宜)。
enum State
Alabama,
Alaska,
Arizona,
Arkansas,
California,
Colorado,
Connecticut,
Delaware,
// ... More
;
// ...
State state = California;
if(state == California) /* true */
【讨论】:
没错,用枚举代替字符串。如果你真的必须使用字符串,不要太在意性能,检查几个字符还不错! 如果不考虑性能,使用枚举而不是字符串的最明显优势是枚举可以在 switch 语句中使用,而字符串不能。 最明显的优势是编译器会告诉你如果你写的是california
而不是California
。【参考方案2】:
图书馆可以。
编译器可能会通过为共享/相同的静态字符串设置别名来优化(假设它们确实被视为常量)。
我目前知道的所有 C++ 标准库实现都具有“小字符串优化”,这意味着不需要为小字符串进行额外的堆分配; IE。
std::string a("small");
将完全自动(堆栈)分配 - 在高度优化的情况下甚至可能是寄存器分配(?)
如果您需要极快的字符串查找并且可以花一些时间构建数据结构,请查看 Tries(WP:Trie,Radix_tree)
就直接替换而言,通常可以通过使用适当调整的哈希映射而不是基于 RB-tree 的哈希映射获得很多好处:
罢工>
std::map<std::string, somethingelse> m_named_objects;
替换为
std::unordered_map<std::string, somethingelse> m_named_objects;
开心
【讨论】:
为map
关键优化案例添加建设性想法【参考方案3】:
在给出的示例中,编译器通常无法优化,因为内容取决于运行时。
std::map<std::string, int>
没有最理想的性能特征,因为std::string
上的operator<()
相对昂贵。
【讨论】:
【参考方案4】:字符串的优化是针对库而不是编译器的。如果您想要类似字符串的标识符,枚举是一种可能性。但是一个更好的,特别是对于打印和调试,是一个固定长度的标识符字符串类。
它可以转换为const char *
和std::string
,但它的内存分配为零。相反,它只是一个 32 个字符(或任何你想要的)数组的包装器。
最好的部分是,因为它是一个标识符,所以你不需要关心 ASCII 逐个字符的比较。 operator<
可以将 32 个字符的数组读取为 8 个uint32_t
s,甚至可以读取为 4 个uint64_t
s。您所需要的只是一个排序,而不是特定排序。 operator==
可以做类似的测试。
这是一个非常简单的类。如果您想要不区分大小写的比较,则可以在将字符串复制到对象时将其转换为小写。
如果您需要超过 31 个字节的字符串(一个用于 \0
终止符),那么我建议将字符串截断到合适的大小。但从给定字符串的 middle 截断,而不是结尾。标识符的开头和结尾往往比中间更独特。您甚至可以在截断的字符串中加入一些特殊字符,以识别它是截断的版本。
也可以采用这个想法,在字符串中放入一个哈希。所以前 4 个字节将是 original 字符串的散列,而不是截断的散列。比较测试只使用散列,其他 28 个字节用于使其易于阅读。
【讨论】:
以上是关于编译器通常对字符串有特殊的优化吗?的主要内容,如果未能解决你的问题,请参考以下文章