2009-04-11 23 views
6

Tôi đang xử lý nhiều chuỗi trong chương trình của mình. Dữ liệu chuỗi này không thay đổi trong toàn bộ thời gian cuộc sống sau khi chúng được đọc vào chương trình của tôi.Cách giải phóng dung lượng không sử dụng của một chuỗi

Nhưng kể từ khi chuỗi C++ giữ dung lượng, chúng sẽ lãng phí rất nhiều không gian sẽ không được sử dụng chắc chắn. Tôi đã cố gắng giải phóng những khoảng trống đó, nhưng nó không hoạt động.

Sau đây là đoạn code đơn giản mà tôi đã cố gắng:

string temp = "123456789"; 
string str; 

cout << str.capacity() << endl; 

str.reserve(16);  
cout << str.capacity() << endl;  
// capacity is 31 on my computer  

str += temp;  
cout << str.capacity() << endl;  

str.reserve(16);  
cout << str.capacity() << endl;  
// can't release. The capacity is still 31. 

(Trình biên dịch là Visual C++)

Làm thế nào tôi có thể phát hành nó?

+0

Có bạn thực sự hết bộ nhớ? Nếu không, đây là tối ưu hóa sớm. Nếu bạn đã hết bộ nhớ, thì những đối tượng nào thực sự thống trị phân bổ bộ nhớ của bạn? Vấn đề là loại điều này không hữu ích. –

+0

Không có cách nào để buộc người cấp phát sử dụng ít byte hơn ** cho việc xây dựng ban đầu của một chuỗi đã cho ** - ít nhất là không phải viết mẫu lớp cấp phát STL của riêng bạn (và có thể không phải là sau đó). –

Trả lời

12

Khi bạn gọi reserve, bạn đang thực hiện yêu cầu để thay đổi dung lượng. Việc triển khai sẽ chỉ đảm bảo rằng số lượng bằng hoặc lớn hơn số tiền này được đặt trước. Do đó, yêu cầu thu nhỏ dung lượng có thể bị bỏ qua một cách an toàn bởi một triển khai cụ thể.

Tuy nhiên, tôi khuyến khích bạn xem xét liệu đây có phải là tối ưu hóa không sớm hay không. Bạn có chắc chắn rằng bạn đang thực sự tạo ra rất nhiều chuỗi mà đó là một nút cổ chai bộ nhớ cho bạn? Bạn có chắc chắn rằng nó thực sự là bộ nhớ mà nút cổ chai?

Từ tài liệu cho reserve:

này có thể mở rộng hoặc thu nhỏ kích thước của không gian lưu trữ trong chuỗi, mặc dù thông báo rằng kết quả công suất sau khi một cuộc gọi đến chức năng này không nhất thiết phải bằng res_arg nhưng có thể bằng hoặc lớn hơn hơn res_arg, do đó thu hẹp lại yêu cầu có thể hoặc không thể tạo ra một số giảm thực tế được phân bổ không gian trong một thư viện cụ thể triển khai. Trong mọi trường hợp, nó không bao giờ cắt bớt nội dung chuỗi (cho mục đích đó, xem thay đổi kích thước hoặc xóa, trong đó sửa đổi nội dung).

+0

+1 có vẻ như tối ưu hóa sớm với tôi. –

5

Tại sao bạn không sử dụng mảng char?

+1

+1. Một mảng char của thư viện có thể được chuyển thành chuỗi std :: khi bạn muốn sử dụng nó và bị hủy sau này. – GogaRieger

+0

Giả sử bạn có nghĩa là phân bổ động, thậm chí có thể "lãng phí" ít nhất 4 byte để giữ độ dài và có thể phân bổ động bên trong có thể phân bổ trong các khối không nhỏ hơn 16/32 byte vì lý do hiệu quả. –

3

Tôi nghĩ bạn có thể sử dụng phương thức hoán đổi để giải phóng dữ liệu. hoán đổi nó bằng một chuỗi cục bộ trống để khi chuỗi cục bộ vượt quá phạm vi bộ nhớ được giải phóng.

+0

+1. Đây là một cách tốt (mặc dù không được bảo đảm) để trả về bộ nhớ nếu bạn muốn đặt chuỗi thành một thứ khác. Mặc dù nó sẽ không giúp cắt tỉa không gian nếu cấp phát cơ bản làm tròn lên đến 16 byte gần nhất (như MSVC++ dường như làm), không có gì sẽ giúp điều đó. –

0

Không có đảm bảo dung lượng tối thiểu cho std::string. Bạn có thể yêu cầu bất kỳ dung lượng nào bạn muốn bằng cách gọi reserve nhưng một triển khai cụ thể chỉ đảm bảo đặt công suất cho một số lượng lớn hơn hoặc bằng kích thước được yêu cầu.

Dưới đây là một phiên bản sửa đổi của chương trình của bạn mà thử nghiệm một số phương pháp của chuỗi thu hẹp:

#include <string> 
#include <iostream> 
using namespace ::std; 

template< typename S > 
S & reserve_request(S & s, typename S::size_type n) { 
    s.reserve(n); return s; 
} 

template< typename S > 
S & shrink_request1(S & s) { s.reserve(); return s; } 

template< typename S > 
S & shrink_request2(S & s) { S(s).swap(s); return s; } 

