Làm thế nào để bạn chuẩn bị cho ra khỏi điều kiện bộ nhớ?


18

Điều này có thể dễ dàng đối với các trò chơi có phạm vi được xác định rõ, nhưng câu hỏi là về các trò chơi hộp cát, nơi người chơi được phép tạo và xây dựng bất cứ thứ gì .

Kỹ thuật có thể:

  • Sử dụng nhóm bộ nhớ với giới hạn trên.
  • Xóa các đối tượng không còn cần thiết theo định kỳ.
  • Phân bổ thêm lượng bộ nhớ lúc đầu để có thể giải phóng sau này như một cơ chế phục hồi. Tôi sẽ nói khoảng 2-4 MB.

Điều này có nhiều khả năng xảy ra trong các nền tảng di động / bảng điều khiển nơi bộ nhớ thường bị giới hạn không giống như PC 16 GB của bạn. Tôi giả sử bạn có toàn quyền kiểm soát phân bổ / giải quyết bộ nhớ và không có bộ sưu tập rác liên quan. Đó là lý do tại sao gắn thẻ này là C ++.

Xin lưu ý rằng tôi không nói về C ++ hiệu quả 7 Mục "Hãy chuẩn bị cho tình trạng hết bộ nhớ" , mặc dù nó có liên quan, tôi muốn thấy một câu trả lời liên quan nhiều hơn đến phát triển trò chơi, nơi bạn thường kiểm soát nhiều hơn những gì xảy ra

Để tóm tắt câu hỏi, làm thế nào để bạn chuẩn bị ra khỏi điều kiện bộ nhớ cho các trò chơi hộp cát, khi bạn đang nhắm mục tiêu một nền tảng với bảng điều khiển bộ nhớ / di động hạn chế?


Phân bổ bộ nhớ không thành công là khá hiếm trên các hệ điều hành PC hiện đại, bởi vì chúng sẽ tự động trao đổi sang ổ cứng khi hết RAM vật lý. Vẫn là một tình huống mà nên tránh, vì trao đổi là nhiều chậm hơn so với RAM vật lý và sẽ ảnh hưởng nghiêm trọng hiệu suất.
Philipp

@Philipp vâng tôi biết. Nhưng câu hỏi của tôi là nhiều hơn đối với các thiết bị giới hạn bộ nhớ như máy chơi game và điện thoại di động, tôi nghĩ rằng tôi đã đề cập đến điều đó.
Concept3d

Đây là một câu hỏi khá rộng (và một cuộc thăm dò ý kiến ​​theo cách nó được diễn đạt). Bạn có thể thu hẹp phạm vi xuống một chút để cụ thể hơn cho một tình huống không?
MichaelHouse

@ Byte56 Tôi đã chỉnh sửa câu hỏi. Tôi hy vọng nó có phạm vi được xác định nhiều hơn bây giờ.
Concept3d

Câu trả lời:


16

Nói chung, bạn không xử lý hết bộ nhớ. Tùy chọn lành mạnh duy nhất trong phần mềm lớn và phức tạp như một trò chơi là chỉ cần sập / xác nhận / chấm dứt trong bộ cấp phát bộ nhớ của bạn càng sớm càng tốt (đặc biệt là trong các bản dựng gỡ lỗi). Các điều kiện hết bộ nhớ được kiểm tra và xử lý trong một số phần mềm hệ thống lõi hoặc phần mềm máy chủ trong một số trường hợp nhưng không thường ở nơi khác.

Thay vào đó, khi bạn có nắp bộ nhớ trên, bạn chỉ cần đảm bảo rằng bạn không bao giờ cần nhiều hơn số lượng bộ nhớ đó. Chẳng hạn, bạn có thể giữ số lượng NPC được phép tối đa tại một thời điểm và chỉ cần ngừng sinh ra các NPC không thiết yếu mới sau khi giới hạn đó được thực hiện. Đối với các NPC thiết yếu, bạn có thể yêu cầu chúng thay thế các NPC không thiết yếu hoặc có một nhóm / nắp riêng cho các NPC thiết yếu mà các nhà thiết kế của bạn biết để thiết kế xung quanh (ví dụ: nếu bạn chỉ có thể có 3 NPCsa thiết yếu, các nhà thiết kế sẽ không đặt nhiều hơn 3 một khu vực / khối - công cụ tốt sẽ giúp các nhà thiết kế thực hiện điều này đúng cách và tất nhiên việc kiểm tra là điều cần thiết).

