이벤트 시스템을 설정하는 더 좋은 방법이 있습니까?


9

이벤트 시스템은 놀랍고, 다루기 어려운 코드 길들이기를 만들고 객체와 게임 루프를 쉽게 전달하여 게임을 역동적으로 만들 수 있습니다. 현재 구현의 효율성으로 어려움을 겪고 있습니다. 현재 객체 목록을 응답하는 이벤트로 분리하는 약간의 최적화는 놀라운 일 이었지만 더 많은 일을 할 수 있어야합니다.

현재 두 가지 방법이 있습니다.

  1. 가장 간단한 : 이벤트가 전송되면 모든 객체가 벡터에 추가됩니다 . 모든 객체는 handle_event () 메소드를 통해 이벤트가 전송됩니다.

  2. 더 복잡 : 문자열이 키이고 int 값인 맵이 있습니다. 이벤트 유형이 추가되면이지도에 추가되며 int는 단순히
    객체 벡터의 벡터 가 증가하고 (더 나은 방법이 있어야 함) 새 벡터를 뒤로 밀어 해당 유형의 이벤트를 처리합니다.
    이벤트가 호출되면 단순히 eventTypes 맵에서 해당 int를 객체 벡터의 벡터 내부 유형으로 호출하고 해당 이벤트를 해당 이벤트 유형을 처리하는 각 객체로 보냅니다.

이 첫 번째 방법은 많은 객체에 대해 (분명히) 느리지 만 매우 적은 객체에 대해서는 매우 빠릅니다. 두 번째 방법은 다른 유형의 이벤트를 처리하려는 큰 객체에서는 매우 빠르지 만 동일한 유형의 이벤트를 처리하는 객체는 객체 당 첫 번째 방법보다 느립니다.

더 빠른 (런타임 현명한) 방법이 있습니까? 문자열 유형에서 정수를 찾는 더 빠른 방법이 있습니까? (처음에는 열거 형이 있었지만 원하는 수준의 역 동성 때문에 필요한 사용자 정의 유형을 허용하지 않았습니다.)


1
이것은 해시 맵 (또는 해시 테이블)의 종류이며 문자열은 해시 번호로 계산되어 배열에서 직접 조회하는 데 사용됩니다. en.wikipedia.org/wiki/Hash_table
Patrick Hughes

좋은 질문은 : 이것이 실제로 성능 병목 현상입니까, 아니면 조기에 걱정하고 있습니까?
Jari Komppa

이미 구현되어 있으며 많은 객체가 사용될 때 병목 현상이 발생합니다. 병목 현상 부족에 대한 이전의 의견은 응용 프로그램 자체가 아니라 실제로 동일한 수의 객체가 실제로 처리 된 위의 두 구현 간의 속도 차이입니다. 이벤트 시스템을 만드는 다른 방법을 기대하고 있었지만 ...이 스레드의 아이디어 중 일부는 특히 이벤트 시스템의 로딩 (100000 개의 객체로 11-25 초 전체로드 시간)에서 속도가 약간 증가합니다.
ultifinitus

100K 개체는 단지 게임에 대한 끔찍한 소리입니다. 이 서버 또는 최종 사용자 클라이언트 응용 프로그램입니까?
패트릭 휴즈

이것이 내 엔진이므로 유연성을 추구합니다. 그것은 (적게 첫 번째, 최적화) 서버 애플리케이션과 최종 사용자 응용 프로그램에 사용됩니다
ultifinitus

답변:


5

큰 성능 병목 현상이 문자열 이름에서 이벤트 ID (정수)를 찾고 있다고 말하는 것 같습니다. 게임 데이터를 사전 처리하여 게임을 실행하기 전 또는 레벨을로드하는 동안 모든 이벤트 이름을 정수로 변환 할 수 있습니다. 게임 플레이 중에는 변환 할 필요가 없습니다.

객체가 자주 생성되고 파괴되는 경우 객체 벡터에 많은 변동이있을 수 있습니다. 이 경우 벡터 대신 링크 된 목록을 사용하여 이점을 얻을 수 있습니다. 삽입 및 삭제 속도가 더 빠릅니다.


병목 현상은 크지 않지만 (10 만 개 개체의 경우 .0000076 ms / 개가 손실 됨) 아이디어가 좋은 아이디어라고 생각합니다! 실제로 ID를 한 번 조회하고 eventID를 원래 문자열 데이터가 아닌 int로 저장한다고 생각합니다. 그리고 실제로 연결된 목록, 좋은 생각은 생각하지 못했습니다.
ultifinitus

