Dies scheint großartig zu sein, wenn man den schnellen Zugriff für eine Komponente unter Verwendung der Entitäts-ID und vor allem die schnelle Iteration über Elemente des Arrays (da Systeme dies ständig tun) und weniger Cache-Fehler berücksichtigt, als wenn ich Komponenten in der Entität speichere oder verwende:
Haben Sie tatsächlich Cache-Fehler gemessen? Sind sie in irgendeiner Weise ein Problem für Sie? Würde es wahrscheinlich messbare Auswirkungen haben, wenn Sie sie für Ihre Komponentenzugriffe entfernen? Ist es für Sie wertvoll, Ihre Optimierungszeit damit zu verbringen, sich um sie zu kümmern, anstatt sich auf Parallelität oder eine bessere GPU-Nutzung zu konzentrieren? Haben Sie zumindest Messungen Ihres Codes vor einer solchen Änderung, damit Sie diese mit Messungen nach der Änderung vergleichen können?
Das einfache Einfügen von Dingen in ein Array garantiert auch nicht weniger Cache-Fehler. Dies ist der erste von mehreren Schritten zur Verbesserung der Cache-Nutzung für Komponenten. Dies kann auch ein falscher Schritt zur Verbesserung Ihrer Gesamtleistung sein.
1) Muss ich für jede Komponente ein Array erstellen? Etwas wie:
PositionComponent positionComponents[MAX_OBJECTS];
HealthComponent healthComponents[MAX_OBJECTS];
A std::vector
oder ähnliches würde genauso gut funktionieren. Beachten Sie, dass vector
Sie Komponentenindizes anstelle von Zeigern verwenden müssen , da der Backing-Speicher neu zugewiesen und vorhandene vorhandene Iteratoren / Zeiger ungültig gemacht werden können.
Sie können auch problemlos ein Chunked-Speichermittel schreiben (wie ein std::deque
, jedoch nicht für Warteschlangen optimiertes), mit dem die Struktur wachsen, eine schnelle Iteration beibehalten und stabile Zeiger haben kann.
2) Wie kann ich Komponenten mithilfe der Entitäts-ID abrufen, wenn ich dies tue?
template <class T>
T* getComponent(int entityId) const {
... // ??
}
Sie können eine zusätzliche boost::flat_map
oder std::unordered_map
eine benutzerdefinierte Hochleistungs-Hash-Tabelle speichern , die Entitäts-IDs Komponentenindizes für diesen Komponententyp zuordnet.
Sie möchten wahrscheinlich nur einen Index oder einen temporären Zeiger zuordnen (einen, den Sie versprechen, niemals beizubehalten), da dies dem Komponenteninhaber ermöglicht, seine Komponentensammlung zu defragmentieren. Dass beides dem Inhaber möglicherweise ermöglichen kann, Speicherblöcke freizugeben, und vor allem (für datenorientierte Anforderungen), ist entscheidend, um die Iteration über Komponenten tatsächlich schneller zu machen. Es wird wahrscheinlich viel schneller sein, Ihren vorhandenen Ansatz zu verwenden, als über ein riesiges und dünn besiedeltes Array zu iterieren. Denken Sie daran, dass Sie bei einem spärlichen Array (auch wenn es zu 99% belegt ist) eine zusätzliche if (isInUse)
Überprüfung in Ihrer Schleife über die Komponenten hinzufügen müssen. Dies ist eine zusätzliche Lese- und Verzweigungsanweisung, die sich spürbar negativ auf Ihre gesamte Iterationsgeschwindigkeit auswirken kann.
Siehe die vier Artikel von BitSquid zum Thema . Diese Leute konzentrieren sich in der Regel auf C-ähnlichen Code und meiden moderne C ++ - Ansätze mit identischer Leistung, aber einfacherer Verwendung. Die zugrunde liegenden Ansätze, die sie verwenden, sind jedoch in C, C ++, C #, Rust oder ähnlichen Sprachen anwendbar.
unordered_map
, nur sie verwenden ihre eigene. Die Konzepte sind die gleichen.unordered_map
die ... Probleme haben (es wurde für eine Reihe von Invarianten entwickelt, die hier nicht zutreffen, aber dafür einen erheblichen Perf-Overhead verursachen Fall).Ja, aber ein Vektor wäre bequemer und ebenso leistungsfähig.
Sie sollten die Entitäts-ID auf den Komponenten selbst speichern. Halten Sie Ihren Vektor nach dieser ID sortiert und verwenden Sie dann vector :: lower_bound , um die Komponenten abzurufen. In meinen eigenen Tests ist diese Suche bis zu einem bestimmten Punkt tatsächlich schneller als eine Karte.
quelle