2013-07-29 2 views
0

Я недавно внедряю систему компонентов Entity для своего игрового движка. Каждый объект хранит карту компонентов, как, что:std :: map Скорость доступа слишком медленная?

Component.h

enum COMPONENT_INFO { 
    COMPONENT_POSITION = 0, 
    COMPONENT_PHYSICS, 
    COMPONENT_RENDERABLE 
}; 

Entity.h

class Entity { 
public: 
    std::bitset<NUM_COMPONENTS> componentBits; 
    std::map<COMPONENT_INFO, Component*> components; 
    .. 
    .. 

    Component *getComponent(COMPONENT_INFO inf) { 
     return components[inf]; 
    } 
}; 

И мой класс System обновляет каждый объект в каждом кадре так:

void update(Entity *e, float delta) { 
     PositionComponent *cmp=(PositionComponent*)e->getComponent(COMPONENT_INFO::COMPONENT_PHYSICS); 
     //x += 1.0f; 
     cmp->x += 1.0f; 
    } 

Все работает по назначению, но у меня есть огромная проблема с производительностью при доступе к карте. Когда я создаю 10000 сущностей и перебираю их (система выполняет итерацию на самом деле), я получаю 80 FPS с голубой пустой системой (без визуальных эффектов, просто на экране). Но когда я закомментировать части доступа и использовать только x += 1.0f;, FPS increadibly увеличивается до 1000. Как что:

void update(Entity *e, float delta) { 
     x += 1.0f; //btw the system has a local x value. 
    } 

Так что проблема просто в доступе компоненты через карту. Что еще я могу использовать в такой системе? Или, может быть, я делаю что-то неправильно в доступе к карте?

Важное изменение: это всего лишь тестовый диск, я имею в виду, что на сущность может быть много компонентов, а не только 3. Я написал их только для тестирования.

+1

Учитывая, что у вас есть только три ключа, вы можете используйте простой массив или 'std :: array '. Но вы уверены, что проблема действительно в доступе к элементам карты? – juanchopanza

+0

Да, проблема заключается в доступе к элементам карты (хотя я удаляю cmp-> x line, fps все равно 80). Между прочим, между ними не будет 3 ключа. Например, если у меня есть 64 разных компонента, все сущности будут вынуждены удерживать их отдельно, если я использую фиксированные массивы. – deniz

+0

Can вы кешируете результат карты в своем классе, чтобы предотвратить r быстрый поиск? –

ответ

2

Ну, в первую очередь, ваш метод getComponent использует оператор [], который не только извлекает экземпляр с заданным ключом, но и не присутствует, вставляет элемент.

http://www.cplusplus.com/reference/map/map/operator%5B%5D/

В данной реализации вы ограничиваете себя одним из компонентов каждого типа в сущности, не может быть лучшим вариантом. Например, вы можете захотеть иметь несколько визуализируемых компонентов для одного объекта, выполняющего различные вещи, такие как частицы, рендеринг сетки и т. Д.

Карта не гарантирует, что объект кладется в памяти последовательно, что может привести к провалу кеша при обработке по очереди , Было бы более эффективно использовать std :: vector, например, и сохранить COMPONENT_INFO в самом объекте компонента.

Другим решением было бы добавить методы в ваш системный класс, который создавал бы определенные типы компонентов, и при этом делал бы некоторые инструкции по каждому экземпляру компонента. Затем система могла сохранять векторы каждого типа компонента и выполнять пакетную обработку.

Надеюсь, это поможет.

Edit:

Идея заключается в том, что при добавлении/создать новый Collision компоненту компонент сам регистрирует в системе, обеспечивающей тип и указатель на себя. То же самое для рендеринга и т. Д. Вы можете иметь карту векторов для каждого типа компонента в вашей системе. Когда вам нужно разрешить collsion, вы просто извлекаете все свои collidables (ссылка на вектор). Вы делаете один поиск, который является log (n), а затем перебираете все collidables (указатели из-за того, что они находятся в векторе, выкладываются последовательно в памяти, что делает его более удобным для кеша).

Также вы проверяете производительность в выпуске?

+0

Спасибо за ответ, компоненты не исправлены, я могу добавить много их. Это просто тест. Поскольку системы проверяют объекты, хотя я использую векторы, мне нужно знать, где именно находится элемент, если я их не сопоставляю, я снова потрачу время O (n), чтобы найти, с каким компонентом я разговариваю с – deniz

+0

. посмотрите на отредактированный ответ, если сможете. –

+0

Хм, версия выпуска в 4 раза быстрее, позвольте мне попробовать -O2 – deniz

2

Поскольку количество компонентов на сущность кажется фиксированным, я бы разбил карту и использовал переменные-члены в Entity для хранения каждого компонента вместе с геттером. Это позволит также избежать бросания в функции обновления:

class Entity { 
private: 
    PositionComponent* m_positionComponent; 
    PhysicsComponent* m_physicsComponent; 
    RenderableComponent* m_renderableComponent; 
public: 
    // initialize the components in the constructor 
    PositionComponent* getPositionComponent() { 
     return m_positionComponent; 
    } 

    // more getters for the other components 
} 

В качестве альтернативы, если вы хотите иметь больше типов компонентов и хотят сохранить конструкцию гибкой, вы можете хранить их в векторе, используя значение перечислений чтобы проиндексировать его. Это позволит избежать поиска карты в целом и даст вам лучшую производительность.

+0

Спасибо за ответ, есть не только 3 компонента. NUM_COMPONENTS предназначен для хранения битового набора. О векторах, если я использую это, мне придется зарезервировать элемент NUM_COMPONENTS для объекта, который будет дорогим (для того, чтобы случайно получить к нему доступ, я должен зарезервировать некоторое пространство, которое я думаю?) – deniz

+0

Кстати, я попробовал вектор и перечисление, (например, до 500 кадров в секунду), но, как я сказал, это будет стоить NUM_COMPONENTS * 4 байта даже для самых маленьких вещей, таких как частицы. – deniz

+1

Я понимаю ваше затруднительное положение, но часто вам приходится торговать пространством для скорости. Однако в этом случае я, вероятно, вернусь к чертежной доске; возможно, есть дизайн, который также решает ваши проблемы, будучи менее общим, но более эффективным. –

2

Как ваше наибольшее количество COMPONENT_INFO фиксирована вы могли бы использовать array<Component*> (или vector<> и использовать, например, myArray[COMPONENT_INFO::COMPONENT_PHYSICS] для адресации. Вы, возможно, придется проверить nullptr, хотя.

+0

Он сохранит 4 * NUM_COMPONENT ненужную память для каждого объекта. – deniz

+0

@deniz, который может быть намного меньше, чем накладные расходы, добавленные с помощью карты - каждый узел имеет указатель на родительский, левый и правый, плюс для дерева RB, он также имеет цвет - это 4 x стоимость одного указатель на использование памяти. –

Смежные вопросы