Implicitly shared Klassen - Wann benutzen und wann nicht?

Verschiedenes zu Qt
Antworten
Doppelkeks
Beiträge: 6
Registriert: 27. Oktober 2007 15:51

Implicitly shared Klassen - Wann benutzen und wann nicht?

Beitrag von Doppelkeks »

Eigentlich klingt das impliziert gesharte System der Qt Containerklassen recht praktisch, aber ich sehe im Code von anderen kaum, dass das kaum genutzt wird. Selbst Qt arbeitet eher mit Pointern und const-Referenzen als beispielsweise ein QPixmap by-value zu übergeben.

Also wo ist der Haken, und wann macht es Sinn, direkt Objekte by-value zu übergeben?

Ist das Problem vielleicht, dass man nie so genau weiß, ob man eine versehenliche deep copy auslöst? Lässt sich das vielleicht irgendwie abschalten (ohne mit const arbeiten zu müssen)? Oder wär das eher ein Job für Smart Pointer?
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Programmiere mit Qt genau so, wie du es mit C++ auch tun würdest! Verwende const-Referenzen, wenn du ein Objekt (Parameter) nicht verändern willst und spare eine Kopie. Verwende Kopien, wenn du Kopien haben willst. Fertig.
Das implicit sharing ist halt ein Feature, um Kopien dort schneller zu machen, wo Kopien gemacht werden, und nicht um Referenzen/Zeiger zu vermeiden. Denn die Kopien sind immer noch (deutlich) teurer als der Zugriff über eine Referenz.
Außerdem stimmt das nicht wirklich, dass Qt selber keine Kopien zieht:
Z.B. QLabel::setPixmap(const QPixmap& pix) kopiert pix in die eigenen Daten. Bin zu faul mehr zu suchen, aber es gibt soooo viele Stellen, da wirst du selber fündig :)
kater
Beiträge: 306
Registriert: 29. Dezember 2009 01:13
Wohnort: Darmstadt

Beitrag von kater »

franzf hat geschrieben: Z.B. QLabel::setPixmap(const QPixmap& pix) kopiert pix in die eigenen Daten.
Woher weisst du das? Oder hab ich nur mal wieder die wichtigen Stellen in der Doku übersehen.
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

kater hat geschrieben:Woher weisst du das? Oder hab ich nur mal wieder die wichtigen Stellen in der Doku übersehen.
Logik ;) Eine Referenz wird es nicht sein, denn die kann man nur einmal setzen, bei Klassen in der Initialisierungsliste des Konstruktors. Ein Zeiger wäre grob fahrlässig - du müsstest dir die Adresse speichern. Jetzt kann dummerweise einer Funktion mit const-Referenz als Parameter auch ein temporäres Objekt übergeben werden; dort wird die Adresse gespeichert -> Bumm! SegFault (das Objekt existiert nicht mehr).
Einzige Möglichkeit: QLabel zieht am Ende eine Kopie.

Code: Alles auswählen

void QLabel::setPixmap(const QPixmap &pixmap)
{
    Q_D(QLabel);
    if (!d->pixmap || d->pixmap->cacheKey() != pixmap.cacheKey()) {
        d->clearContents();
        d->pixmap = new QPixmap(pixmap); // Kopie :)
    }

    if (d->pixmap->depth() == 1 && !d->pixmap->mask())
        d->pixmap->setMask(*((QBitmap *)d->pixmap));

    d->updateLabel();
}
kater
Beiträge: 306
Registriert: 29. Dezember 2009 01:13
Wohnort: Darmstadt

Beitrag von kater »

Glaub ich habs nach 10min denken kapiert.
Aber ich habe ein ganz anderes Argument, dass für eine Kopie spricht. QLabel kann das Bild automatsich vergrößern. Man braucht also zwangsläufig eine neue Variable, die das neue Bild hält.
Doppelkeks
Beiträge: 6
Registriert: 27. Oktober 2007 15:51

Beitrag von Doppelkeks »

franzf hat geschrieben:Programmiere mit Qt genau so, wie du es mit C++ auch tun würdest!
Das ist der Grund, warum ich die Frage eigentlich unter "C++ Grundlagen" gestellt hatte - ich habe nur recht oberflächliches Wissen über C++ (habe mehr Erfahrung mit Müllsammler-Sprachen). Dann werd ich jetzt wohl hauptsächlich const-Referenzen nutzen.

Der Code-Ausschnitt aus der QLabel Klasse haut mich jetzt aber grade um - so wie ich den Sinn des implicit sharings verstanden hatte, dachte ich, dass genau solche Fälle damit vermieden werden sollen - deep copy nur dann, wenn Daten auch wirklich verändert werden, ansonsten internes ref-counting.
Außerdem verstehe ich noch nicht, warum die QPixmap im Heap und nicht auf dem Stack liegt. Eigentlich hält QPixmap doch alle "schweren" Daten selber schon im Heap? Wie gesagt, ich bin Neuling :)

