[C++] Container für nicht-kopierbare Objekte

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
Schrompf
Moderator
Beiträge: 4854
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

[C++] Container für nicht-kopierbare Objekte

Beitrag von Schrompf »

Moin,

dachte mir, die Frage kann ich ja auch mal den Profis hier stellen. Ich habe eine komplexe nicht kopierbare Klasse. Dazu will ich bei Programmstart eine Anzahl anhand Systemparameter festlegen und dann diese Anzahl Instanzen erzeugen. Bisher habe ich das banal in einem vorreservierten std::vector getan:

Code: Alles auswählen

std::vector<Dingens> mDingenses;
mDingenses.reserve(anzahl);
for( 0 .. anzahl )
  mDingenes.emplace_back(...Konstruktorparameter...);
Jetzt nach Wechsel zu Visual Studio 2015 geht das nicht mehr. Anscheinend kompiliert der jetzt die Templates richtig, anstatt sie nur textuell einzusetzen wie bisher. Und damit wird auch die nicht benutzte Funktion resize() instanziiert, wodurch der Compiler Copy- oder Move-Konstruktor braucht und sich darob beschwert.

Wie könnte ich das Problem codearm lösen? Eigenen Speicher und Placement New wäre aktuell die einzige Option, die mir einfällt, aber da gibt's sicher was Kluges.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Schrompf
Moderator
Beiträge: 4854
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Container für nicht-kopierbare Objekte

Beitrag von Schrompf »

Korrektur: anscheinend hat sich nur die Implementation von std::vector<>::reserve() geändert. Eigentlich hätte das nie funktionieren dürfen, weil reserve() ja bei schon existierender Befüllung *immer* Objeke bewegen oder umkopieren muss. Hmm... seltsam. Aber nuja, die Orginalfrage besteht weiterhin: wie löst man das simpel und sicher?
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Container für nicht-kopierbare Objekte

Beitrag von dot »

Schrompf hat geschrieben:Jetzt nach Wechsel zu Visual Studio 2015 geht das nicht mehr. Anscheinend kompiliert der jetzt die Templates richtig, anstatt sie nur textuell einzusetzen wie bisher. Und damit wird auch die nicht benutzte Funktion resize() instanziiert, wodurch der Compiler Copy- oder Move-Konstruktor braucht und sich darob beschwert.
Der C++ Standard schreibt (für den Fall der impliziten Spezialisierung eines Klassentemplate) vor, dass die Definition einer Memberfunktion nur instanziert wird, wenn die Memberfunktion auch benutzt wird. Das Problem liegt bereits in der Verwendung von reserve() und emplace_back(). Beide Funktionen müssen potentiell eine Reallokation des internen Buffers durchführen und dabei den Inhalt des Arrays vom alten in den neuen Buffer moven, es ist für diese beiden Funktionen daher (auch laut Standard) notwendig, dass der im vector gespeicherte Typ zumindest Moveable ist. Ich kann ehrlich gesagt nicht ganz nachvollziehen, wie der Code so jemals kompiliert haben soll.
Schrompf hat geschrieben:Eigenen Speicher und Placement New wäre aktuell die einzige Option, die mir einfällt, aber da gibt's sicher was Kluges.
Ich fürchte, wenn du die Objekte zusammehängend im Speicher liegen haben willst, wird es wohl oder übel darauf hinauslaufen. Ansonsten einfach std::list verwenden... ;)
Benutzeravatar
Schrompf
Moderator
Beiträge: 4854
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Container für nicht-kopierbare Objekte

Beitrag von Schrompf »

Stimmt, einfach ein anderer Container, der nicht reallokiert. Hm. Hier in diesem Fall ist mir aber tatsächlich die cachelokale Unterbringung wichtig, auch wenn das wahrscheinlich völlig fehlgeleitete Optimierung ist. Hm. Genaugenommen ist das wahrscheinlich sogar tödlich wegen False Sharing, wenn ich meine Worker Threads lokal beieinander liegen habe. Irgendwann werden die Cache-Zeilen mal größer und dann grillt mir das False Sharing die Performance.

Gut, dass wir dieses Gespräch geführt haben. Der Erkenntnisweg des Thomas Z. - jetzt live auf irgendnem Dritten.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Container für nicht-kopierbare Objekte

Beitrag von dot »

