编译器通常对字符串有特殊的优化吗?

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&lt;std::string, int&gt; 没有最理想的性能特征,因为std::string 上的operator&lt;() 相对昂贵。

【讨论】:

【参考方案4】:

字符串的优化是针对库而不是编译器的。如果您想要类似字符串的标识符,枚举是一种可能性。但是一个更好的,特别是对于打印和调试,是一个固定长度的标识符字符串类。

它可以转换为const char *std::string,但它的内存分配为零。相反,它只是一个 32 个字符(或任何你想要的)数组的包装器。

最好的部分是,因为它是一个标识符,所以你不需要关心 ASCII 逐个字符的比较。 operator&lt; 可以将 32 个字符的数组读取为 8 个uint32_ts,甚至可以读取为 4 个uint64_ts。您所需要的只是一个排序,而不是特定排序。 operator== 可以做类似的测试。

这是一个非常简单的类。如果您想要不区分大小写的比较,则可以在将字符串复制到对象时将其转换为小写。

如果您需要超过 31 个字节的字符串(一个用于 \0 终止符),那么我建议将字符串截断到合适的大小。但从给定字符串的 middle 截断,而不是结尾。标识符的开头和结尾往往比中间更独特。您甚至可以在截断的字符串中加入一些特殊字符,以识别它是截断的版本。

也可以采用这个想法,在字符串中放入一个哈希。所以前 4 个字节将是 original 字符串的散列,而不是截断的散列。比较测试只使用散列,其他 28 个字节用于使其易于阅读。

【讨论】:

以上是关于编译器通常对字符串有特殊的优化吗?的主要内容,如果未能解决你的问题,请参考以下文章

GCC 可以使用编译时常量变量优化类的方法吗?

DLL文件没有语言限制吗?

编译器会优化未使用的链接文件吗?

c++中for循环和switch语句哪个更高效

尾递归优化

编译器可以优化多个相同的函数调用吗