kater hat geschrieben:Aber ich habe ein ganz anderes Argument, dass für eine Kopie spricht. QLabel kann das Bild automatsich vergrößern. Man braucht also zwangsläufig eine neue Variable, die das neue Bild hält.
Ich schreibe gerade ein Widget, das Pixmaps malen kann, und verändere die Größe nur mittels QPainter.scale, ohne die Orginaldaten zu verändern. Ist das schlecht?
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Der Code-Ausschnitt aus der QLabel Klasse haut mich jetzt aber grade um - so wie ich den Sinn des implicit sharings verstanden hatte, dachte ich, dass genau solche Fälle damit vermieden werden sollen - deep copy nur dann, wenn Daten auch wirklich verändert werden, ansonsten internes ref-counting.
Der Verwender von QPixmap hat darauf keinen Einfluss! Shallow Copy immer! Wenn die Kopie verändert wird, werden die Daten vom Original abgekoppelt. Wenn also ein nicht-temporäres QPixmap existiert, das dem QLabel übergeben wird, speichert QLabel eine billige Kopie - implizite Datenteilung. Stirbt das Original wird das QLabel-Pixmap der neue Datenhalter. Usw.
Außerdem verstehe ich noch nicht, warum die QPixmap im Heap und nicht auf dem Stack liegt.
Schau dir die API an: QLabel kann vieles darstellen. Text, Pixmap, Picture, usw. Wenn ständig alle Objekte komplett im Speicher liegen, würde ein einziges QLabel ganz schön SPeicher fressen. Einen Zeiger hingegen kannst du delete-en - und auf "0" setzen! Und das ist ein weiterer "Trick", der dann im paintEvent zum Tragen kommt:

Code: Alles auswählen

if (d->picture) {
       // Juppidu, wir haben ein QPicture zu zeichnen :P
    } else
    if (d->pixmap && !d->pixmap->isNull()) {
        // Juppidu, wir malen ein QPixmap
Eigentlich hält QPixmap doch alle "schweren" Daten selber schon im Heap? Wie gesagt, ich bin Neuling :)
Das ist doch relativ egal. Normalerweise sollte man seine Member als Value speichern, aber hier ist dadurch einiges gewonnen, wenn man es nicht tut.
kater hat geschrieben:Aber ich habe ein ganz anderes Argument, dass für eine Kopie spricht. QLabel kann das Bild automatsich vergrößern. Man braucht also zwangsläufig eine neue Variable, die das neue Bild hält.
Aber du kennst die Klasse dann ja schon besser, ich hab nur von der Schnittstelle von QLabel aus argumentiert ;)
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Glaub so richtig folgen kann ich der Disskussion hier ned, mein Kopf fängt gleich an zu rauchen ^^ Hier werden irgendwie viel zu viele Dinge durcheinander gemischt.

Naja, iss ja auch Montag morgen ...
Außerdem verstehe ich noch nicht, warum die QPixmap im Heap und nicht auf dem Stack liegt.
d->pixmap = new QPixmap(pixmap); // Kopie :)
glaub dir geht es um die Zeile ? oder ?
Also prinzipiell hasst recht, QPixmap haelt nur wenig daten direkt, die "fetten" Daten mit sicherheit per Verwaltungsdaten aufn heap.

Also schlimmstenfalls wuerden bei der obigen anweissung, einmal das new direkt für die verwaltung aufgerufen, und einmal nen implizietes Kopieren auf dem Heap fuer die Daten, wenn das Ding ned impliziet geshared ist.

Waehrend das 2te kopieren durch den Speichermanager oder durch das impliziete sharing wegoptimiert, bzw an ne andere Stelle verlagert werden kann, ist das 1 new zwingend.
Und news sind immer teuer. Trolltech muss also gute gruende haben das so zu machen ...

Gründe fallen mir aber momentan echt ned viele ein !
Auf alle faelle würd ich zuerst mal schauen, was d denn genau ist.
Isses ne Union oder nen Struct, was mehrere Typen von Zeigern haelt (QLabel kann ja ned nur Pixmaps anzeigen), iss klar warum sie das machen, oder ?
Ansonsten gehen mir auch die Ideen aus.
Generell, bei konsequenten implizieten Sharing sollte die Kopie der Verwaltungsdaten billiger sein wie jedes new ! Und als Anzeige von nicht vorhanden das new in Kauf nehmen, obwohl Pixmaps an sich auch einen "leerzustand" haben ...

Ciao ....
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

RHBaum hat geschrieben:Auf alle faelle würd ich zuerst mal schauen, was d denn genau ist.
Isses ne Union oder nen Struct, was mehrere Typen von Zeigern haelt (QLabel kann ja ned nur Pixmaps anzeigen), iss klar warum sie das machen, oder?
"d" ist der PIMPL-Zeiger. Darin liegen die Daten des Labels.

Code: Alles auswählen

QPixmap  *pixmap;
    QPixmap *scaledpixmap;
    QImage *cachedimage;