template< typename S > 
S & shrink_request3(S & s) { S(s.c_str()).swap(s); return s; } 

template< typename S > 
void test(S & s) { cout << s.capacity() << endl; } 

int main() { 
    string temp = "123456789"; // length 16 
    string str; 

    test(str);       // 15 
    test(reserve_request(str, 16)); // 31 
    test(str += temp);     // 31 
    test(reserve_request(str, 16)); // 31 
    test(shrink_request1(str));  // 31 
    test(shrink_request2(str));  // 31 
    test(shrink_request3(str));  // 31 
    return 0; 
} 

Nó sẽ xuất hiện mà Visual C++ 's std::string thường giữ một số công suất dự phòng.

Nếu dự án của bạn tải số lượng lớn chuỗi được đọc từ nguồn bên ngoài có kích thước không bao giờ thay đổi, bạn có thể tắt (như những người khác đã đề xuất) lưu trữ chúng trong một khối bộ nhớ ký tự lớn được phân tách bằng '\0' ký tự (tức là, như C-strings). Nếu muốn, bạn có thể cung cấp các hàm bao bọc trả lại std::string giây khi đang di chuyển.

4

Spelling ra Naveen's answer:

string x = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"; 
cerr << x.capacity() << "\n"; // MSVC++: 63 g++: 52 

// This tends not to work (although in theory it could): 
//x = "XYZ"; 
//cerr << x.capacity() << "\n"; // MSVC++: 63 g++: 52 

// This tends to work (although in theory it might not): 
string("XYZ").swap(x); 
cerr << x.capacity() << "\n"; // MSVC++: 15 g++: 3 

Lưu ý rằng nếu bộ cấp phát cơ bản phân bổ hơn n byte khi xây dựng một string chiều dài n (ví dụ bằng cách làm tròn lên đến gần 32 như MSVC++ dường như do), không có cách nào để sử dụng ít byte hơn. Tuy nhiên, có thể bạn sẽ không muốn làm điều đó, vì điều này "làm tròn" được thực hiện để làm cho quá trình phân bổ bộ nhớ động hiệu quả hơn, và cũng có tác dụng phụ của việc ghép nối các chuỗi ngắn nhanh hơn trung bình (vì ít reallocations cần xảy ra).

1

Điều này chủ yếu là triển khai cụ thể. Ý tưởng là giảm thiểu yêu cầu phân bổ phân mảnh bộ nhớ. Thật dễ dàng để chứng minh rằng bằng cách tăng gấp đôi kích thước hiện tại mỗi khi khối được mở rộng, cả số phân bổ và phân mảnh bộ nhớ đều được giảm thiểu. Do đó, việc triển khai container STL điển hình sẽ tăng gấp đôi khối hiện có khi mở rộng. Một điều bạn có thể làm là sử dụng một trình phân bổ tùy chỉnh sẽ không phân bổ nhiều hơn mức cần thiết, tiếp theo, xây dựng std :: string objects của bạn khi bạn không còn cần thao tác chúng nữa (hoặc khi thao tác xong, chỉ cần hoán đổi vào một cái mới.). std :: sting object - điều này về cơ bản là những gì người khác đã làm trong câu trả lời của họ) và cuối cùng, bạn có thể sử dụng bộ cấp phát bộ nhớ gộp để giảm thiểu phân mảnh bộ nhớ, lãng phí và cải thiện hiệu suất.

Xem:

http://www.codeguru.com/cpp/cpp/cpp_mfc/stl/article.php/c4079 http://www.sjbrown.co.uk/2004/05/01/pooled-allocators-for-the-stl/ http://www.codeproject.com/KB/stl/blockallocator.aspx

Tìm kiếm "STL cấp phát" và "Memory Pool"

4

Thử std::string swap-trick để teo dây của bạn:

std::string(str.data(), str.size()).swap(str) 

Nơi str là chuỗi bạn muốn cắt giảm kích thước.

0

công suất sẽ KHÔNG BAO GIỜ ít hơn 15 với STL dinkumware. std :: basic_string có một liên minh đó là con trỏ đến một bộ đệm cấp hoặc một bộ đệm 16 byte (nếu công suất() < = 15) (cho các chuỗi char)

thấy file header xstring

trong Ví dụ bạn cung cấp cho nơi bạn dự trữ 16 bạn đang thực sự đặt 17 (một cho null) đó là> 16 do đó được phân bổ chứ không phải là lưu trữ trong 16 byte trong công đoàn con trỏ bộ nhớ cache. Phân bổ đó tăng gấp đôi kích thước trước đó (16) để bạn nhận được 32. Dung lượng của chuỗi đó có thể là 31.

Nhưng điều này phụ thuộc vào thực thi STL.

Thay đổi tham số mẫu cấp phát trong mẫu bị giảm của std :: basic_string là KHÔNG đủ - lựa chọn khi nào cấp phát và bao nhiêu, nằm trong tiêu chuẩn :: basic_string grow algorithm NOT trong bộ cấp phát. Tăng gấp đôi kích thước trước đó (và co lại khi < 1/4) là công cụ tiêu chuẩn trong Giới thiệu về thuật toán - Cormen Lieserson Rivest Stein

Không chắc về algo thu hẹp trong dinkumware .......

Các vấn đề liên quan