Việc phân bổ và giải phóng một khối lớn bộ nhớ khi khởi động có thể dọn sạch bộ nhớ không?


18

Cuốn sách Hoàn thành mã hóa trò chơi, Phiên bản thứ tư , chương 5 ( Khởi tạo và tắt máy trò chơi ), phần Kiểm tra bộ nhớ chứa mẫu mã thú vị này:

bool CheckMemory(const DWORDLONG physicalRAMNeeded, const DWORDLONG virtualRAMNeeded)
{
    MEMORYSTATUSEX status; 
    GlobalMemoryStatusEx(&status);
    if (status.ullTotalPhys < physicalRAMNeeded) 
    {
        // you don’t have enough physical memory. Tell the player to go get a 
        // real computer and give this one to his mother. 
        GCC_ERROR("CheckMemory Failure: Not enough physical memory."); 
        return false;
    }
    // Check for enough free memory.
    if (status.ullAvailVirtual < virtualRAMNeeded) 
    {
        // you don’t have enough virtual memory available.
        // Tell the player to shut down the copy of Visual Studio running in the 
        // background, or whatever seems to be sucking the memory dry. 
        GCC_ERROR("CheckMemory Failure: Not enough virtual memory.");
        return false;
    }

    char *buff = GCC_NEW char[virtualRAMNeeded]; 
    if (buff)
    {
        delete[] buff;
    }
    else
    {
        // even though there is enough memory, it isn't available in one
        // block, which can be critical for games that manage their own memory 
        GCC_ERROR("CheckMemory Failure: Not enough contiguous memory."); 
        return false;
    }
}

Điều này đặt ra một số câu hỏi.

Phần đầu tiên chỉ hỏi HĐH (Windows) có bao nhiêu RAM vật lý. Phần gây tò mò là phần thứ hai, phân bổ một khối lớn bộ nhớ và giải phóng nó ngay lập tức:

char *buff = GCC_NEW char[virtualRAMNeeded]; 
if (buff)
{
    delete[] buff;
}

Tác giả tiếp tục giải thích:

... chức năng này phân bổ và ngay lập tức giải phóng một khối bộ nhớ khổng lồ. Điều này có tác dụng làm cho Windows dọn sạch mọi rác rưởi tích lũy trong trình quản lý bộ nhớ và kiểm tra hai lần để bạn có thể phân bổ một khối liền kề lớn như bạn cần. Nếu cuộc gọi thành công, về cơ bản, bạn đã chạy tương đương với máy Zamboni thông qua bộ nhớ hệ thống của bạn, để sẵn sàng cho trò chơi của bạn chạm vào ...

Nhưng tôi có đặt phòng của tôi về điều đó.

"Làm sạch rác đã tích lũy trong trình quản lý bộ nhớ?" Có thật không? Nếu trò chơi vừa mới bắt đầu, không nên có rác?

"Đảm bảo rằng bạn có thể phân bổ một khối liền kề?" Trong trường hợp rất cụ thể mà bạn sẽ tự mình quản lý bộ nhớ, điều này sẽ có ý nghĩa, nhưng vẫn vậy, nếu bạn phân bổ nhiều bộ nhớ cho con dơi, bạn sẽ không thể chạy bất kỳ ứng dụng nào khác. hệ thống trong khi bạn đang bật.

Ngoài ra, điều này có khả năng buộc HĐH phải cam kết tất cả bộ nhớ đó hay không, và do đó, sẽ khiến rất nhiều bộ nhớ vào không gian đĩa trao đổi, làm chậm quá trình khởi động ứng dụng của bạn rất nhiều?

Đây thực sự là một thực hành tốt?


3
Hầu hết các hệ điều hành hiện đại sẽ làm gì nào khi một ứng dụng giao đất một vùng rộng lớn của bộ nhớ, họ sử dụng phân bổ lạc quan và không thực sự làm bất cứ điều gì cho đến khi bạn điền vào bộ nhớ, tôi không thể tưởng tượng này làm bất cứ điều gì hơn là một khả năng chậm không-op
Vality

