「性能革命」从堆到栈:C++游戏字符串优化全攻略 20倍提升(附性能对比)

31 阅读3分钟

很多人使用字符串时,会直接使用标准库提供的string,但是string使用的是堆内存,无论申请还是释放还是造成内存碎片,都会对性能有比较大的影响.所以如果能使用栈内存来代替会有较大的性能提升.

template<int Length>
template<int Length>
class StackString
{
public:
    // 不使用StackString() = default; 因为这样仍然会允许使用初始化列表,但是使用初始化列表可能会引起歧义导致容易出现错误
    StackString() {}
    // 为了方便查代码,将返回指针的函数命名为具体的名字
    const char* str() const { return mValue; }
    // 仅为了在类型匹配时能够编译通过,尽量避免直接使用
    const char* data() const { return mValue; }
    char* data() { return mValue; }
    bool isEmpty() const { return mValue[0] == '\0'; }
    void clear() { mValue[0] = '\0'; }
    int length() const
{
        for (int i = 0; i < Length; ++i)
        {
            if (mValue[i] == '\0')
            {
                return i;
            }
        }
        return Length;
    }
    constexpr int size() const { return Length; }
    constexpr const char& operator[](int index) const
    {
        if (index < 0 || index >= Length)
        {
            cout << "数组越界" << endl;
            index = Length - 1;
        }
        return mValue[index];
    }
    constexpr char& operator[](int index)
    {
        if (index < 0 || index >= Length)
        {
            cout << "数组越界" << endl;
            index = Length - 1;
        }
        return mValue[index];
    }
public:
    char mValue[Length]{};
};
template<int Length>
class StackString
{
public:
    // 不使用StackString() = default; 因为这样仍然会允许使用初始化列表,但是使用初始化列表可能会引起歧义导致容易出现错误
    StackString() {}
    // 为了方便查代码,将返回指针的函数命名为具体的名字
    const char* str() const { return mValue; }
    // 仅为了在类型匹配时能够编译通过,尽量避免直接使用
    const char* data() const { return mValue; }
    char* data() { return mValue; }
    bool isEmpty() const { return mValue[0] == '\0'; }
    void clear() { mValue[0] = '\0'; }
    int length() const
{
        for (int i = 0; i < Length; ++i)
        {
            if (mValue[i] == '\0')
            {
                return i;
            }
        }
        return Length;
    }
    constexpr int size() const { return Length; }
    constexpr const char& operator[](int index) const
    {
        if (index < 0 || index >= Length)
        {
            cout << "数组越界" << endl;
            index = Length - 1;
        }
        return mValue[index];
    }
    constexpr char& operator[](int index)
    {
        if (index < 0 || index >= Length)
        {
            cout << "数组越界" << endl;
            index = Length - 1;
        }
        return mValue[index];
    }
public:
    char mValue[Length]{};
};

大多数使用字符串的时候都是已经知晓字符串最大的长度为多少,所以就可以使用栈内存的字符串来代替string.

可以根据自己的需求封装函数,使其达到string的易用性.

以下是测试代码:

#include <chrono>
#include <iostream>
#include <string>

using namespace std;

template<int Length>
class StackString
{
public:
    // 不使用StackString() = default; 因为这样仍然会允许使用初始化列表,但是使用初始化列表可能会引起歧义导致容易出现错误
    StackString() {}
    // 为了方便查代码,将返回指针的函数命名为具体的名字
    const char* str() const { return mValue; }
    // 仅为了在类型匹配时能够编译通过,尽量避免直接使用
    const char* data() const { return mValue; }
    char* data() { return mValue; }
    bool isEmpty() const { return mValue[0] == '\0'; }
    void clear() { mValue[0] = '\0'; }
    int length() const
{
        for (int i = 0; i < Length; ++i)
        {
            if (mValue[i] == '\0')
            {
                return i;
            }
        }
        return Length;
    }
    constexpr int size() const { return Length; }
    constexpr const char& operator[](int index) const
    {
        if (index < 0 || index >= Length)
        {
            cout << "数组越界" << endl;
            index = Length - 1;
        }
        return mValue[index];
    }
    constexpr char& operator[](int index)
    {
        if (index < 0 || index >= Length)
        {
            cout << "数组越界" << endl;
            index = Length - 1;
        }
        return mValue[index];
    }
public:
    char mValue[Length]{};
};

int TEST_COUNT = 100000000;
void test_std_string() 
{
    using namespace chrono;
    auto start = high_resolution_clock::now();

    string s;
    for (int i = 0; i < TEST_COUNT; ++i) 
    {
        s = "123";
    }

    auto end = high_resolution_clock::now();
    auto duration = duration_cast<milliseconds>(end - start);
    cout << "[std::string] Time: " << duration.count() << " ms " << "str:" << s.data() << "\n";
}

void test_stack_string() 
{
    using namespace chrono;
    auto start = high_resolution_clock::now();

    StackString<32> s;
    for (int i = 0; i < TEST_COUNT; ++i) 
    {
        memcpy(s.data(), "123", 4);
    }

    auto end = high_resolution_clock::now();
    auto duration = duration_cast<milliseconds>(end - start);
    cout << "[StackString] Time: " << duration.count() << " ms " << "str:" << s.data() << "\n";
    system("pause");
}

int main() 
{
    cout << "===== 性能基准测试 =====" << "\n";
    cout << "测试次数: " << TEST_COUNT << " 次\n\n";

    cout << "-- std::string 测试 --" << "\n";
    test_std_string();

    cout << "\n-- StackString 测试 --" << "\n";
    test_stack_string();

    return 0;
}

最终测试结果:

微信截图_20250223153509.png