QWebView QWebElement Experiment

1. Teil

In diesem Experiment mit dem QWebView und dessen DOM-Elementen, den QWebElement’s (aus der Qt beta 4.6) untersuche ich die Performance im Falle hochfrequenter Updates auf dem DOM. Ziel ist es herauszufinden, ob sich herkömmliche GUI-Entwicklungen (z.B. mit QtGui) durch eingebettete browserbasierte Ansätze effizient ersetzen lassen, um dadurch in den Genuss der fortgeschrittenen Layoutingfähigkeiten moderner Webbrowser zu gelangen.

Video in Originalauflösung: http://www.pareis.com/images/stories/video/QtWebView-Experiment.mov

Die Testapplikation verwalten im Bereich des C++ Modells Referenzen auf die angeschlossenen View-Elemente im DOM des eingebetteten Browserwidgets. Bei Änderungen an den Modelldaten werden die formatierten Zahleninformationen in die DOM-Elemente geschrieben und anschliessend je nach Änderungstyp die CSS class der Zelle (table cell, td) gesetzt.

void Browser::updateCells() {
    for(int i=0; i<batch; i++) {
        int r = rand() % rows;
        int c = rand() % columns;
        double orig = values[r][c];
        double &now = values[r][c];
        now = 150.0 * rand()/((double)RAND_MAX + 1) - 20.0;
        QString s = QString("%1").arg(now, 0, 'f', 2);
        cell[r][c].setPlainText(s);
        if(now  orig) {
            cell[r][c].removeClass("down");
            cell[r][c].removeClass("norm");
            cell[r][c].addClass("up");
        } else {
            cell[r][c].removeClass("down");
            cell[r][c].removeClass("up");
            cell[r][c].addClass("norm");
        }
        coord cc = {r,c};
        queue.enqueue(cc);
        if(queue.size()>1000/interval/2*batch) {
            cc = queue.dequeue();
            cell[cc.r][cc.c].removeClass("up");
            cell[cc.r][cc.c].removeClass("down");
            cell[cc.r][cc.c].addClass("norm");
        }
        nupdates++;
    }
    elapsed += t.elapsed();
}

Die Ergebnisse können sich sehen lassen. Das eingebettete Browser-Widget bewältigt mehr als 1000 TD Änderungen pro Sekunde. Den limitierenden Faktor stellt der QTimer dar bzw. dessen Signals. Werden aber viele View-Änderungen innerhalb eines einzelnen Qt-Events durchgeführt, bleibt die CPU-Last im sinnvollen Rahmen. Ein sehr positiver Aspekt ist die problemlose Bedienbarkeit der Oberfläche selbst im Bereich der Sättigung.

2. Teil

In einem 2. Versuch habe ich einen direkten Vergleich der Performance zwischen dem embedded QWebView Browser und einer rein Qt-basierten Anzeige vorgenommen. Ausserdem habe ich die Ereignisproduktion in einen separaten Thread ausgelagert.

In der 2. Applikation arbeiten folgende Codebestandteile:

Das Modell emittiert Changes:

void Changer::run() {
    forever {
        if(stopped) return;
        msleep(1000/interval);
        for(int i=0; i<batch; i++) {
            int r = rand() % rows;
            int c = rand() % columns;
            double orig = values[r][c];
            emit changed(r, c, orig,
                values[r][c] = 150.0 * rand()/((double)RAND_MAX + 1) - 20.0);
        }
    }
}

Im embedded QWebView die gleiche Verarbeitung wie oben, leicht modifiziert:

void Browser::updateCell(int r, int c, double orig, double now) {

    QString s = QString("%1").arg(now, 0, 'f', 2);

    QWebElement &ccc = cell[r][c];
    ccc.setPlainText(s);

    if(color->isChecked()) {
        if(now < orig) {
            ccc.setAttribute("class", "down");
        } else if(now > orig) {
            ccc.setAttribute("class", "up");
        } else {
            ccc.setAttribute("class", "norm");
        }
    }
}

Im Qt Widgets basierten Ansatz sieht es fast identisch aus, ausser dass hier keine class-Attribute gesetzt werden (was in Qt auch ginge, jedoch aus Performancegründen nicht in Betracht gezogen wurde) sondern Farbvorgaben gemacht werden, was selbstredend nicht so sehr flexibel ist wie HTML:

void QtBrowser::updateCell(int r, int c, double orig, double now) {

    QString s = QString("%1").arg(now, 0, 'f', 2);

    QLabel *ccc = cell[r][c];
    ccc->setText(s);

    if(color->isChecked()) {
        if(now < orig) {
            ccc->setPalette(red);
            ccc->setAutoFillBackground(true);
        } else if(now > orig) {
            ccc->setPalette(green);
            ccc->setAutoFillBackground(true);
        } else {
            ccc->setAutoFillBackground(false);
        }
    }
}

Threadanalyse

QWebView-threads

Threaddump eines laufenden QWebView

Im browserbasierten Ansatz werden ca. 61% der Zeit mit Repaints, ca. 3.5% mit dem Eventhandling und interessanterweise ca. 30% mit einem asynchronen (timergesteuerten) Layouting zugebracht.

QtWidgets-threads

Threadanalyse Qt Widgets basierte Anzeige

In Fall der Qt Widgets basierten Implementierung werden ca. 76% der Zeit im Repaint und ca. 15% im Eventhandling verbracht.

Fazit

Ich komme nach diesem Experiment also zu dem Schluss, dass der embedded Browser gegenüber dem Qt-basierten Ansatz zunächst erst einmal keine signifikanten Performancenachteile besitzt und sich daher als Basis für die Implementierung von Rich-Client-Applikationen mit C++ anbietet, bei denen es auf sehr gute Grafikfähigkeiten gepaart mit einem hochperformanten Kern ankommt.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s