Implicitly shared Klassen - Wann benutzen und wann nicht?
-
Doppelkeks
- Beiträge: 6
- Registriert: 27. Oktober 2007 15:51
Implicitly shared Klassen - Wann benutzen und wann nicht?
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?
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?
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
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
Logikkater hat geschrieben:Woher weisst du das? Oder hab ich nur mal wieder die wichtigen Stellen in der Doku übersehen.
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();
}-
Doppelkeks
- Beiträge: 6
- Registriert: 27. Oktober 2007 15:51
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.franzf hat geschrieben:Programmiere mit Qt genau so, wie du es mit C++ auch tun würdest!
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
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?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.
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.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.
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:Außerdem verstehe ich noch nicht, warum die QPixmap im Heap und nicht auf dem Stack liegt.
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 QPixmapDas ist doch relativ egal. Normalerweise sollte man seine Member als Value speichern, aber hier ist dadurch einiges gewonnen, wenn man es nicht tut.Eigentlich hält QPixmap doch alle "schweren" Daten selber schon im Heap? Wie gesagt, ich bin Neuling
Aber du kennst die Klasse dann ja schon besser, ich hab nur von der Schnittstelle von QLabel aus argumentiertkater 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.
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 ...
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 ....
Naja, iss ja auch Montag morgen ...
Außerdem verstehe ich noch nicht, warum die QPixmap im Heap und nicht auf dem Stack liegt.
glaub dir geht es um die Zeile ? oder ?d->pixmap = new QPixmap(pixmap); // Kopie![]()
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 ....
"d" ist der PIMPL-Zeiger. Darin liegen die Daten des Labels.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?
Code: Alles auswählen
QPixmap *pixmap;
QPixmap *scaledpixmap;
QImage *cachedimage;
#ifndef QT_NO_PICTURE
QPicture *picture;
#endif
#ifndef QT_NO_MOVIE
QPointer<QMovie> movie;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;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;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.
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...
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...
Naja, dann ist doch klar was sie sich mit dem "new" "erkaufen" !"d" ist der PIMPL-Zeiger. Darin liegen die Daten des Labels.
Wie du siehst, QLabel kopiert dir dein Bild (impliziet geshared), und malt es dann auf deinem Paintdevice.Naja, ist QLabel da jetzt die schlechteste Wahl oder gibt es eine performantere Klasse für diese Aufgabe?
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 ...
-
Doppelkeks
- Beiträge: 6
- Registriert: 27. Oktober 2007 15:51
Ok danke Leute, das Speichersparen bei Leerzustand macht Sinn.

Ah, logisch! Ich hab mich wohl durch das new blenden lassen...franzf hat geschrieben:Der Verwender von QPixmap hat darauf keinen Einfluss! Shallow Copy immer!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.