Liệu việc dọn sạch một dải đất dài trong rừng rậm, khắc radio, tai nghe, v.v ... bằng gỗ có khiến máy bay hạ cánh và cung cấp vật tư không?
Dan Neely

10
Mã hóa Toàn bộ trò chơi có chứa rất nhiều thứ vô nghĩa kết hợp với rất nhiều không-hiểu-C ++ và C-that-vẻ-một-bit-như-C ++ (cũng chứng minh trong mẫu này, kiểm tra kết quả của việc operator newcho nullptr), nếu bạn cho phép tôi để nói. Điều tốt nhất bạn có thể làm với cuốn sách đó là thắp sáng ống khói của bạn. Phân bổ và giải phóng một khối lớn bộ nhớ tất nhiên không "dọn sạch" bộ nhớ.
Damon

@Damon, tôi đã không kiểm tra, nhưng tôi nghi ngờ ít nhất họ đã quá tải newnhà điều hành toàn cầu để trả về null thay vì ném bad_alloc. Nếu họ không, thì có, mã này thậm chí còn vô nghĩa hơn: P
glampert

1
@glampert: Ngay cả khi cho rằng đó là trường hợp, operator deleteđược yêu cầu chấp nhận nullptrvà coi nó là không-op. Bất kỳ quá tải toàn cầu không làm điều đó là bị hỏng. Điều đó có nghĩa là nó vô nghĩa theo một trong hai cách. Cũng giống như giả định rằng việc phân bổ một khối lớn bộ nhớ và giải phóng nó sẽ "kỳ diệu" làm điều gì đó tốt. Tốt nhất, nó sẽ không gây hại gì (rất có thể, vì các trang thậm chí không được chạm vào ... nếu không nó có thể hoán đổi một số trang từ bộ công việc của bạn mà bạn sẽ cần tải lại sau).
Damon

Câu trả lời:


12

Một điều mà việc phân bổ trước một khối lớn bộ nhớ có thể làm, nếu máy tính của bạn thiếu RAM, có thể buộc HĐH giải phóng thêm dung lượng bằng cách hoán đổi các phần của không gian bộ nhớ của các chương trình khác vào đĩa.

Vì việc hoán đổi như vậy nói chung là một hoạt động rất chậm, đóng băng khá nhiều chương trình của bạn trong khi nó đang diễn ra, có thể có một số lợi thế để đảm bảo rằng, nếu điều đó xảy ra dù sao đi nữa, nó sẽ xảy ra trước khi trò chơi của bạn bắt đầu thay vì ở giữa trò chơi.

Điều đó nói rằng, việc buộc một trao đổi vào đĩa như thế này không chính xác đáng tin cậy 100%:

  • Trên nhiều triển khai bộ nhớ ảo, chỉ phân bổ bộ nhớ không thực sự kích hoạt bất kỳ sự hoán đổi nào; thay vào đó, nó chỉ xảy ra khi bạn truy cập lần đầu vào từng trang của bộ nhớ bạn đã phân bổ. (Điều này chắc chắn đúng với các phiên bản Linux hiện đại, với bộ nhớ được kích hoạt vượt mức, vì nó là mặc định; tôi không biết chắc chắn các phiên bản Windows khác nhau xử lý nó như thế nào.)

    Do đó, nếu bạn thực sự muốn mã này "dọn sạch bộ nhớ", có lẽ bạn nên thêm một memset()cuộc gọi hoặc tương đương với việc thực sự ghi dữ liệu vào mọi phần của mảng (hoặc ít nhất là các physicalRAMNeededbyte đầu tiên của nó) trước khi phát hành nó.

  • Ngoài ra, việc buộc HĐH phải hoán đổi các chương trình khác ra khỏi RAM như thế này không có nghĩa là họ sẽ đứng ngoài cuộc. Windows là một hệ điều hành đa nhiệm và ngay khi một trong những chương trình bị tráo đổi đó muốn chạy lại và sử dụng dữ liệu bị tráo đổi của nó, nó sẽ bị tráo đổi, có khả năng đóng băng trò chơi của bạn một lần nữa.

    Do đó, thủ thuật này thường chỉ hữu ích nếu RAM bị ăn mòn bởi những khối dữ liệu lớn không được truy cập tích cực (chẳng hạn như các tài liệu lớn bị bỏ ngỏ trong nền trong một số phần mềm chỉnh sửa). Các trình duyệt web có xu hướng đặc biệt có vấn đề về vấn đề này, vì ngay cả khi bạn không thực sự sử dụng một trang web được mở trong một số tab nền, trang vẫn có thể có các tập lệnh chạy trên đó buộc nó phải được giữ trong RAM.

    (Có nhiều cách để một chương trình thực sự yêu cầu HĐH khóa một phần không gian bộ nhớ của nó vào RAM và ngăn không cho nó bị tráo đổi, nhưng những cách này thường yêu cầu các đặc quyền nâng cao. Trong mọi trường hợp, mã bạn đã đăng không xuất hiện sử dụng bất kỳ phương pháp nào như vậy.)