Một hệ thống truyền phát thực sự tốt cũng rất quan trọng đặc biệt đối với các trò chơi hộp cát. Bạn không cần phải giữ tất cả các NPC và vật phẩm trong bộ nhớ. Khi bạn di chuyển qua các khối của thế giới, các đoạn mới sẽ được truyền vào và các đoạn cũ được truyền ra ngoài. Chúng thường bao gồm các NPC và vật phẩm cũng như địa hình. Cần thiết lập giới hạn thiết kế và kỹ thuật cho các giới hạn vật phẩm với hệ thống này, vì biết rằng hầu hết các khối X cũ sẽ được giữ xung quanh và các khối Y mới được tích cực tải sẽ được tải, do đó trò chơi cần có không gian để giữ tất cả dữ liệu của các khối X + Y + 1 trong bộ nhớ.

Một số trò chơi cố gắng xử lý các tình huống hết bộ nhớ bằng cách tiếp cận hai lượt. Hãy nhớ rằng hầu hết các trò chơi có nhiều dữ liệu được lưu trong bộ nhớ cache không cần thiết về mặt kỹ thuật (giả sử, các đoạn cũ được đề cập ở trên) và phân bổ bộ nhớ có thể làm một cái gì đó như:

allocate(bytes):
  if can_allocate(bytes):
    return internal_allocate(bytes)
  else:
    warning(LOW_MEMORY)
    tell_systems_to_dump_caches()

    if can_allocate(bytes):
      return internal_allocate(bytes)
    else:
      fatal_error(OUT_OF_MEMORY)

Đây là một biện pháp cuối cùng để xử lý các tình huống bất ngờ khi phát hành nhưng trong quá trình gỡ lỗi và kiểm tra có lẽ bạn nên ngay lập tức gặp sự cố. Bạn không muốn phải phụ thuộc vào loại công cụ này (đặc biệt là vì việc bỏ bộ đệm có thể gây ra một số hậu quả nghiêm trọng về hiệu suất).

Bạn cũng có thể xem xét việc bỏ các bản sao có độ phân giải cao của một số dữ liệu, ví dụ, bạn có thể kết xuất các mức kết cấu mipmap có độ phân giải cao hơn nếu bạn sắp hết bộ nhớ GPU (hoặc bất kỳ bộ nhớ nào trong kiến ​​trúc bộ nhớ dùng chung). Điều này thường đòi hỏi rất nhiều công việc kiến ​​trúc để làm cho giá trị nó, mặc dù.

Lưu ý rằng một số trò chơi hộp cát không giới hạn có thể dễ dàng bị sập, ngay cả trên PC (hãy nhớ rằng các ứng dụng 32 bit thông thường có giới hạn 2-3 GB không gian địa chỉ ngay cả khi bạn có PC có 128GB RAM; 64- HĐH bit và phần cứng cho phép nhiều ứng dụng 32 bit chạy đồng thời nhưng không thể làm gì để nhị phân 32 bit có không gian địa chỉ lớn hơn). Cuối cùng, bạn có một thế giới trò chơi rất linh hoạt, sẽ cần không gian bộ nhớ không giới hạn để chạy trong mọi trường hợp hoặc bạn có một thế giới rất hạn chế và bị kiểm soát, luôn hoạt động hoàn hảo trong bộ nhớ bị chặn (hoặc một cái gì đó ở giữa).


+1 cho câu trả lời này. Tôi đã viết hai hệ thống hoạt động theo phong cách của Sean và các nhóm bộ nhớ riêng biệt và cả hai đều kết thúc hoạt động tốt trong sản xuất. Đầu tiên là một người sinh sản đã quay ngược đầu ra trên một đường cong đến mức tắt giới hạn tối đa để người chơi sẽ không bao giờ nhận thấy sự giảm đột ngột (nghĩ rằng tổng thông lượng đã được hạ thấp bởi mức an toàn đó). Thứ hai là liên quan đến các khối trong đó một sự phân bổ thất bại sẽ buộc thanh trừng và tái phân bổ. Tôi cảm thấy rằng ** một thế giới rất hạn chế và có kiểm soát, luôn hoạt động hoàn hảo trong bộ nhớ bị chặn ** là điều tối quan trọng đối với bất kỳ khách hàng nào đang chạy dài.
Patrick Hughes

+1 để đề cập đến việc tích cực xử lý lỗi trong các bản dựng gỡ lỗi càng tốt. Hãy nhớ rằng trên phần cứng bảng điều khiển gỡ lỗi, đôi khi bạn có quyền truy cập vào nhiều tài nguyên hơn bán lẻ. Bạn có thể muốn bắt chước các điều kiện đó trên phần cứng dev bằng cách phân bổ các đối tượng gỡ lỗi dành riêng cho không gian địa chỉ trên các thiết bị bán lẻ sẽ gặp sự cố và sử dụng hết khi không gian địa chỉ tương đương bán lẻ được sử dụng.
FlintZA

5

