为何为不会抛出异常的函数添加noexcept会降低性能?
noexcept会降低无异常函数的性能? 问题背景
原本预期给绝不会抛出异常的函数添加noexcept关键字不会产生性能负面影响,就像const关键字一样,但测试中发现给isVowel函数添加noexcept后,性能反而出现了下降。
测试场景
先生成标记元音字符的std::array<bool,256>映射表,再通过isVowel函数查询字符是否为元音,对比有无noexcept修饰的两个版本的性能差异。
简化示例代码
#include <array> #include <limits> static constexpr unsigned char vowels[]{ 'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U' }; auto constexpr genVowelTestMap() { std::array<bool, std::numeric_limits<256> m{}; for (const unsigned char& c : vowels) { m[c] = true; } return m; } static constexpr auto vowelTestMap = genVowelTestMap(); bool isVowel(const char c)noexcept/*The relevant noexcept*/{ return vowelTestMap[c]; }
完整测试代码
#include <array> #include <limits> #include <string> #include <cassert> #include <chrono> #include <iostream> static constexpr unsigned char vowels[]{ 'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U' }; auto constexpr genVowelTestMap() { std::array<bool, 256> m{}; static_assert(sizeof(char) == 1); for (const unsigned char& c : vowels) { m[c] = true; } return m; } static constexpr auto vowelTestMap = genVowelTestMap(); inline bool isVowel(const char c){ return vowelTestMap[c]; } inline bool isVowelNoExcept(const char c)noexcept{ return vowelTestMap[c]; } std::string genRandomLetterString(size_t size) { std::string s(size, '\0'); //24 + 24 options = 48 options. for (size_t i = 0; i < size; i++) { char& c = s[i]; c = std::rand() % 48; if (c < 24) c += 'a'; else c += 'a' - 24; assert(isalpha(c)); } return s; } template<bool USE_NO_EXCEPT> void runTest() { const int runs = 1000; auto start = std::chrono::high_resolution_clock::now(); const std::string s = genRandomLetterString(1u << 20); size_t a = 0; for (int i = 0; i < runs; i++) { for (const char& c : s) { if constexpr (USE_NO_EXCEPT) { if (isVowelNoExcept(c))a++; } else { if (isVowel(c))a++; } } } auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count(); std::cout << (USE_NO_EXCEPT?"NOEXCEPT":"\t") << "\tr(" << (a/runs) << ")Time taken : " << duration << " microseconds." << std::endl; } int main() { for (int j = 0; j < 10; j++) { runTest<true>(); runTest<false>(); } }
测试结果
使用MSVC编译器(C++20、x64、/O2、/Ob2、/Oi、/GL)编译时,noexcept版本性能比普通版本低约20%;切换至/O3优化等级后,两者性能一致。多次测试数据如下:
Time taken : noexcept version : normal version.
Time taken : 814786 microseconds:645568 microseconds. //The first is always slowest. Should prob be ignored.
Time taken : 675711 microseconds:612367 microseconds. //Run order was noexcept, normal and then repeat.
Time taken : 685613 microseconds:605072 microseconds.
Time taken : 655509 microseconds:607108 microseconds.
Time taken : 756300 microseconds:623599 microseconds.
Time taken : 718311 microseconds:605397 microseconds.
Time taken : 672052 microseconds:615306 microseconds.
Time taken : 703469 microseconds:608384 microseconds.
Time taken : 668540 microseconds:604204 microseconds.
Time taken : 667859 microseconds:605363 microseconds.
此外,对比两个版本的汇编代码,未发现明显差异。
核心疑问
为何noexcept会降低绝不会抛出异常的函数的性能?
内容的提问来源于stack exchange,提问作者r123