Về cơ bản, thủ thuật này dường như chỉ hữu ích trong tình huống đường biên tương đối hẹp mà người dùng sẽ có đủ RAM để chạy trò chơi của bạn (và bất kỳ phần mềm nào khác đang chạy tích cực trong nền) mà không bị tráo đổi, nhưng RAM hiện chứa đầy các tệp mở không hoạt động và không sử dụng hoặc dữ liệu khác có thể và nên được hoán đổi vào đĩa trong khi trò chơi của bạn đang chạy. Trong những tình huống như vậy, nó có thể có hiệu quả trong việc làm cho trò chơi của bạn có vẻ mượt mà và phản ứng nhanh hơn, bằng cách thay thế các hoạt động hoán đổi nhỏ thường xuyên trong khi chơi trò chơi bằng một lần duy nhất trong khi khởi động.


1
Từ kiến ​​thức hạn chế của tôi về quản lý bộ nhớ cấp thấp, tôi có thể nói rằng quyết định có hay không trao đổi một trang cụ thể phức tạp hơn nhiều và xem xét nhiều yếu tố hơn là chỉ đơn giản là dung lượng bộ nhớ được yêu cầu và dung lượng bộ nhớ khả dụng . Đơn giản hóa việc này, và nói chung dựa vào các giả định không phải là rất khôn ngoan.
Gấu trúc gấu trúc

3
Tôi nghĩ điều quan trọng cần nhớ là tất cả những điều này là giả định có thể hoạt động, không hoạt động hoặc dường như hoạt động, nhưng vì những lý do hoàn toàn không liên quan. Theo như tôi biết, không có hành vi nào được chỉ ra trong câu trả lời này được ghi lại, và sẽ không khôn ngoan khi dựa vào chúng. Một bản cập nhật hệ điều hành, thay đổi cài đặt, thay đổi trình biên dịch hoặc khá nhiều thứ có thể khiến điều này ngừng hoạt động hoặc thậm chí có tác động tiêu cực đến hiệu suất.
Gấu trúc gấu trúc

26

Tôi không biết bài viết đó bao nhiêu tuổi, nhưng tôi nói nó khá cũ. Trong Windows hiện đại (XP và mới hơn, nhưng đặc biệt trên các phiên bản 64 bit), tôi sẽ nói rằng làm một việc như thế sẽ có ít tác động tiêu cực đến việc khởi động trò chơi của bạn.

Trước hết, hãy nhớ rằng không gian địa chỉ được ảo hóa cho mỗi quy trình. Theo như quá trình của bạn, bạn có toàn bộ không gian địa chỉ và bạn sẽ luôn có được bộ nhớ (ảo) liền kề, bất kể bộ nhớ đó có tiếp giáp vật lý hay không.

