std::string_view 简要介绍

std::string_view 简要介绍

目的

字符串在开发中使用频率很高,其性能至关重要。C++ 标准库中的 std::string 采用多种技术以提升性能。比如:短字符串优化技术,当字符串较短时,直接在栈上分配空间,不进行动态内存分配,从而提升短字符串的效率;

但是字符串的初始化会影响其性能。std::string 一般通过 char* 类型进行初始化,例如:字符串字面量、从文件读入等,这样就需将 char* 字符数据拷贝到 std::string 所管理的内存空间中;而很多时候并不会去修改字符串内容,拷贝毫无必要,仅需生成 char* 某种标记即可,所以此时 std::string 初始化就造成了性能损失;

针对上述问题,C++17 引入了 std::string_view 。std::string_view 仅标记字符串位置与尺寸,不拷贝字符串数据,避免了额外的内存分配。所以当仅访问而不修改字符串时,std::string_view 即可提升效率;

对比

下述代码对 std::string 与 std::string_view 的性能进行了简单对比。需要注意的是,测试字符串的长度需超过 std::string 开启短字符串优化的长度,这样差别更为显著;

#include <iostream>
#include <vector>
#include <string>
#include <string_view>
#include <chrono>

/**
 * @brief  std::string 与 std::string_view 测试
 */
int main()
{
    const int N = 1000000;
    const char s[] = "qwertyuiopasdfghjklzxcvbnm1234567890";  // 使用 GCC 编译,长度需超过 23

    auto start = std::chrono::system_clock::now();
    for (int i = 0; i < N; ++i) {
        std::string str = s;
        std::string sub = str.substr(2, 10);
    }
    auto end = std::chrono::system_clock::now();
    auto elapse = (std::chrono::duration_cast<std::chrono::milliseconds>(end-start)).count();
    std::cout << "string      type elapse: " << elapse << " ms \n";

    start = std::chrono::system_clock::now();
    for (int i = 0; i < N; ++i) {
        std::string_view str = s;
        std::string_view sub = str.substr(2, 10);
    }
    end = std::chrono::system_clock::now();
    elapse = (std::chrono::duration_cast<std::chrono::milliseconds>(end-start)).count();
    std::cout << "string_view type elapse: " << elapse << " ms \n";

    return 0;
}

终端输出:

string      type elapse: 4111 ms 
string_view type elapse: 8 ms 

从上述代码看出,使用 std::string_view 后,循环中的操作性能提升约 500 倍,几乎是质的飞跃;

总结

在处理字符串时,当仅需访问而不修改其内容时,可多使用 std::string_view 代替 std::string,以消除数据拷贝,避免额外内存分配,进而提升处理字符串的效率;

参考


本文作者: 王同学