Có cách nào tốt hơn để thiết lập một hệ thống sự kiện?


9

Hệ thống sự kiện thật tuyệt vời, chúng tạo ra mã cực kỳ khó sử dụng và thực sự cho phép tạo ra các trò chơi năng động thông qua việc giao tiếp dễ dàng với các đối tượng và vòng lặp trò chơi. Tôi đang có một thời gian khó khăn với hiệu quả của việc thực hiện hiện tại của tôi. Hiện tại tôi tối ưu hóa một chút về việc tách danh sách các đối tượng thành các sự kiện mà họ phản ứng đã làm nên điều kỳ diệu, nhưng tôi cần phải làm nhiều hơn nữa.

Hiện tại tôi có hai phương pháp:

  1. Đơn giản nhất: tất cả các đối tượng được thêm vào một vectơ khi một sự kiện được gửi, tất cả các đối tượng được gửi sự kiện thông qua phương thức handle_event ()

  2. Phức tạp hơn: Tôi có một bản đồ với chuỗi là khóa và int là giá trị của nó. Khi một loại sự kiện được thêm vào, nó được thêm vào bản đồ này, với int đơn giản được tăng lên (phải có cách tốt hơn)
    vectơ vectơ của các đối tượng sau đó đẩy lùi một vectơ mới để xử lý loại sự kiện đó.
    Khi một sự kiện được gọi, nó chỉ đơn giản gọi int tương ứng trong ánh xạ eventTypes tới kiểu bên trong vectơ của vectơ của các đối tượng và gửi sự kiện đó đến từng đối tượng xử lý loại sự kiện đó.

Các phương thức đầu tiên này khá chậm (rõ ràng) đối với nhiều đối tượng, nhưng khá nhanh đối với rất ít đối tượng. Trong khi đó phương thức thứ hai khá nhanh với các đối tượng lớn muốn xử lý các loại sự kiện khác nhau, nhưng chậm hơn phương thức đầu tiên trên mỗi đối tượng với các đối tượng xử lý cùng loại sự kiện.

Có cách nào nhanh hơn (chạy theo thời gian) không? Có cách nào nhanh hơn để tra cứu một int từ kiểu chuỗi không? (Ban đầu tôi có một enum, nhưng nó không cho phép các loại tùy chỉnh, điều này cần thiết vì mức độ năng động mong muốn.)


1
Đây là loại mà Hash Maps (hoặc Bảng Hash) dành cho, chuỗi được tính thành số băm mà sau đó được sử dụng để tra cứu trực tiếp trong một mảng. vi.wikipedia.org/wiki/Hash_table
Patrick Hughes

Một câu hỏi hay là: đây thực sự là một nút cổ chai hiệu suất, hay bạn chỉ đang lo lắng sớm?
Jari Komppa

Nó đã được thực hiện và có một nút cổ chai, khi nhiều đối tượng được sử dụng. Nhận xét trước đây của tôi về việc thiếu nút cổ chai không nằm trong chính ứng dụng mà thực sự là sự khác biệt về tốc độ giữa hai lần triển khai ở trên nơi có cùng số lượng đối tượng được xử lý. Tôi đã hy vọng các phương pháp khác để tạo hệ thống sự kiện ... Tuy nhiên, một số ý tưởng trong luồng này sẽ tăng tốc độ khá nhiều, đặc biệt là trong việc tải hệ thống sự kiện (thời gian tải 11-25 giây với 100000 đối tượng)
ultifinitus

Đối tượng 100K nghe có vẻ khủng khiếp chỉ cho một trò chơi. Đây là máy chủ hay ứng dụng khách người dùng cuối?
Patrick Hughes

Đây là động cơ của tôi, vì vậy tôi thực sự sẽ linh hoạt. Nó sẽ được sử dụng trong các ứng dụng máy chủ và ứng dụng người dùng cuối (ít thường là lần đầu tiên, tối ưu hóa)
ultifinitus

Câu trả lời:


5

Có vẻ như bạn đang nói nút cổ chai hiệu suất lớn đang tìm kiếm ID sự kiện (số nguyên) từ tên chuỗi của họ. Bạn có thể xử lý trước dữ liệu trò chơi của mình để chuyển đổi tất cả các tên sự kiện thành số nguyên trước khi chạy trò chơi, hoặc có thể trong khi tải cấp độ; sau đó bạn sẽ không phải thực hiện bất kỳ chuyển đổi nào trong quá trình chơi trò chơi.

Nếu các đối tượng thường xuyên được tạo ra và phá hủy, có thể có rất nhiều sự khuấy động trong các vectơ của các đối tượng. Trong trường hợp này, bạn có thể hưởng lợi bằng cách sử dụng danh sách được liên kết thay vì vectơ; chúng nhanh hơn để chèn và xóa.


Nút cổ chai không lớn (đối với 100.000 đối tượng, nó mất 0,0000076 ms / đối tượng) nhưng tôi nghĩ ý tưởng của bạn là một ý tưởng tuyệt vời! Tôi nghĩ rằng tôi thực sự sẽ thực hiện tra cứu id một lần và lưu trữ eventID dưới dạng int chứ không phải dữ liệu chuỗi gốc. Và tôi thực sự đã không nghĩ đến danh sách liên kết, ý tưởng tốt.
ultifinitus