1
+1 ID를 전처리합니다. 각 모듈이 같은 것을 통해 얻을 수있는 EventType 유형을 가짐으로써 게으르게 할 수도 있습니다 EventType takeDamageEvent = EventSystem::CacheEventType("takeDamageEvent");. 클래스를 정적 ​​멤버로 만들면 필요한 각 클래스마다 하나의 사본 만 떠 다니게됩니다.
michael.bartnett

2
문자열 대신 ID를 저장하면 디버깅이 다소 어려워집니다. 개체의 위치를 ​​지정하는 텍스트를 볼 수 있으면 항상 유용합니다. 디버깅 목적으로 유지하는 id와 문자열을 모두 저장하여 반쯤 진행할 수 있습니다 (아마도 릴리스 빌드에서 제거 될 수도 있음).
Nicol Bolas

2

글쎄, 간단한 것들을 먼저 꺼내 보자. 이이 map(아마도 이벤트의 이름) 문자열 사이의 정수 (등록 된 이벤트 리스너의 인덱스).

의 조회 시간은 map두 가지를 기반으로합니다. 맵의 항목 수와 두 키를 비교하는 데 걸리는 시간 (캐시 문제 무시). 조회 시간에 문제가있는 경우이를 처리하는 한 가지 방법은 문자열 유형을 변경하여 비교 기능을 변경하는 것입니다.

하자 당신이 사용하고있는 가정 std::stringoperator<비교. 이것은 매우 비효율적입니다. 바이트 단위 비교를 수행합니다. 실제 문자열을 비교하는 것보다 중요하지 않습니다. 엄격한 순서를 제공하는 일종의 비교가 필요합니다 ( map다른 방식으로는 작동하지 않기 때문에 ).

따라서 대신 32 바이트 고정 길이 문자열을 사용해야합니다 std::string. 나는 이것을 식별자로 사용합니다. 이러한 고정 문자열에 대한 비교 테스트는 바이트 단위 비교를 수행하지 않습니다. 대신 32 비트 (또는 64 비트) 비교를 수행합니다. 4 바이트마다 부호없는 정수로 취하여 다른 문자열의 해당 4 바이트와 비교합니다. 이렇게하면 비교에는 최대 8 개의 비교 만 필요합니다. 순서는 데이터와 문자와 관련이 없지만 엄격한 약한 순서를 보장합니다.

31 바이트보다 긴 문자열을 저장하면 (NULL 문자 필요) 문자열이 잘립니다 (그러나 끝이 아닌 가운데에서 시작합니다. 엔트로피는 시작과 끝에서 가장 큰 경향이 있음을 알았습니다). 그리고 그보다 짧은 문자열은로 나머지 문자를 채 웁니다 \0.

이제 map전체를 버리고 해시 테이블을 사용할 수 있습니다. 실제로 다양한 종류의 이벤트 유형이 100,000 개가 넘는 경우 이것이 좋습니다. 그러나 나는 그것이 원격으로 합리적인 게임 일지 모른다.


따라서 std :: string 대신 32 바이트 고정 길이 문자열을 사용해야합니다. 환상적인! 문자열 유형을 변경하려고 생각하지 않았습니다.
ultifinitus

0

일반적인 질문에 대답하려면 :

이벤트 시스템을 설정하는 더 좋은 방법

없습니다. 이벤트 시스템에 필요한 특정 요구 사항을 식별하고 (여러 가지가있을 수 있음) 올바른 작업에 적합한 도구를 사용하기 만하면됩니다.

나는 수많은 이벤트 시스템을 구현하고 일반화하려고 노력 했으며이 결론에 도달했습니다. 컴파일 타임이나 런타임으로 만들거나 객체 계층이나 칠판 등을 사용하는 경우 tradoff가 실제로 달라지기 때문입니다.

가장 좋은 방법은 다양한 종류의 이벤트 시스템을 연구하는 것이며 어떤 경우 어떤 시스템을 사용해야하는지에 대한 힌트를 얻을 수 있습니다.

이제 가장 유연한 시스템을 원한다면 이벤트의 동적 속성으로 블랙 보드 시스템 (런타임)을 구현하면 매우 유연하지만 속도가 느릴 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.