Tôi đang đọc trong một tệp văn bản lớn với 1,4 triệu dòng có kích thước 24 MB (trung bình 17 ký tự một dòng).Tại sao bộ nhớ dư thừa cho chuỗi trong Delphi?
Tôi đang sử dụng Delphi 2009 và tệp là ANSI nhưng được chuyển đổi thành Unicode khi đọc, vì vậy, bạn có thể nói văn bản sau khi được chuyển đổi có kích thước 48 MB.
(Edit: Tôi tìm thấy một ví dụ đơn giản hơn nhiều ...)
Tôi đang tải văn bản này vào một StringList đơn giản:
AllLines := TStringList.Create; AllLines.LoadFromFile(Filename);
tôi thấy rằng các dòng dữ liệu dường như mất nhiều bộ nhớ hơn 48 MB.
Thực tế, chúng sử dụng 155 MB bộ nhớ.
Tôi không quan tâm Delphi sử dụng 48 MB hoặc thậm chí nhiều đến 60 MB cho phép một số chi phí quản lý bộ nhớ. Nhưng 155 MB có vẻ quá nhiều.
Đây không phải là lỗi của StringList. Trước đây tôi đã thử tải các dòng vào cấu trúc bản ghi và tôi nhận được kết quả tương tự (160 MB).
Tôi không thấy hoặc hiểu điều gì có thể khiến Delphi hoặc trình quản lý bộ nhớ FastMM sử dụng gấp 3 lần lượng bộ nhớ cần thiết để lưu trữ chuỗi. Phân bổ đống không thể không hiệu quả, phải không?
Tôi đã sửa lỗi này và nghiên cứu nó hết mức có thể. Bất kỳ ý tưởng nào về lý do tại sao điều này có thể xảy ra hoặc những ý tưởng có thể giúp tôi giảm mức sử dụng dư thừa sẽ được đánh giá cao.
Lưu ý: Tôi đang sử dụng tệp "nhỏ hơn" này làm ví dụ. Tôi thực sự cố gắng để tải một tập tin 320 MB, nhưng Delphi là yêu cầu cho hơn 2 GB RAM và hết bộ nhớ vì yêu cầu chuỗi dư thừa này.
Addenum: Marco Cantu vừa ra mắt với a White Paper on Delphi and Unicode. Delphi 2009 đã tăng chi phí cho mỗi chuỗi từ 8 byte lên 12 byte (cộng thêm có thể là 4 cho con trỏ thực tế vào chuỗi). Thêm 16 byte cho mỗi 17x2 = 34 byte thêm gần 50%. Nhưng tôi thấy hơn 200% chi phí. 150% có thể là gì?
Thành công !! Cảm ơn tất cả các bạn đã đề xuất. Tất cả các bạn đã cho tôi suy nghĩ. Nhưng tôi sẽ phải cung cấp cho Jan Goyvaerts tín dụng cho câu trả lời, kể từ khi ông hỏi:
... tại sao bạn sử dụng TStringList? Phải tệp thực sự được lưu trữ trong bộ nhớ dưới dạng các dòng riêng biệt?
Điều đó dẫn tôi đến giải pháp thay vì tải tệp 24 MB dưới dạng chuỗi 1,4 triệu StringList, tôi có thể nhóm các dòng của mình thành các nhóm tự nhiên mà chương trình của tôi biết. Vì vậy, điều này dẫn đến 127.000 dòng được tải vào danh sách chuỗi.
Bây giờ mỗi dòng trung bình 190 ký tự thay vì 17. Chi phí trên mỗi dòng StringList là giống nhau nhưng bây giờ có nhiều dòng ít hơn.
Khi tôi áp dụng điều này vào tệp 320 MB, nó không còn hết bộ nhớ và hiện tải trong chưa đến 1 GB RAM. (Và nó chỉ mất khoảng 10 giây để tải, đó là khá tốt!)
Sẽ có thêm một chút xử lý để phân tích các dòng được nhóm, nhưng không đáng chú ý trong quá trình xử lý thời gian thực của từng nhóm.
(Trong trường hợp bạn tự hỏi, đây là chương trình phả hệ, và đây có thể là bước cuối cùng tôi cần để cho phép tải tất cả dữ liệu về một triệu người trong không gian địa chỉ 32 bit trong vòng chưa đầy 30 giây Vì vậy, tôi vẫn còn có một bộ đệm 20 giây để chơi với để thêm các chỉ mục vào dữ liệu sẽ được yêu cầu để cho phép hiển thị và chỉnh sửa dữ liệu.)
Bạn đo lường bộ nhớ như thế nào? Tôi hy vọng không có cột Mem Usage từ Task Manager. Đó không phải là những gì bạn nghĩ. –
Để đo bộ nhớ, tôi sử dụng GlobalMemoryStatusEx. Xem: http://msdn.microsoft.com/en-us/library/aa366589(VS.85).aspx – lkessler
Bạn nên kiểm tra dung lượng bộ nhớ thực sự được sử dụng trong Delphi. Các Delphi MM sẽ suballocate các khối lớn hơn nó thu được từ hệ điều hành, và phát hành chúng cho hệ điều hành chỉ khi có thể (phân mảnh và như thế có thể phủ nhận nó), vì vậy những gì Windows nhìn thấy và những gì Delphi không có thể khác nhau. Nếu bạn sử dụng thư viện FastMM đầy đủ có sẵn từ Sourceforge, nó có các cơ sở để truy vấn phân bổ MM cho bạn cái nhìn sâu hơn về những gì đang diễn ra. Nếu không, bạn có thể sử dụng một trình thông báo bộ nhớ (tức là AQTime) để kiểm tra nó và xem bộ nhớ được phân bổ, khi nào và tại sao. –