Ứng dụng thường được thử nghiệm trên nền tảng được nhắm mục tiêu với các tình huống xấu nhất và bạn sẽ luôn được chuẩn bị cho nền tảng mà bạn đang nhắm mục tiêu. Lý tưởng nhất là ứng dụng không bao giờ bị sập, nhưng ngoài việc tối ưu hóa cho các thiết bị cụ thể, có rất ít sự lựa chọn khi bạn phải đối mặt với cảnh báo bộ nhớ thấp.

Cách thực hành tốt nhất là có các hồ bơi được sắp xếp sẵn và trò chơi sử dụng ngay từ đầu tất cả các bộ nhớ cần thiết. Nếu trò chơi của bạn có tối đa 100 đơn vị thì có một nhóm cho 100 đơn vị và đó là trò chơi. Nếu 100 đơn vị vượt quá yêu cầu mem cho một thiết bị được nhắm mục tiêu thì bạn có thể tối ưu hóa đơn vị để sử dụng ít bộ nhớ hơn hoặc thay đổi thiết kế thành tối đa 90 đơn vị. Không nên có trường hợp bạn có thể xây dựng những thứ không giới hạn, luôn luôn phải có một giới hạn. Sẽ rất tệ cho một trò chơi hộp cát để sử dụng newcho từng trường hợp bởi vì bạn không bao giờ có thể dự đoán việc sử dụng mem và sự cố là tồi tệ hơn nhiều so với giới hạn.

Ngoài ra, thiết kế trò chơi phải luôn luôn ghi nhớ các thiết bị được nhắm mục tiêu thấp nhất bởi vì nếu bạn đặt thiết kế của mình với những thứ "không giới hạn" thì sẽ khó khăn hơn rất nhiều để giải quyết các vấn đề về bộ nhớ hoặc thay đổi thiết kế sau này.


1

Chà, bạn có thể phân bổ khoảng 16 MiB (chỉ cần chắc chắn 100%) khi khởi động hoặc thậm chí vào .bssthời gian biên dịch và sử dụng "phân bổ an toàn", với một chữ ký như inline __attribute__((force_inline)) void* alloc(size_t size)( __attribute__((force_inline))là một mingw-w64thuộc tính GCC buộc nội tuyến của các phần mã quan trọng ngay cả khi tối ưu hóa bị vô hiệu hóa, ngay cả khi chúng nên được bật cho trò chơi) thay vì mallocthử void* result = malloc(size)và nếu thất bại, hãy bỏ bộ nhớ cache, giải phóng bộ nhớ dự phòng (hoặc nói mã khác để sử dụng .bssnhưng không nằm ngoài phạm vi của câu trả lời này) và tuôn ra dữ liệu chưa được lưu (lưu thế giới vào đĩa, nếu bạn sử dụng khái niệm khối giống như Minecraft, hãy gọi một cái gì đó như thế saveAllModifiedChunks()). Sau đó, nếu malloc(16777216)(phân bổ lại 16 MiB này) không thành công (một lần nữa, thay thế bằng analog cho .bss), chấm dứt trò chơi và hiển thịMessageBox(NULL, "*game name* couldn't continue because of lack of free memory, but your world was safely saved. Try closing background applications and restarting the game", "*Game name*: out of memory", MB_ICONERROR)hoặc một nền tảng thay thế cụ thể. Để tất cả chúng cùng nhau:

__attribute__((force_inline)) void* alloc(size_t size) {
    void* result = malloc(size); // Attempt to allocate normally
    if (!result) { // If the allocation failed...
        if (!reserveMemory) std::_Exit(); // If alloc() was called from forceFullSave() or reportOutOfMemory() and we again can't allocate, just quit, something is stealing all our memory. If we used the .bss approach, this wouldn't've been necessary.
        free(reserveMemory); // Global variable, pointer to the reserve 16 MiB allocated on startup
        forceFullSave(); // Saves the game
        reportOutOfMemory(); // Platform specific error message box code
        std::_Exit(); // Close silently
    } else return result;
}

Bạn có thể sử dụng một giải pháp tương tự với std::set_new_handler(myHandler)nơi myHandlerđược void myHandler(void)gọi là khi newthất bại:

void newerrhandler() {
    if (!reserveMemory) std::_Exit(); // If new was called from forceFullSave() or reportOutOfMemory() and we again can't allocate, just quit, something is stealing all our memory. If we used the .bss approach, this wouldn't've been necessary.
    free(reserveMemory); // Global variable, pointer to the reserve 16 MiB allocated on startup
    forceFullSave(); // Saves the game
    reportOutOfMemory(); // Platform specific error message box code
    std::_Exit(); // Close silently
}

// In main ()...
std::set_new_handler(newerrhandler);
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.