Ähm.. Hilfe? :) Performancefrage.
-
Hi,
ich habe momentan ein "Konzept", das in etwa so aufgebaut ist. (Auf Konstruktoren/Destruktoren etc. ist natürlich verzichtet worden.)
class Base { public: virtual void foo() = 0; }; class Fan : public Base { public: void foo() { std::cout << "Fan-Base!\n"; } }; class BaseManager { std::vector<Base*> bases_; public: void add(Base *base) { bases_.push_back(base); } void foo() { for (auto i = bases_.begin(); i != bases_.end(); ++i) (*i)->foo(); } }; int main() { BaseManager mngr; mngr.addBase(new Fan()); mngr.foo(); }Das funktioniert soweit natürlich. Allerdings sehe ich hier einige Probleme. Abgesehen von den viele Cache-Misses, die das Ganze produzieren wird, dauert es ewig, sehr viele (kleine) einzelne Objekte zu allokieren bzw. wieder freizugeben. Es wäre irgendwie schön, wenn man mehr Speicher am Stück reservieren könnte, sodass die Daten nicht alle völlig vereinzelt im Speicher liegen. Trotzdem möchte ich natürlich nicht für jede von Base erbende Klasse eine eigene "addXY"-Funktion in der Manager-Klasse basteln. Hat jemand hier eine Idee?
Edit:
Das sollte doch eigentlich ein öfter vorkommendes Problem sein? Vielleicht gibt es ja sogar ein Standard-Pattern dafür, aber ich kann nichts finden.
-
Du könntest operator new() für die Klassen, die von Base erben, überladen. Wie man das macht, steht z.B. in "Effective C++". D.h. du allozierst für die verschiedenen Typen Speicherblöcke (so etwas wie std::deque). Mehrere verschiedene Blöcke deswegen, weil Typen eine unterschiedliche Größe haben können.
-
wxSkip schrieb:
Du könntest operator new() für die Klassen, die von Base erben, überladen. Wie man das macht, steht z.B. in "Effective C++". D.h. du allozierst für die verschiedenen Typen Speicherblöcke (so etwas wie std::deque). Mehrere verschiedene Blöcke deswegen, weil Typen eine unterschiedliche Größe haben können.
Sind dafür nicht Allocator (C++) zuständig, den man als zusätzlichen
template-Parameter angeben kann?
Ist zwar komplizierter als nur ein neuesnewaber "riecht besser" und kannst gezielt einsetzen.Edit:Allocator durch Allocator (C++) ausgetuscht
-
Rhombicosidodecahedron schrieb:
wxSkip schrieb:
Du könntest operator new() für die Klassen, die von Base erben, überladen. Wie man das macht, steht z.B. in "Effective C++". D.h. du allozierst für die verschiedenen Typen Speicherblöcke (so etwas wie std::deque). Mehrere verschiedene Blöcke deswegen, weil Typen eine unterschiedliche Größe haben können.
Ist dafür nicht der Allocator zuständig, den man als zusätzlichen
template-Parameter angeben kann?
Ist zwar komplizierter als nur ein neuesnewaber "riecht besser" und kannst gezielt einsetzen.Und wie soll das funktionieren? So?
template<typename T> class Allocator; template<> class Allocator<Fan> { static std::deque fan_blocks; public: Fan *allocate(Fan const &fan) { fan_blocks.push_back(fan); return &fan_blocks.back(); //weiß nicht genau, ob die auch nach Vergrößerung noch gültig sind, aber so ungefähr } } template<typename T> void BaseManager::add(T const &in) { bases_.push_back(Allocator<T>().allocate(in)); }So, dann hätten wir nur noch ein kleines Problem mit der Deallokation. Viel Spaß

-
cooky451 schrieb:
Das funktioniert soweit natürlich. Allerdings sehe ich hier einige Probleme. Abgesehen von den viele Cache-Misses
Ist der Manager performance kritisch? Also Base::foo() eine kleine Funktion und die Schleife wird oft mit extrem vielen Elementen durchlaufen? Wenn nicht, dann optimierst du an der falschen Stelle.
für die allokation: small object allocator. Gibt auch was in Boost dazu, wenn ich mich recht entsinne. Allerdings löst dies das Fragmentierungsproblem nur halb.
-
cooky451 schrieb:
Trotzdem möchte ich natürlich nicht für jede von Base erbende Klasse eine eigene "addXY"-Funktion in der Manager-Klasse basteln.
Wer einen Manager baut, mag auch addXY-Funktionen, natürlich mit dem sogenannten perfect forwarding. Alternativ auch emplace-Funktionen oder einen überladenen operartor new.
Ich würde die Lektüre von "modern c++ design" empfehlen für small object allocators und ich mag noch einwerfen, daß man sich richtiges new/delete manchmal schenken kann, wenn man erst alle reintut und dann alle löscht, und natürlich simple Freispeicherlisten erwähnen.
-
Klassen dessen Namen den String "Manager" containieren sind böse.