Trong thực tế, việc bộ nhớ có liền kề trong bộ nhớ vật lý hay không không phải là thứ bạn có quyền kiểm soát. HĐH sẽ chọn cách phân bổ các khối vật lý khi thấy phù hợp và không có gì bạn làm ở cấp ứng dụng có thể ảnh hưởng đến lợi ích (nếu có) của việc có bộ nhớ vật lý liền kề.

Bạn cần quan tâm đến việc phân mảnh bộ nhớ ảo nếu bạn tiếp tục phân bổ và giải phóng bộ nhớ có kích thước khác nhau trong một khoảng thời gian rất dài. Điều này tất nhiên ít liên quan hơn với không gian địa chỉ 64 bit. Các trò chơi thường không chạy trong nhiều tháng, vì vậy rất có thể bạn sẽ không phải quan tâm đến vấn đề này, trừ khi chúng ta đang nói về các máy chủ trò chơi chạy dài.

Ngay cả khi bạn phân bổ nhiều bộ nhớ có kích thước cực kỳ khác nhau trong máy 32 bit, phân bổ bộ nhớ hiện đại là khá tốt, do đó không có khả năng bạn sẽ gặp sự cố phân mảnh bộ nhớ ảo trừ khi bạn chủ động tìm kiếm chúng

Thành thật với bạn, tôi không biết nhà văn đó đang làm gì. Ngay cả khi không gian địa chỉ được chia sẻ và bạn đã có được một khối lớn tiếp giáp trong bộ nhớ vật lý, bằng cách giải phóng nó, bạn đang nói rằng bạn không quan tâm đến nó nữa. Có những chương trình khác đang chạy trên nền thực hiện phân bổ của riêng họ, vì vậy ngay cả khi malloc khổng lồ của bạn thành công, không ai đảm bảo chương trình tiếp theo cũng sẽ thành công.

Tôi nghĩ đó là một trường hợp "nó hoạt động vì một số lý do tôi không thực sự hiểu, vì vậy tôi đã tạo ra một cái gì đó để giải thích nó".


10
RAM không giống như một đĩa cứng quay trong đó có các khối liền kề theo bất kỳ cách nào "tốt" hơn không. Đây không phải là sự thật. truy cập RAM liền kề là một thứ tự có độ lớn nhanh hơn truy cập ngẫu nhiên. Các chip RAM được chia thành các phần có độ trễ nhất định để chuyển đổi và quan trọng hơn, các lỗi bộ nhớ cache L1 và L2 sẽ ảnh hưởng mạnh đến hiệu suất của bạn khi bộ nhớ bị phân tán.
CaptainCodeman

@CaptainCodeman: Tôi phải chấp nhận đi ra khỏi lĩnh vực kiến ​​thức của mình, nhưng bằng mọi cách, với tư cách là một lập trình viên Windows, ứng dụng bạn không kiểm soát được liệu bộ nhớ của bạn có tiếp giáp vật lý hay không. Yêu cầu một khối lớn bộ nhớ sẽ không thay đổi nhiều.
Panda Pyjama

@CaptainCodeman thực sự có khối ảo liền kề nằm trong khối mod 2 ^ N liền kề trong RAM sẽ tăng tốc do phân bổ dòng bộ đệm
ratchet freak

8
@CaptainCodeman Vâng, truy cập DRAM tuần tự nhanh hơn, nhưng chúng tôi đang nói về ảo -> ánh xạ vật lý xảy ra trong các trang 4 KB (trở lên), do đó, ánh xạ đó có tuần tự hay không không ảnh hưởng nhiều đến bộ nhớ đọc tốc độ. Thêm vào đó, tìm nạp trước bộ đệm và những thứ như thế hoạt động trong không gian địa chỉ ảo, không phải vật lý.
Nathan Reed

1
@NathanReed Đủ công bằng, và cảm ơn vì đã làm rõ. Tôi chỉ bày tỏ rằng tốc độ truy cập RAM không hoàn toàn đồng nhất trên không gian bộ nhớ, như đã đề xuất.
CaptainCodeman
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.