C++性能优化神器:它 比 std::stoi 快 3 倍!
1. 背景
我最近在项目中遇到一个需求——针对同一个类型的接口,需要支持传入多种数据类型,大概形式如下,
代码语言:javascript代码运行次数:0运行复制void UpdateValue(const char* value_name,int value);
void UpdateValue(const char* value_name,double value);
void UpdateValue(const char* value_name,const char* value);
如果是C++接口,可以用重载用模板,但是我需要提供C接口,这些特性都用不了。虽然可以采用如下的方案:
代码语言:javascript代码运行次数:0运行复制void UpdateIntValue(const char* value_name,int value);
void UpdateDoubleValue(const char* value_name,double value);
void UpdateCharValue(const char* value_name,const char* value);
但是这样的话,每次有新的类型,都需要增加新的接口,每次增加新的接口,都意味着使用库的铜须需要重新编译、集成,这套方案显然是不成熟的。 通过思考发现,我可以通过value_name
知晓value
的类型,所以可以考虑如下的方案:
void UpdateValue(const char* value_name, const char* value);
这样提供的接口是稳定的,不会因为类型增加而改变。但是这样又引入了一个新的问题,就是如何将char*转换为对应的类型,比如int、double等。
通常情况下,我们会使用std::stoi
、std::stof
等函数,但是这些函数存在一些问题:
- 性能问题:std::stoi() 和 std::stof() 需要将输入转换为 std::string,这可能导致额外的堆分配和数据拷贝,从而降低性能。
- **依赖
std::locale
**:许多标准转换函数受std::locale
影响,可能导致额外的性能开销。 - 不灵活:
std::stoi()
只能处理std::string
,而std::strtol()
/std::strtof()
需要 C 风格字符串,使用起来不够现代化。
对于依赖和不灵活,我到可以退而求其次,但是性能一直是我特别关注的。后来我发现了charconv
,它是一个用于字符数组与数值类型之间转换的库,可以高效地进行数值解析和格式化。
当前可查的 std::from_chars()
和 std::stoi()
/ std::stof()
的性能对比结果如下:
方法 | 解析整数 (100 万次) | 解析浮点数 (100 万次) |
---|---|---|
std::stoi | 120 ms | - |
std::from_chars | 35 ms | 90 ms (C++20) |
std::strtol | 100 ms | - |
std::strtod | - | 180 ms |
从数据来看,std::from_chars()
解析 整数比 std::stoi()
快 3~4 倍,解析 浮点数比 std::strtod()
快 2 倍,这对性能敏感的应用来说是一个巨大的提升。
2. charconv
<charconv>
自 C++17 引入,提供 std::to_chars()
和 std::from_chars()
,用于在 数值类型和字符数组 之间进行高效转换。 其使用非常简单,如下仅以整数为例,其余类型雷同。
整数转字符串 (to_chars
)
代码语言:javascript代码运行次数:0运行复制#include <charconv>
#include <iostream>
#include <array>
int main() {
std::array<char, 20> buffer; // 预分配足够大的缓冲区
int value = 12345;
auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value);
if (ec == std::errc{}) {
std::cout << "Converted: " << std::string(buffer.data(), ptr) << std::endl;
} else {
std::cerr << "Conversion failed" << std::endl;
}
}
字符串转整数 (from_chars
)
代码语言:javascript代码运行次数:0运行复制#include <charconv>
#include <iostream>
int main() {
constchar* str = "12345";
int value;
auto [ptr, ec] = std::from_chars(str, str + std::strlen(str), value);
if (ec == std::errc{}) {
std::cout << "Parsed value: " << value << std::endl;
} else {
std::cerr << "Parsing failed" << std::endl;
}
}
3. 实战建议
虽然 charconv
性能强大,但在使用时需要注意以下几点:
- 负数解析:
std::from_chars()
解析数字时,直到C++20后才支持解析负数,如果项目中不能使用C++20时,需要手动处理:
const char* str = "-123";
int value;
if (*str == '-') {
auto [ptr, ec] = std::from_chars(str + 1, str + std::strlen(str), value);
value = -value; // 手动处理负号
}
- 需要确保缓冲区足够大:使用
std::to_chars()
时,需要保证char
数组大小足够,否则可能会转换失败。
4. 总结
std::charconv
提供了高效数值转换方法,不仅可以显著提高性能,还避免了 std:locale
影响。但是使用时还需要注意如上的实战建议。
发布者:admin,转转请注明出处:http://www.yc00.com/web/1748041479a4722747.html
评论列表(0条)