2010-11-01 17 views
14

(Tuyên bố từ chối trách nhiệm: Tôi không biết tiêu chuẩn C++ có thể nói gì về điều này..Tôi biết, tôi thật kinh khủng)Chuỗi STL GNU: là sao chép trên ghi có liên quan ở đây không?

khi hoạt động trên các chuỗi rất lớn Tôi nhận thấy rằng std :: string đang sử dụng sao chép -Viết. Tôi quản lý để viết vòng lặp nhỏ nhất mà sẽ tái tạo các hành vi quan sát và một sau, ví dụ, chạy nghi ngờ nhanh:

#include <string> 
using std::string; 
int main(void) { 
    string basestr(1024 * 1024 * 10, 'A'); 
    for (int i = 0; i < 100; i++) { 
     string a_copy = basestr; 
    } 
} 

khi thêm một ghi trong cơ thể lặp a_copy[1] = 'B';, một bản sao thực tế dường như đã diễn ra, và chương trình chạy trong 0,3 giây thay vì một vài phần nghìn giây. 100 bài viết đã làm chậm nó xuống khoảng 100 lần.

Nhưng sau đó nó trở nên kỳ lạ. Một số chuỗi của tôi không được viết, chỉ đọc từ, và điều này đã không được phản ánh trong thời gian thực hiện, đó là gần như chính xác tỷ lệ thuận với số lượng hoạt động trên các chuỗi. Với một số đào, tôi thấy rằng chỉ cần đọc từ một chuỗi vẫn cho tôi rằng hiệu suất hit, do đó, nó đã dẫn tôi giả sử các chuỗi GNU STL đang sử dụng copy-on-read (?).

#include <string> 
using std::string; 
int main(void) { 
    string basestr(1024 * 1024 * 10, 'A'); 
    for (int i = 0; i < 100; i++) { 
     string a_copy = basestr; 
     a_copy[99]; // this also ran in 0.3s! 
    } 
} 

Sau reveling trong khám phá của tôi trong một thời gian, tôi phát hiện ra rằng đọc (với nhà điều hành []) từ chuỗi cơ sở cũng mất 0.3s cho toàn bộ program..I'm đồ chơi không phải là 100% cảm thấy thoải mái với điều này. Các chuỗi STL thực sự là sao chép trên máy đọc hay chúng có cho phép copy-on-write không? Tôi đã nghĩ rằng toán tử [] có một số biện pháp bảo vệ chống lại một người sẽ giữ tham chiếu nó trả về và sau đó viết cho nó; Đây đúng là tình trạng đó phải không? Nếu không, điều gì đang thực sự xảy ra? Nếu ai đó có thể trỏ đến một số phần có liên quan trong tiêu chuẩn C++, điều đó cũng sẽ được đánh giá cao.

Để tham khảo, tôi đang sử dụng g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3 và GNU STL.

+0

Vì câu trả lời dưới đây cho thấy đây có lẽ là câu hỏi về trình biên dịch nhiều hơn câu hỏi chuẩn C++. Bạn đang sử dụng trình biên dịch nào? Bạn đã thử các cài đặt tối ưu hóa khác nhau chưa? –

+0

Hơn so với trình biên dịch tôi tưởng tượng rằng điều này đã làm với việc thực hiện STL cụ thể mà OP đang sử dụng. Từ góc nhìn tiêu chuẩn, tôi nghĩ Charles Bailey đã trả lời. – Raj

+0

C++ 98/03 được thiết kế để cho phép các chuỗi COW, nhưng COW không bắt buộc. Ngẫu nhiên, std :: string không phải là một phần của STL mặc dù các khái niệm STL sau đó được áp dụng cho nó. –

Trả lời

14

C++ không phân biệt giữa số operator[] để đọc và viết, nhưng chỉ operator[] cho đối tượng const và đối tượng có thể thay đổi (không phải const). Vì a_copy có thể thay đổi, nên operator[] có thể thay đổi sẽ được chọn, sẽ buộc sao chép vì toán tử đó trả về tham chiếu (có thể thay đổi).

Nếu hiệu quả là mối quan tâm, bạn có thể bỏ a_copy thành const string để buộc phiên bản const của operator[] được sử dụng, sẽ không tạo bản sao của bộ đệm trong.

char f = static_cast<const string>(a_copy)[99]; 
+0

Tôi đã không coi là const-ness như một yếu tố ở tất cả. Cảm ơn vì điều đó. Hiệu quả không hoàn toàn là mối quan tâm của tôi ở đây, nhiều như nội bộ của GNU STL, tôi cho là vậy. Biết các công cụ của bạn và mọi thứ. :) –

+2

Bạn nên sử dụng 'const_cast <>' (http://msdn.microsoft.com/en-us/library/bz6at95h (VS.80) .aspx) để tạo CV. –

+0

@ J-16: Không, không, bạn không nên. Đó là diễn viên chỉ hữu ích cho việc loại bỏ const, đó là rất hiếm khi điều đúng để làm. – Puppy

13

Tiêu chuẩn C++ không cấm hoặc ủy quyền sao chép trên văn bản hoặc bất kỳ chi tiết triển khai nào khác cho std::string. Miễn là các yêu cầu ngữ nghĩa và phức tạp được đáp ứng thì việc triển khai có thể chọn bất kỳ chiến lược triển khai nào mà nó thích.

Lưu ý rằng operator[] trên chuỗi không phải là const thực sự là thao tác "ghi" khi nó trả về tham chiếu có thể được sử dụng để sửa đổi chuỗi tại bất kỳ thời điểm nào. Không có bản sao nào bị ảnh hưởng bởi sự sửa đổi đó.

Bạn đã thử định hình một trong hai cách này chưa?

const string a_copy = basestr; 
a_copy[99]; 

Hoặc

string a_copy = basestr; 
const std::string& a_copy_ref = a_copy; 
a_copy_ref[99]; 
+0

Thật vậy, cả hai cơ quan vòng lặp này đều giống nhau với ví dụ đầu tiên; nghĩa là, chỉ một vài phần nghìn giây. –

2

Hãy thử mã này:

#include <iostream> 
#include <iomanip> 
#include <string> 

using namespace std; 

template<typename T> 
void dump(std::ostream & ostr, const T & val) 
{ 
    const unsigned char * cp = reinterpret_cast<const unsigned char *>(&val); 
    for(int i=0; i<sizeof(T); i++) 
     ostr 
      << setw(2) << setfill('0') << hex << (int)cp[i] << ' '; 
    ostr << endl; 
} 

int main(void) { 
    string a = "hello world"; 
    string b = a; 
    dump(cout,a); 
    dump(cout,b); 

    char c = b[0]; 

    dump(cout,a); 
    dump(cout,b); 
} 

On GCC, đây là sản phẩm tôi nhận được:

3c 10 51 00 
3c 10 51 00 
3c 10 51 00 
5c 10 51 00 

Mà dường như để cho biết rằng có , chúng được sao chép khi đọc trong trường hợp này.

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