Điều này có vẻ tuyệt vời, xem xét truy cập nhanh cho một thành phần bằng cách sử dụng id thực thể và quan trọng hơn là lặp lại nhanh các thành phần của mảng (vì các hệ thống làm điều này mọi lúc) và ít bộ nhớ cache hơn so với khi tôi lưu trữ các thành phần trong Thực thể hoặc sử dụng:
Bạn đã thực sự đo nhớ cache? Có phải họ là một vấn đề đối với bạn? Sẽ loại bỏ chúng cho truy cập thành phần của bạn có thể có tác động có thể đo lường được? Có đáng để bạn dành thời gian tối ưu hóa để lo lắng về chúng thay vì tập trung vào sự song song hoặc sử dụng GPU tốt hơn không? Bạn có ít nhất có các phép đo mã của mình trước bất kỳ thay đổi nào như vậy để bạn có thể so sánh chúng với các phép đo từ sau khi thay đổi không?
Đơn giản chỉ cần đặt mọi thứ vào một mảng cũng không đảm bảo sẽ bỏ lỡ bộ nhớ cache ít hơn. Đây là bước đầu tiên của một số để cải thiện việc sử dụng bộ đệm cho các thành phần. Nó có thể là một bước sai để cải thiện hiệu suất tổng thể của bạn, quá.
1) Tôi có phải tạo mảng cho từng thành phần không? Cái gì đó như:
PositionComponent positionComponents[MAX_OBJECTS];
HealthComponent healthComponents[MAX_OBJECTS];
Một std::vector
hoặc tương tự sẽ làm việc như là tốt. Lưu ý rằng như làvector
có thể phân bổ lại bộ lưu trữ sao lưu của nó và làm mất hiệu lực các trình lặp / con trỏ hiện có, bạn sẽ phải sử dụng các chỉ mục thành phần thay vì con trỏ.
Bạn cũng có thể dễ dàng viết một phương tiện lưu trữ chunked (như một std::deque
, nhưng không được tối ưu hóa cho hàng đợi) cho phép cấu trúc phát triển, giữ tốc độ lặp nhanh và có con trỏ ổn định.
2) Làm thế nào tôi có thể nhận được các thành phần bằng cách sử dụng id thực thể nếu tôi làm điều này?
template <class T>
T* getComponent(int entityId) const {
... // ??
}
Bạn có thể lưu trữ một bảng băm bổ sung boost::flat_map
hoặc std::unordered_map
hoặc hiệu suất cao tùy chỉnh ánh xạ ID thực thể thành các chỉ số thành phần cho loại thành phần đó.
Có lẽ bạn sẽ chỉ muốn ánh xạ tới một chỉ mục hoặc một con trỏ tạm thời (một con trỏ mà bạn hứa sẽ không bao giờ giữ lại) vì điều đó cho phép chủ sở hữu thành phần chống phân mảnh bộ sưu tập thành phần của nó. Cả hai đều có khả năng cho phép chủ sở hữu giải phóng các khối bộ nhớ và quan trọng hơn (đối với các nhu cầu hướng dữ liệu) là điều cần thiết để thực sự làm cho việc lặp lại các thành phần nhanh hơn. Nó có thể sẽ nhanh hơn nhiều để sử dụng phương pháp tiếp cận hiện tại của bạn so với việc lặp đi lặp lại trên một mảng lớn và dân cư thưa thớt. Hãy nhớ rằng với một mảng thưa thớt (ngay cả khi chiếm 99%), bạn phải thêm một mảng bổ sungif (isInUse)
kiểm tra bên trong vòng lặp của bạn qua các thành phần là một lệnh đọc + nhánh bổ sung có thể có tác động tiêu cực đáng chú ý đến tốc độ lặp chung của bạn.
Xem bốn bài viết của BitSquid về chủ đề này . Những kẻ đó có xu hướng tập trung vào mã giống như C và tránh các cách tiếp cận C ++ hiện đại với hiệu suất giống hệt nhau nhưng sử dụng dễ dàng hơn, nhưng các cách tiếp cận cơ bản mà họ sử dụng được áp dụng trong C, C ++, C #, Rust hoặc các ngôn ngữ tương tự.