为何Transform视图版大小写无关字符串相等判断性能逊于投影版?
摘要
在判断两个字符串是否大小写无关相等时,为何以下代码
bool rangeV3WithTransformViews(std::string_view str1, std::string_view str2) { return equal(str1 | transform(tolowercase), str2 | transform(tolowercase)); }
的性能远低于这段代码?
bool rangeV3WithProj(std::string_view str1, std::string_view str2) { return equal(str1, str2, std::equal_to{}, tolowercase, tolowercase); }
后者在GCC 13.2搭配-O3编译选项时,生成的汇编代码与底层C风格实现完全一致:
#define to_lower_ascii(c) (((c) >= 'A' && (c) <= 'Z') ? (c) + 32 : (c)) int cstyle(const char* str1, const char* str2) { while (*str1 != '\0' && *str2 != '\0' && to_lower_ascii(*str1) == to_lower_ascii(*str2)) { str1++; str2++; } return to_lower_ascii(*str1) - to_lower_ascii(*str2); }
详细说明
我最初因实现大小写无关字符串相等需编写冗长、底层且可读性差的代码而沮丧,于是尝试基于range编写实现,最初得到上述第一个方案,却被quick-bench.com证实其性能较差。有两点结论清晰:
- GCC(至少QuickBench上的版本及对应编译选项)可将
rangeV3WithProj编译为与cstyle完全一致的汇编,说明编译器能完全看透该抽象; rangeV3WithProj比rangeV3WithTransformViews更简洁、抽象层级更少,理论上不应更慢。
我原本预期编译器也能对rangeV3WithTransformViews做同样优化,但实际并未实现。因此提出以下问题:
- 是什么导致第一个代码片段本质上比第二个更慢?
- 是什么阻碍编译器为第一个片段生成与后两者一致的汇编代码?
我认为问题出在transform上,但为何它会导致汇编差异与性能下降?毕竟输入是std::string_view,属于sized_range,transform对其处理后仍返回sized_range,不存在循环长度未知的问题。
注意事项
我并不清楚QuickBench的具体运作机制,但令我疑惑的是,即便传入-O3 -std=c++20选项,Compiler Explorer生成的rangeV3WithProj与cstyle汇编代码并不完全一致。
(¹) 如果我在代码中埋下错误,比如将某些!=改为==、32改为34,或是最后一处str2应为str1,你能发现吗?
内容的提问来源于stack exchange,提问作者Enlico
相关产品推荐
相关产品推荐