1
+1 Để tiền xử lý ID. Bạn cũng có thể làm điều đó một cách lười biếng bằng cách có một loại EventType mà mỗi mô-đun có thể có được thông qua một cái gì đó như EventType takeDamageEvent = EventSystem::CacheEventType("takeDamageEvent");. Làm cho nó trở thành một thành viên tĩnh của các lớp và bạn sẽ chỉ có một bản sao trôi nổi trong mỗi lớp cần nó.
michael.bartnett

2
Lưu ý rằng việc lưu trữ id thay vì chuỗi sẽ khiến việc gỡ lỗi hơi khó khăn. Nó luôn hữu ích để có thể xem một số văn bản chỉ định nơi một đối tượng đến từ. Bạn có thể đi được nửa đường bằng cách lưu trữ cả id và chuỗi mà bạn giữ xung quanh cho mục đích gỡ lỗi (thậm chí có thể bị xóa trong các bản dựng phát hành).
Nicol Bolas

2

Chà, trước tiên hãy lấy những thứ đơn giản ra khỏi đường đi. Bạn có điều này mapgiữa các chuỗi (tên của sự kiện, có lẽ) và số nguyên (chỉ mục của người nghe sự kiện đã đăng ký).

Thời gian tra cứu trong một mapdựa trên hai điều: số lượng vật phẩm trong bản đồ và thời gian cần thiết để so sánh giữa hai khóa (bỏ qua các vấn đề về bộ đệm). Nếu thời gian tra cứu là một vấn đề, một cách để xử lý nó là thay đổi chức năng so sánh bằng cách thay đổi loại chuỗi.

Giả sử bạn đang sử dụng std::stringoperator<để so sánh. Điều này rất kém hiệu quả; nó làm một so sánh khôn ngoan byte. Bạn không quan tâm đến chuỗi thực sự ít hơn so với so sánh; bạn chỉ cần một số loại so sánh đưa ra một trật tự yếu nghiêm ngặt (vì mapkhông hoạt động khác).

Vì vậy, bạn nên sử dụng chuỗi có độ dài cố định 32 byte thay vì std::string. Tôi sử dụng chúng cho định danh. Các thử nghiệm so sánh cho các chuỗi cố định này không thực hiện so sánh theo byte; thay vào đó họ thực hiện so sánh 32 bit (hoặc 64 bit). Nó nhận mỗi 4 byte là một số nguyên không dấu và so sánh nó với 4 byte tương ứng của chuỗi khác. Bằng cách đó, việc so sánh chỉ mất tối đa 8 so sánh. Nó đảm bảo một thứ tự yếu nghiêm ngặt, mặc dù thứ tự không liên quan gì đến dữ liệu dưới dạng ký tự.

Lưu trữ chuỗi dài hơn 31 byte (cần ký tự NULL) cắt ngắn chuỗi (nhưng từ giữa, thay vì kết thúc. Tôi thấy rằng entropy có xu hướng lớn nhất ở đầu và cuối). Và chuỗi ngắn hơn pad đó ra các ký tự còn lại với \0.

Bây giờ, bạn có thể bỏ maphoàn toàn và sử dụng bảng băm. Nếu bạn thực sự có hơn 100.000 loại sự kiện khác nhau, đây có thể là một ý tưởng tốt. Nhưng tôi không biết về một trò chơi mà đó sẽ là một điều hợp lý từ xa.


Vì vậy, bạn nên sử dụng chuỗi có độ dài cố định 32 byte thay vì chuỗi std ::. Tuyệt diệu! Tôi đã không nghĩ sẽ thay đổi kiểu chuỗi.
ultifinitus

0

Để trả lời câu hỏi chung:

Cách tốt hơn để thiết lập một hệ thống sự kiện

Chẳng có ai. Tất cả những gì bạn có thể làm là xác định các nhu cầu cụ thể mà bạn có cho các hệ thống sự kiện của mình (có thể có một số khác nhau) và sau đó sử dụng công cụ phù hợp cho công việc phù hợp.

Tôi đã thực hiện và cố gắng khái quát hóa hàng tấn hệ thống sự kiện và tôi đã đi đến kết luận này. Bởi vì tradoffs thực sự khác biệt nếu bạn tạo nó theo thời gian biên dịch hoặc thời gian chạy, nếu bạn sử dụng phân cấp đối tượng hoặc bảng đen, v.v.

Cách tốt nhất là nghiên cứu các loại hệ thống sự kiện khác nhau và sau đó bạn sẽ có manh mối về việc sử dụng hệ thống nào trong trường hợp nào.

Bây giờ, nếu bạn muốn hệ thống thực sự linh hoạt nhất, hãy triển khai hệ thống bảng đen (thời gian chạy) với các thuộc tính động của các sự kiện và bạn sẽ có thứ gì đó rất linh hoạt nhưng có thể rất chậm.

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.