Bối cảnh / Tổng quan
Các thao tác trên các biến tự động ("từ ngăn xếp", là các biến mà bạn tạo mà không gọi malloc
/ new
) thường nhanh hơn nhiều so với các biến liên quan đến cửa hàng miễn phí ("heap", là các biến được tạo bằng cách sử dụng new
). Tuy nhiên, kích thước của mảng tự động được cố định tại thời gian biên dịch, nhưng kích thước của mảng từ cửa hàng miễn phí thì không. Hơn nữa, kích thước ngăn xếp bị giới hạn (thường là một vài MiB), trong khi đó cửa hàng miễn phí chỉ bị giới hạn bởi bộ nhớ hệ thống của bạn.
SSO là Tối ưu hóa chuỗi ngắn / nhỏ. Một std::string
thường lưu trữ chuỗi dưới dạng một con trỏ đến cửa hàng miễn phí ("heap"), cung cấp các đặc tính hiệu suất tương tự như khi bạn gọi new char [size]
. Điều này ngăn chặn tràn ngăn xếp cho các chuỗi rất lớn, nhưng nó có thể chậm hơn, đặc biệt là với các hoạt động sao chép. Như một tối ưu hóa, nhiều triển khai std::string
tạo ra một mảng tự động nhỏ, đại loại như thế char [20]
. Nếu bạn có một chuỗi có 20 ký tự hoặc nhỏ hơn (được đưa ra ví dụ này, kích thước thực tế khác nhau), nó sẽ lưu nó trực tiếp trong mảng đó. Điều này tránh sự cần thiết phải gọi new
tất cả, làm tăng tốc mọi thứ lên một chút.
BIÊN TẬP:
Tôi không mong đợi câu trả lời này khá phổ biến, nhưng vì nó, hãy để tôi đưa ra một triển khai thực tế hơn, với lời cảnh báo rằng tôi chưa bao giờ thực sự đọc bất kỳ triển khai SSO nào "trong tự nhiên".
Chi tiết thực hiện
Tối thiểu, std::string
cần lưu trữ các thông tin sau:
- Kích cỡ
- Công suất
- Vị trí của dữ liệu
Kích thước có thể được lưu trữ dưới dạng std::string::size_type
hoặc như một con trỏ đến cuối. Sự khác biệt duy nhất là bạn có muốn trừ hai con trỏ khi người dùng gọi size
hoặc thêm size_type
một con trỏ khi người dùng gọi end
. Khả năng có thể được lưu trữ một trong hai cách là tốt.
Bạn không trả tiền cho những gì bạn không sử dụng.
Đầu tiên, hãy xem xét việc thực hiện ngây thơ dựa trên những gì tôi đã nêu ở trên:
class string {
public:
// all 83 member functions
private:
std::unique_ptr<char[]> m_data;
size_type m_size;
size_type m_capacity;
std::array<char, 16> m_sso;
};
Đối với hệ thống 64 bit, điều đó thường có nghĩa là std::string
có 24 byte 'phí' trên mỗi chuỗi, cộng thêm 16 cho bộ đệm SSO (16 được chọn ở đây thay vì 20 do yêu cầu đệm). Sẽ thực sự có ý nghĩa khi lưu trữ ba thành viên dữ liệu đó cộng với một mảng các ký tự cục bộ, như trong ví dụ đơn giản hóa của tôi. Nếu m_size <= 16
, sau đó tôi sẽ đưa tất cả dữ liệu vào m_sso
, vì vậy tôi đã biết dung lượng và tôi không cần con trỏ tới dữ liệu. Nếu m_size > 16
, thì tôi không cần m_sso
. Hoàn toàn không có sự chồng chéo nơi tôi cần tất cả chúng. Một giải pháp thông minh hơn mà không lãng phí không gian sẽ trông giống một cái gì đó giống như thế này (chỉ mục đích chưa được kiểm tra):
class string {
public:
// all 83 member functions
private:
size_type m_size;
union {
class {
// This is probably better designed as an array-like class
std::unique_ptr<char[]> m_data;
size_type m_capacity;
} m_large;
std::array<char, sizeof(m_large)> m_small;
};
};
Tôi cho rằng hầu hết các triển khai trông giống như thế này.
std::string
thực hiện", và một người khác hỏi "những gì hiện SSO bình", bạn có thể hoàn toàn mất trí để xem xét họ có cùng một câu hỏi