#ifndef QT_NO_PICTURE
    QPicture *picture;
#endif
#ifndef QT_NO_MOVIE
    QPointer<QMovie> movie;
Labels gibt es viele in Programmen. Stell dir vor, dass diese ganzen Member (Picture, Pixmap/...) values sind. Dann brauchen die IMMER egal ob Leerzustand oder nicht, einiges an Speicher.
QPicturePrivate:

Code: Alles auswählen

QAtomicInt ref;

    QBuffer pictb;
    int trecs;
    bool formatOk;
    int formatMajor;
    int formatMinor;
    QRect brect;
    QRect override_rect;
    QScopedPointer<QPaintEngine> paintEngine;
    bool in_memory_only;
    QList<QImage> image_list;
    QList<QPixmap> pixmap_list;
    QList<QBrush> brush_list;
    QList<QPen> pen_list;
QMoviePrivate:

Code: Alles auswählen


    QImageReader *reader;
    int speed;
    QMovie::MovieState movieState;
    QRect frameRect;
    QPixmap currentPixmap;
    int currentFrameNumber;
    int nextFrameNumber;
    int greatestFrameNumber;
    int nextDelay;
    int playCounter;
    qint64 initialDevicePos;
    QMovie::CacheMode cacheMode;
    bool haveReadAll;
    bool isFirstIteration;
    QMap<int, QFrameInfo> frameMap;
    QString absoluteFilePath;

    QTimer nextImageTimer;
QPixmap glaub ich hat einen günstigeren Leerzustand, hab aber grad nicht die Zeit da näher zu suchen.
Wenn diese Daten permanent im Speicher liegen, wächst der QLabel-Speicherbedarf recht stark. Ich kann mir denken, dass die Daten deshalb nicht als Value in QLabel gehalten werden.
kater
Beiträge: 306
Registriert: 29. Dezember 2009 01:13
Wohnort: Darmstadt

Beitrag von kater »

Dann nur eine Frage noch nebenbei.
Bei meinem Programmen wird oft ein Webcam Bild angezeigt. Momentan benutzte ich da QLabel für, weil es eben einfach ist. Die Rate mit der das Bild aktualisiert wird liegt bei theoretsichen 30FPS, praktisch unter 10.

Wenn ich mir nun den Thread anschaue, kommt es mir so vor, als wäre das mit QLabel ein ziemlicher Overhead um ein paar Webcam Bilder darzustellen.
Ich sag jetzt mal absichtlich nicht Video, weil ein Video für mich zusammenhängende komprimierte Bilder sind. Die Bilder liegen aber alle einzeln vor.

Naja, ist QLabel da jetzt die schlechteste Wahl oder gibt es eine performantere Klasse für diese Aufgabe?

Sry. Bissel Offtopic... :)
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

"d" ist der PIMPL-Zeiger. Darin liegen die Daten des Labels.
Naja, dann ist doch klar was sie sich mit dem "new" "erkaufen" !
Naja, ist QLabel da jetzt die schlechteste Wahl oder gibt es eine performantere Klasse für diese Aufgabe?
Wie du siehst, QLabel kopiert dir dein Bild (impliziet geshared), und malt es dann auf deinem Paintdevice.
Das ist sicher fuer nen einzelbild ne gangbare methode.

Wenns auf performance ankommt, wuerd man glaub ich, selber hand anlegen. Je nach "Anforderung" kannst da weitgenug runtergehen von den Schnittstellen her.

1. Wurf waer sicher, du baust selber ein QWidget, was das bild ned Haelt sondern nur einmal aufn Paintdevice "darstellt", sobald es eins bekommt ...

das kannst je nach Anforderung noch viel weiter treiben ...

Bsp, für windows, du nutzt qt unabhangige libs die dir ein Bild einladen und in ein geraetunabhangiges BitMap (DiB) convertieren, dann laesst dir von deinem QWidget das FenserHandle vom client geben, und malst ueber WinApi funktionen selbst auf dem Bereich.
Iss mit sicherheit sehr performant, aber Plattformunabhaengigkeit ade ....


Ciao ...
kater
Beiträge: 306
Registriert: 29. Dezember 2009 01:13
Wohnort: Darmstadt

Beitrag von kater »

Ok danke.
Doppelkeks
Beiträge: 6
Registriert: 27. Oktober 2007 15:51

Beitrag von Doppelkeks »

Ok danke Leute, das Speichersparen bei Leerzustand macht Sinn.
franzf hat geschrieben:
Der Code-Ausschnitt aus der QLabel Klasse haut mich jetzt aber grade um - so wie ich den Sinn des implicit sharings verstanden hatte, dachte ich, dass genau solche Fälle damit vermieden werden sollen - deep copy nur dann, wenn Daten auch wirklich verändert werden, ansonsten internes ref-counting.
Der Verwender von QPixmap hat darauf keinen Einfluss! Shallow Copy immer!
Ah, logisch! Ich hab mich wohl durch das new blenden lassen... :oops:
Antworten