Rein aus interesse: Was ist denn das für ein Ding, das so klein ist, dass False Sharing ein Problem sein könnte, gleichzeitig aber konzeptionell weder Copyable noch Moveable sein darf!? Mit std::list werden die Dinger in der Praxis sehr wahrscheinlich auch wieder hintereinander im Speicher liegen, weil die Allokationen direkt hintereinander passieren...
Benutzeravatar
Schrompf
Moderator
Beiträge: 4854
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Container für nicht-kopierbare Objekte

Beitrag von Schrompf »

Mein erster Versuch eines parallelen Job-Systems.

Code: Alles auswählen

/// @file Traum_ParallelArbeiter.h
/// Ein Arbeiter, der anfallende Aufgaben in seinem eigenen Thread ausführt

#pragma once

/// Führt in dem Thread, in dem er gestartet wurde, Aufgaben aus der Warteschlange aus.
class Traum_ParallelArbeiter
{
  friend class Traum_Parallel;

public:
  /// Konstruktor, nimmt eine Warteschlange entgegen, aus der er Jobs ausführt, und die Wartebedingung, an der er lauscht,
  /// wenn's gerade nix zu tun gibt
  Traum_ParallelArbeiter( moodycamel::ConcurrentQueue<Traum_ParallelFaser*>& pWarteschlange, std::mutex& pMutex, 
    std::condition_variable& pBedingung);

private:
  /// Arbeitsfunktion, sollte im dafür vorgesehenen Thread aufgerufen werden.
  void Arbeiten();

  /// Sagt dem Arbeiter, dass er zum Ende kommen soll
  void SetEndeSignal();

private:
  /// die Warteschlange, aus der wir Aufgaben ziehen
  moodycamel::ConcurrentQueue<Traum_ParallelFaser*>& mWarteschlange;
  /// Mutex und Bedingung, auf denen wir warten, bis wieder was zu tun ist
  std::mutex& mMutex;
  std::condition_variable& mBedingung;

  /// besagt, ob wir zum Ende kommen sollen.
  std::atomic_bool mIstEndeGefordert;

  /// ordentliches Stück Platzhalterspeicher ans Ende, damit jeder Arbeiter in einer eigenen Cache-Zeile landet
  static const size_t PlatzhalterGroesse = 256 - 3*sizeof( void*) - sizeof( std::atomic_bool);
  uint8_t mPlatzhalter[PlatzhalterGroesse];
};
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Container für nicht-kopierbare Objekte

Beitrag von dot »

Ok, macht Sinn, in dem Fall seh ich aber kein Problem darin, einfach eine std::list zu benutzen, da die Geschwindigkeit, in der über die Liste von Traum_ParallelArbeitern iteriert werden kann, völlig irrelevant sein sollte, ansonsten sorgst du mit deinem Platzhalter ja selbst schon dafür, dass das nicht unbedingt so effizient ist...
Benutzeravatar
Schrompf
Moderator
Beiträge: 4854
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Container für nicht-kopierbare Objekte

Beitrag von Schrompf »

Iteriert wird darüber eh nur einmal beim Starten und einmal beim Beenden. Mir ist nur aufgefallen, dass die ja bei jeder neuen Fiber, die sie annehmen, ihre Membervars anfassen. Daher die Vermutung von False Sharing.

Wobei... auch das ist Quatsch. Das sind ja nur Referenzen auf die Queue und die Wartebedingung, die sind für alle Instanzen des Arbeiters eh dieselben. Und der Rest der Klasse ist so irrelevant, dass False Sharing hier wirklich keinen kratzt. Du hast Recht, ich schmeiß den Platzhalter wieder raus.

Und ich habe allgemein den Eindruck, dass es sich lohnen könnte, das Gesamtsystem hier mal zur Diskussion zu stellen. Wenn jemand von euch Lust und Zeit hat, Software noch in dem Detail zu diskutieren.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: [C++] Container für nicht-kopierbare Objekte

Beitrag von Spiele Programmierer »

Ich nehme in so einem Fall meistens std::vector<std::unique_ptr<T>>.
Vorteile: Random Access, einfügen von bereits konstruierten Objekten und theoretisch leicht bessereres Cache-Verhalten, weil die Vorwärts- und Rückwärtszeiger dazwischen entfallen.

Optimal ist es natürlich auch nicht, aber bis auf ganz große Ausnahmen, sind Objekte ja zumindest verschiebbar.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Container für nicht-kopierbare Objekte

Beitrag von dot »

Stimmt, das ist auf jeden Fall auch eine gute Lösung.
Antworten