Tuesday, 23 September 2014

Wayland and Qt 5.4

Since nobody else has done the honors yet, I'm happy to announce that - as decided at the Qt Contributors Summit this year - support for running applications under a Wayland compositor will be seeing its initial release with Qt 5.4. That is, the QtWayland repository is finally going to stop sitting in the corner, sulking. :)

There's a few "buts", though.

Firstly, it should be noted that support for QWidget-Based applications (and other desktop-based usecases) may be far from ideal, and quality may not be great. This is a consequence of most development on QtWayland having been driven from mobile/embedded viewpoints to date, and is not, in general, an inherent limitation on the windowing system. It's also something of a reflection on Wayland itself, which is only now starting to mature for desktop use (through xdg-shell etc etc.)

tl;dr: Think of this as a technical preview, keep your expectations realistic, and if you want to use it, expect to roll up your sleeves a bit and get dirty from time to time.

Secondly, the QtCompositor API in the QtWayland module (allowing you to write your own Wayland compositor) will not be seeing a release at this time. The API is not frozen, and has not seen the usual polish/quality that you might expect from Qt APIs. As this API is only of use to a limited number of people (those looking to implement an embedded/mobile device, typically, or write their own DE) this should not impact too many people.

tl;dr: If you want to write a compositor, you get to keep both pieces if it breaks. If you want to use applications under an existing Wayland compositor, you're fine.

Future work to QtWayland is largely an open story, but some obvious candidates come to mind:

  • Continued work on xdg-shell support
  • Plugin based window decorations (to enable environment-specific look and feel) this has now landed in the 5.4 branch :)
  • Integration with the rest of Qt's autotests (I spent a while getting tests fixed or at least runnable under window-compositor, but it would be nice to automate this)
  • "Official" subsurface protocol support
If there's something you would like to see happen, here or not, you're more than welcome to pitch in. If you'd like to talk to the other people hacking on QtWayland, please pop by on #qt-lighthouse on freenode, and talk to the folks there :-)

I'd also like to take a moment to thank everyone for their contributions to QtWayland. In particular, I'd like to say thanks to the following, in no particular order (and I'm extremely sorry if I've missed someone, please let me know and I'll happily add you to the list):
  • Kristian Høgsberg & Jesse Barnes, for their initial work on the port, sponsored by Intel,
  • Jørgen Lind, Samuel Rødal, Andy Nichols, Laszlo Agocs, and Paul Olav Tvete for continuing work on it excellently and admirably,
  • Nokia for sponsoring a good deal of the development up until their abrupt departure from the Qt world,
  • Digia for continuing to help out after Nokia left,
  • Andrew Knight, for ably shepherding problems encountered by Jolla for quite a long time,
  • Jolla for sponsoring a large chunk of work on QtWayland (past and present),
  • Gunnar Sletta for rewriting integration with rendering (especially QtQuick), removing a large number of bugs & improving performance,
  • Giulio Camuffo for numerous fixes, improvements and interaction with the wider Wayland community.
As a conclusion, I'd like to note that I'm really happy to see this finally happen - I've wanted it for a very long time now - and for Wayland to keep moving on for bigger and better things. Hopefully, this release will achieve its intended result (that more eyes/hands get exposed to the code, and start to use it, and help out with it).

Labels: , , , , , , ,

Friday, 12 September 2014

profiling is not understanding

When software goes slow, generally, the first reaction is to profile. This might be done through system tools (like Instruments on OS X, perf/valgrind/etc on Linux, VTune, etc). This is fine and good, but just because you have the output of a tool does not necessarily correlate to understanding what is going on.

This might seem like an obvious distinction, but all too often, efforts at improving performance focus on the small picture ("this thing here is slow") and not the bigger picture ("why is this so slow"). At Jolla, I had the pleasure of running into one such instance of this, together with Gunnar Sletta, my esteemed colleague, and friend.

As those of you who are familiar with Jolla may know, we had been working on upgrading to a newer Qt release. This also involved quite a bit of work for us, both in properly upstreaming work we had done on the hurry to the late-2013 release, and in isolating problems and fixing them properly in newer code (the new scenegraph renderer, and the v4 javascript engine in particular have been an interesting ride to get both at once!).

As a part of this work, we noted that touch handling was quite slow (something which we had worked around for our initial release, but now wanted to solve properly). This was due to the touch driver on the Jolla introducing touchpoints faster than the display was updating, that is, while the display might be updating at 57 hz (yes, the Jolla is weird, it doesn't do 60 hz) - we might be getting input events a lot more frequently than that.

This was, in turn, causing QtQuick to run touch processing (involving costly item traversals, as well as the actual processing of touch handling) a lot more frequently than the display was updating. As these took so much time, this in turn slowed rendering down, meaning even more touch handling was going on per frame. A really ugly situation.

Figure 1: Event tracing inside the Sailfish OS Compositor
Figure 1 demonstrates this happening at the compositor level. The bottom slice (titled "QThread") is the event delivery thread, responsible for reading events from evdev The peaks there are - naturally - when events are being read in. The top thread is the GUI thread, and the high peaks there are touch events being processed and delivered to the right QtQuick item (in this case, a Wayland client, we'll get to that later). The middle slice is the compositor's scenegraph rendering (using QtQuick).

With the explanation out of the way, let's look at the details a bit more. It's obvious that the event thread is regularly delivering events at around-but-not-quite twice the display update. Our frame preparation on the GUI thread looks good, despite the too-frequent occurrence of event delivery, though, and the render thread is coping too.

But this isn't a major surprise - the compositor in this case is dead simple (just showing a fullscreen client). What about the client? Let's take a look at it over the same timeframe...

Figure 2: Event tracing for the client (Silica's component gallery, in this case)
Figure 2 focuses on two threads in the client: the render thread (top), and the GUI thread (bottom). Touch events are delivered on the GUI thread, QtQuick processes them there while preparing the next frame for the render thread.

Here, it's very clear that touch processing is happening way too often, and worse than that, it's taking a very long time (each touch event's processing is taking ~4ms), not leaving much time for rendering - and this was on a completely unloaded device. In a more complicated client still, this impact would be much, much worse, leading to frame skipping (which we saw, on some other applications).

Going back to my original introduction here, if we had used traditional profiling techniques, we'd have seen that touch handling/preparation to render was taking a really long time. And we might have focused on optimizing that. Instead, thanks to some out-of-the-box thinking, we looked at the overall structure of application flow, and were able to see the real problem: doing extra work that wasn't necessary.

As an aside to this, I'm happy to announce that we worked out a neat solution to this: QtQuick now doesn't immediately process touch events, instead, choosing to wait until it is about to prepare the next frame for display - as well as "compressing" them to only deal with the minimal number of sensible touch updates per frame. This should have no real impact on any hardware where touch delivery was occurring at a sensible rate, but for any hardware where touch was previously delivering too fast, this will no longer be a problem as of Qt 5.4.

(Thanks to Gunnar & myself for the fix, Carsten & Mikko for opening my eyes about performance tooling, and Jolla for sponsoring this work.

P.S. If you're looking for performance experts, Qt/QML/etc expertise or all round awesome, Gunnar and myself are currently interested in hearing from you.)

Labels: , , , , , , , , , , , , ,

Wednesday, 13 August 2014

sailing in search of fresh waters

I've had a long, quiet time on this blog over the past few years while I've been frantically helping Jolla to launch their self-named product: the Jolla. I've enjoyed (almost) every day I've been there: they really are a great bunch of people and the work has been plentiful and challenging.

But as the saying goes, "this too shall pass". Nothing lasts forever, and it's time for a change: after this week, I will be taking a break from Jolla to get some fresh perspective.

On the bright side, maybe I'll have some more time for writing now :)

If anyone is interested in getting a hold of a C++/Qt/QML/Linux expert with a focus on performance, expertise on mobile, and a wide range of knowledge across other areas who loves open source, please let me know.

Labels: , , , , , , , ,

Wednesday, 18 July 2012

Qt 5 and Android

Astute observers of the Qt 5 repositories may have noticed that for quite a while, patches have been trickling in from me allowing Qt 5 to compile on Android.

The goal in mind was to allow use of Qt on Android primarily in order to work at the system level (not using the regular Android display stack, but using Wayland on Android) - tying in with Collabora's other work on Android, but this work also doesn't preclude someone from e.g. implementing a platform plugin to allow Android applications to run natively on unhacked devices, similar to Necessitas on Qt 4 - and I'd very much like to see that happen upstream.

In terms of compilation, there is one approach currently upstreamed that involves using the NDK, see this wiki page for more information. You'll note it's quite easy to do a build yourself, something that was quite intentional, since I figure that the only way it's going to improve easily is if it is easy to hack on it. I'm sure the build & installation instructions can be more optimal still (like installing to /system/lib, etc) but it's a start. Contributions welcome. I should also take a moment to thank the Necessitas guys, their mkspecs provided a nice starting point.

I had started an alternative route of integrating Qt with Android image builds (so, check out the Android tree, repo sync, drop Qt in place, run 'make' and have it built & deployed for you), but unfortunately, my sponsored time to work on this ran out, and so I wasn't able to finish it. It's still an interesting area of work, and so, I do plan to try continue it in my spare time.

In terms of actually using it, one area which is a bit of pain still, is that there's a bug in the way bionic's linker handles R_ARM_COPY relocations - instead of looking up the symbol to copy in the shared libraries the binary depends on, it finds the binary's symbol instead, meaning it doesn't really do any actual relocation.

The symptom of this is that your binary will crash on start due to things being zero'd out that really shouldn't be (like QObject::staticMetaObject in my case), depending on how it's been built. Thanks to Thiago for helping me nut that very difficult problem out. There is a patch pending on Android's gerrit instance, but I need to find the time to go rebase the patch and retest it to make sure it still works, although the code changes in the area look quite trivial.

For those of you who are visually oriented: I'm sorry, but there's not much to show here, because - as of yet - I don't have anything graphical running. Though in theory, it might be already possible to easily shoehorn Wayland libraries into the NDK using Pekka's work, and build QtWayland that way. But if anyone wants to talk Qt on Android, or better still, contribute, I'm all ears.

Massive kudos to Collabora for sponsoring my work on this!

Labels: , , , , , ,

Wednesday, 30 May 2012

writing a layout in QML

Sometimes, for whatever reason, the layouts provided "out of the box" in QML just don't cut it. lately, I've been doing a few rather different things for experimentation and learning purposes that have meant I've run into quite a lot of these cases.

when this happens, the first instinct is to fall into despair - but there's really no need for that. writing your own layout really isn't that hard. Here's a small, fairly self contained example doing just that.

MyLayout.qml:
import QtQuick 2.0

Item {
    id: layout

    property bool ready: false

    onChildrenChanged: performLayout()
    onWidthChanged: performLayout()
    onHeightChanged: performLayout()

    /* the meat of the layout */
    function performLayout() {
        /* nothing to layout? don't bother then */
        if (layout.children.length == 0)
            return

        var currentX = 0

        console.log("DOING LAYOUT FOR " + layout.children.length + " ITEMS")

        /* first real step of doing anything: go over all the children */
        for (var i = 0; i < layout.children.length; ++i) {
            var obj = layout.children[i]

            /* in the real world, we'd probably do something a lot more complex,
             * but let's just position our children along a row.
             */
            console.log("Positioning at " + currentX + " to " + (currentX + obj.width))
            obj.x = currentX
            currentX += obj.width
        }

        console.log("LAYOUT DONE")
    }
}

MyItem.qml:
import QtQuick 2.0

Rectangle {
    width: 100
    height: 50
    color: "black"

    Rectangle {
       width: 90
       height: 40
       anchors.centerIn: parent

       color: "red"
    }
}

main.qml
import QtQuick 2.0

Rectangle {
    width: 1000
    height: 100

    MyLayout {
        MyItem { }
        MyItem { }
        MyItem { }
        MyItem { }
        MyItem { }
        MyItem { }
        MyItem { }
    }
}

This is obviously very simplified for demonstration purposes, to name a few things that it doesn't do:
  • it omits things like margins, wrapping
  • it will break if MyItem is ever anchored
  • it doesn't use anchoring (which might make for a more optimal implementation in this particular case - at the least, it wouldn't have to relayout if the width/height of the layout changed)
  • it doesn't relayout if the geometry of the children change
  • it relayouts whenever properties changes, which isn't optimal if e.g. the layout is animating a change, instead, it should delay a relayout using a Timer

All of these are left to the reader, but hopefully it's of some help in getting started.

Labels: , , , , , ,

Wednesday, 21 March 2012

on the importance of doing nothing

I've been meaning to write about this for a while, but I've only just now been driven over the edge by having to go and basically run sed over code again for no good reason.

When you're programming, always make sure you question *why* things are done. Qt provides three functions, helpfully named qMalloc/qRealloc/qFree. Despite the 'q' in front of their names, these functions do absolutely nothing useful, they just wrap around their stdlib friends. This was originally done to enable replacement of the allocator inside Qt (but there are better ways to do that, without getting sidetracked from my central point), but in reality, doesn't have much use. That's why I'm trying to deprecate them.

Now, you might ask, "what impact could a simple function call have, anyway"? I'm glad you asked. Benchmark time (spoiler for the lazy: ~10% extra overhead for small allocation sizes, ~0-5% for larger allocation sizes).
virgin:~/mallocbench% cat main.cpp
#include <QtCore>
#include <qtest.h>
#include <qcoreapplication.h>
#include <qdatetime.h>

class MallocBenchmark : public QObject
{
Q_OBJECT
private slots:
    void qtMalloc();
    void qtMalloc_data();
    void regularMalloc();
    void regularMalloc_data();
};

void MallocBenchmark::qtMalloc_data()
{
    QTest::addColumn<int>("size");
    QTest::newRow("1") << 1;
    QTest::newRow("10") << 1;
    QTest::newRow("100") << 100;
    QTest::newRow("10000") << 10000;
    QTest::newRow("1000000") << 1000000;
    QTest::newRow("10000000") << 10000000;
}

void MallocBenchmark::qtMalloc()
{
    QFETCH(int, size);

    QBENCHMARK {
        void *p = ::qMalloc(size);
        ::qFree(p);
    }
}

void MallocBenchmark::regularMalloc_data()
{
    qtMalloc_data();
}

void MallocBenchmark::regularMalloc()
{
    QFETCH(int, size);

    QBENCHMARK {
        void *p = malloc(size);
        free(p);
    }
}

QTEST_MAIN(MallocBenchmark)

#include "main.moc"


And now, the results on my machine:
********* Start testing of MallocBenchmark *********
Config: Using QTest library 5.0.0, Qt 5.0.0
PASS   : MallocBenchmark::initTestCase()
RESULT : MallocBenchmark::qtMalloc():"1":
     0.000059 msecs per iteration (total: 62, iterations: 1048576)
RESULT : MallocBenchmark::qtMalloc():"10":
     0.000062 msecs per iteration (total: 66, iterations: 1048576)
RESULT : MallocBenchmark::qtMalloc():"100":
     0.000087 msecs per iteration (total: 92, iterations: 1048576)
RESULT : MallocBenchmark::qtMalloc():"10000":
     0.000083 msecs per iteration (total: 88, iterations: 1048576)
RESULT : MallocBenchmark::qtMalloc():"1000000":
     0.0043 msecs per iteration (total: 72, iterations: 16384)
RESULT : MallocBenchmark::qtMalloc():"10000000":
     0.0063 msecs per iteration (total: 52, iterations: 8192)
PASS   : MallocBenchmark::qtMalloc()
RESULT : MallocBenchmark::regularMalloc():"1":
     0.000053 msecs per iteration (total: 56, iterations: 1048576)
RESULT : MallocBenchmark::regularMalloc():"10":
     0.000051 msecs per iteration (total: 54, iterations: 1048576)
RESULT : MallocBenchmark::regularMalloc():"100":
     0.000082 msecs per iteration (total: 86, iterations: 1048576)
RESULT : MallocBenchmark::regularMalloc():"10000":
     0.000076 msecs per iteration (total: 80, iterations: 1048576)
RESULT : MallocBenchmark::regularMalloc():"1000000":
     0.0043 msecs per iteration (total: 71, iterations: 16384)
RESULT : MallocBenchmark::regularMalloc():"10000000":
     0.0060 msecs per iteration (total: 99, iterations: 16384)
PASS   : MallocBenchmark::regularMalloc()
PASS   : MallocBenchmark::cleanupTestCase()
Totals: 4 passed, 0 failed, 0 skipped
********* Finished testing of MallocBenchmark *********

Around 10% extra time per iteration on smaller allocation sizes, 0-5% on larger sizes (most likely explained by glibc falling back to using mmap for larger allocations, which is going to take an awful long time compared to a single function call). These, obviously, aren't huge numbers. But remember: this is overhead you're taking for no reason at all. Don't do it. Your CPU cycles will thank me.

Labels: , , , , , , , ,

Monday, 19 March 2012

Qt 5.1, aka: when QFileSystemWatcher might not be so useless


With 5.0 now being feature frozen, I thought I'd turn myself towards something I've been meaning to do (and talking about doing) for a very long time. Back before the Qt Project launched, even before Qt Contributors Summit 2011, in fact. I thought I'd make QFileSystemWatcher more useful.

Let's review what QFileSystemWatcher offers: a way to add and remove paths for monitoring directories and files, a directoryChanged(path) and a fileChanged(path) signal. That's it. If you think about this for a moment, you realise just how broken the semantics of the signals are: what _is_ a 'change' anyway? I guess this is one of the reasons that QFileSystemWatcher has been called 'deprecated' (I fixed one of the others, a performance issue, a while back).

So I've been working on making the signals a bit less useless. In the longer run, I plan to call fileChanged and directoryChanged deprecated. They'll still be emitted (of course) to keep existing code working, but in addition, you'll have (subject to review):

pathCreated(path) - emitted when something is created inside a directory you are monitoring, or if something that didn't exist that you were monitoring for is created (more on this later)
pathDeleted(path) - emitted when something is deleted inside a directory you were monitoring, or something that you _were_ monitoring was deleted
fileModified(path) - emitted when a file you were monitoring is modified (attributes or contents)

I also have early plans to introduce a pathMoved(oldLocation, newLocation), but that one has a lot of caveats: it might only work on certain platforms, in certain phases of the moon, and only if you're very lucky - on many platforms, it will likely continue to be synthesised as a pathDeleted(oldPath) and pathCreated(newPath) (if you're watching the new location).

I also have vague ideas about introducing more syntactically friendly API over the top of this, something like:

QPathMonitor pathMonitor(myPath);
connect(&pathMonitor, SIGNAL(deleted()), SLOT(watchedPathDeleted()));
... etc ...

but that's a bit farther away in that I haven't really thought it through, yet.

In working on the new signals on Windows, I stumbled across QTBUG-2331, which sort of proves just how useless the existing signals were: deleting a watched directory on Windows essentially never removed the watch, because it didn't notice the deletion. Hopefully, it will be fixed soon, because it's going to block the work on the new signals: otherwise, pathDeleted will never be emitted on a watched path on Windows, and that makes my unit tests sad.

I noted earlier that I have plans to emit pathCreated when you monitor a non-existent path, well, that's correct - I intend to allow just that. None of the native APIs (that I know of) allow for recieving events when you monitor a non-existent path, which is a bit sad, as it means we'll need to fall back to polling on a timer for those cases, but it's certainly much better than nothing.

Oh, and if you're curious, it seems like Linux has the best filesystem monitoring API, in the form of inotify. OS X/BSD comes in second with kqueue (though having to open file descriptors to do the actual monitoring is a bit crap, and not being able to get any sort of real fine-grained notifications on _what_ was added/removed in a directory is also painful). Windows is also incredibly painful due to running into many stupid limitations (like only some ~60 paths being able to be monitored per thread, so the backend spawns loads of threads if you monitor a lot of paths), and not being able to get even remotely useful signals without a lot of extra legwork. Not to mention the above bug, where signals are emitted before deletion.

OS X may get more love in the future, as there is talk about another contributor revisiting the state of the FSEvents backend (which has been disabled for a very long time, and the code removed in Qt 5, due to being massively buggy and unmaintained).

For Windows, ReadDirectoryChangesW might improve the situation, somewhat, but I certainly don't have the time to investigate it for 5.1, and I also lack the motivation, not being a Windows user myself. Contributions welcome?

Labels: , , , , , ,

Sunday, 22 January 2012

QFileSystemWatcher internals in Qt 5

Just thought I'd share some details on some of the recent changes I've pushed to Qt 5 a few weeks ago. (Yes, this post is rather overdue, I've been a bit slack with writing it). If you were in Tampere when I gave a short, completely underprepared Q&A on Qt 5 a few days ago, this won't be news to you, but I will go into a bit more detail.

tl;dr, all in all, a lot of code was deleted, and things still function more or less the same, except a bit better. That's quite a common story for Qt 5, I hope... :)

First of all, platform support: as with Qt 5 itself, Symbian support is no longer a goal. Since I wanted to make some changes to internals, and wasn't able to even remotely come close to building the Symbian code, it was removed.

On Linux, the (ancient, and no longer used by default) dnotify backend also met its maker. Since inotify has been around for some 6-7 years, it was about time, especially as the dnotify backend had some interesting bugs in behaviour.

The OS X FSEvents backend (also unused for quite some time, due to bugs, and not being a recommended way of working apparently) joined to make for a trinity of dead implementations. OS X's watching is survived by kqueue, which it shares with BSD platforms.

The currently supported backends are:
  • inotify (on Linux)
  • kqueue (on BSD and OS X)
  • WaitForMultipleObjects on Windows, which I need to become more familiar with. Not having a Windows machine has meant that I'm not really able to do much here...
Aside from backend support, there were some more 'fun' changes which went in. First, some detail on implementation. Each QFileSystemWatcher has an 'engine' associated with it, which is backend-specific, and does the actual monitoring. The backend is responsible for communicating changes to the 'frontend' QFileSystemWatcher, which then sends the notifications to the API user.

In the past, QFileSystemWatcher engines used to be run in a thread. I'm not sure why this was done originally, but it pretty much never made much sense - monitoring file changes is not a particularly intensive operation, so this is just a waste of resources (thread stack, time to start the thread, etc) - which was compounded by this being a thread per engine, meaning that if you have a few different libraries monitoring files, they'd each start their own thread.

Another nasty side effect of this thread was resource consumption caused by monitoring. If you monitored a large number of paths, but couldn't consume events faster than the OS was throwing them at you, then that engine thread would happily sit there and keep on reading them and turning them into Qt signals for the QFileSystemWatcher/user code. But because that code was on a different thread, and unable to keep up, you'd just keep getting more, and more, and more signals, and memory usage would keep growing and growing.

This thread has now been removed, so changes are implicitly rate-limited to the thread the QFileSystemWatcher lives in, meaning that all of these are no longer a problem. Kudos should also go to Bradley Hughes for fixing a few issues which I missed on platforms other than Linux after it was integrated.

Brad also took this work a step further: QFileSystemWatcher has never been documented as being thread-safe, but the engines may have happened to be more or less thread-safe thanks to living on a different thread to the QFileSystemWatcher, through mutexing. One part inside Qt itself actually needed this for autotests to function correctly, too: QFileSystemModel. He fixed this requirement, and was thus able to remove the mutexes from the engines. Thanks!

I'd also like to thank Brad, João Abecasis, and anyone I've forgotten for helping to review these changes and get them integrated.

(One thing I neglected to mention above - the thread story is a little more complicated on Windows. Windows still has threads inside the engine (although the engine itself is no longer a thread, so there's still one less). This is necessary because WaitForMultipleObjects can only process up to MAXIMUM_WAIT_OBJECT handles at a time, unless you use multiple threads to do the monitoring, so that's exactly what it does. It spawns multiple threads on-demand as soon as it can't find a thread with a spare slot. But this is nothing new.)

Labels: , , , , , ,

Thursday, 1 December 2011

why I avoid QRegExp in Qt 4 and so should you

A few times, when I've been working on something performance-critical, I've had people suggest (or ask me to review code) using QRegExp. I usually tell them that "this is a bad idea, QRegExp is slow/unmaintained", but I never actually sat down to do benchmarks. Well, in Qt 5, the subject of replacing QRegExp has been discussed a bit, and we have a volunteer: Giuseppe D'Angelo, a Qt hacker and italian student living in the UK (looking for work, by the way!).

One of the first steps he has taken has been to quantify the performance of two of our leading candidates for replacements: PCRE, and ICU's regex engine. He also took the liberty of benchmarking Qt's existing QRegExp - here's the results:

(Caveat: Giuseppe has asked for someone more familiar with ICU to look over the code there to make sure the results aren't negatively impacted.)


RESULT : REBenchmark::PCREBenchmark():"URI":
     1,123 msecs per iteration (total: 1,123, iterations: 1)
RESULT : REBenchmark::PCREBenchmark():"Email":
     1,798 msecs per iteration (total: 1,798, iterations: 1)
RESULT : REBenchmark::PCREBenchmark():"Date":
     99 msecs per iteration (total: 99, iterations: 1)
RESULT : REBenchmark::PCREBenchmark():"URI|Email":
     2,650 msecs per iteration (total: 2,650, iterations: 1)

RESULT : REBenchmark::ICUBenchmark():"URI":
     11,674 msecs per iteration (total: 11,674, iterations: 1)
RESULT : REBenchmark::ICUBenchmark():"Email":
     17,056 msecs per iteration (total: 17,056, iterations: 1)
RESULT : REBenchmark::ICUBenchmark():"Date":
     392 msecs per iteration (total: 392, iterations: 1)
RESULT : REBenchmark::ICUBenchmark():"URI|Email":
     30,552 msecs per iteration (total: 30,552, iterations: 1)

RESULT : REBenchmark::QRegExpBenchmark():"URI":
     21,579 msecs per iteration (total: 21,579, iterations: 1)
RESULT : REBenchmark::QRegExpBenchmark():"Email":
     55,426 msecs per iteration (total: 55,426, iterations: 1)
RESULT : REBenchmark::QRegExpBenchmark():"Date":
     1,357 msecs per iteration (total: 1,357, iterations: 1)
RESULT : REBenchmark::QRegExpBenchmark():"URI|Email":
     77,224 msecs per iteration (total: 77,224, iterations: 1)

(These tests were run on: Ubuntu 10.04 LTS 32 bit, Core(TM)2 Duo CPU P9600  @ 2.66GHz, 4GB RAM, GCC 4.4.3, Qt 4.7.4, PCRE 8.20, ICU 4.8.1.1)

As we see from these results, PCRE is the clear winner, probably thanks to its use of JIT in evaluating the expressions. We see ICU showing decent results, and getting second place (see the caveat above). Leading the rear of the pack, we see QRegExp, 10x-30x slower than PCRE - and these are hardly complicated regular expressions.

In conclusion, for now, if you want performance, you should probably steer clear of QRegExp, and find other ways to do what you want, either using QString directly if it's a simple job, or looking into a light shim around another regex engine until Qt 5, when hopefully, your problems will go away with a shiny new class.

Labels: , , , , , , , ,

Thursday, 10 November 2011

debugging connmand

just so it's out there on the intarwebs, since it cost me a few minutes of head scratching: when you're trying to debug connmand, -d isn't enough. You need to use -n (nofork) too, otherwise you'll be left with a console, wondering why you aren't seeing the magics happening.

Labels: ,

Monday, 21 February 2011

daily hack: library dependency trees

Many a time, ok, well, quite frequently at least I've needed to see what is bringing a dependency into an application or a library I'm working on. At the moment, I'm working on removing some useless dependencies for performance and misc reasons, so I'm finding information like that very helpful.

So, scripting to the rescue.

Use is simple:

burchr@virgin:~/code/scripts(master+)% genlibtree.rb /usr/lib/libQtCore.so
Dependency tree for /usr/lib/libQtCore.so
 /usr/lib/libQtCore.so
* /usr/lib/libpthread.so.0
* /usr/lib/libz.so.1
* /usr/lib/libdl.so.2
* /usr/lib/libgthread-2.0.so.0
** /usr/lib/libglib-2.0.so.0
** /usr/lib/librt.so.1
** /usr/lib/libc.so.6
* /usr/lib/libstdc++.so.6
** /usr/lib/libm.so.6
** /usr/lib/ld-linux.so.2
** /usr/lib/libgcc_s.so.1

There's a few gotchas in that it won't list repeated dependencies, only searches /usr/lib, etc - but it more or less works. Patches welcome. I hope you find it useful in your quest to remove unneeded dependencies - it is worthwhile running it over your work, at least, to see what bloat you're pulling in.

(Much thanks to many folks, but especially Thiago Macieria of Qt and other fame, for giving me a bit of a crash course in some of how shared libraries work under the hood.)

edit: I should probably note that this may not necessarily provide a full picture. Shared objects may still be loaded at runtime, and in the case of QtGui, are in the form of things like image plugins and other things. It's still useful, though.

edit 2: since I've had a few people point out that it is horrific ruby, I'd just like to note that yes, I am not in fact a perfectionist when it comes to throwaway tools I write in five minutes. Patches welcome. ;)

Labels: , , , , , ,

Monday, 17 January 2011

Qt: users of qmake, rejoice!

Those of you who have used qmake to build projects for any length of time on Linux will have probably come across the PKGCONFIG feature by now.

For those of you who haven't, here's a quick recap:
CONFIG += link_pkgconfig # enable the PKGCONFIG feature
PKGCONFIG += glib-2.0 # link against glib-2.0
While this looks simple, it has two small problems which aren't all that fun.

Firstly, it had what was really quite easily arguably a bug in that it didn't halt the qmake process if a requested package didn't exist. It would happily tell you it didn't exist, and then continue as if nothing had gone wrong, a very annoying problem in large projects:
$ cat test.pro
CONFIG += link_pkgconfig
PKGCONFIG += foobar

$ qmake test.pro:
Package foobar was not found in the pkg-config search path.
Perhaps you should add the directory containing `foobar.pc'
to the PKG_CONFIG_PATH environment variable
No package 'foobar' found

$ echo $?
0

Secondly, it wasn't possible to (easily) check for optional dependencies and enable/disable functionality depending on the result of that check.

This has now changed! Thanks to Oswald for merging merge request 1022 into Qt, PKGCONFIG will, in the future, stop on error, and (even more fun) - we can now use the shiny new qmake packagesExist() test function to detect whether a library is installed:
CONFIG += link_pkgconfig

packagesExist(glib-2.0) {
    DEFINES += HAS_GLIB
    PKGCONFIG += glib-2.0
}

// and in the code:
#ifdef HAS_GLIB
    // use glib here
#endif
Much thanks to Marco for prompting me to finally try fix this (I've eternally been annoyed by it, but someone else being frustrated by it made me leap into action), as well as to Oswald, Marius and Arvid for their review and comments during writing, and finally to Murray and Mathias for respectively reporting and trying to patch the first issue in this post, a fact I only became aware of *after* finishing my patch. Great minds think alike, it seems!

Labels: , , , , , ,

Tuesday, 30 November 2010

Coding antipatterns: excessive nesting

With apologies to Dave Neary for stealing his excellent word, 'antipatterns', from his talk at MeeGo Conference.

This is an opinion piece. Feel free to skip it if you already know what you're doing when writing code.

We all have things we dislike in code, sometimes it's indentation, sometimes it's a bit less trivial. One of my current pet hatreds is excessive nesting.

For those of you that don't know what I mean, if you see something like this:
void foo(bool bar, bool lol, bool hax, bool meep)
{
    if (bar) {
        if (lol || hax) {
            if (meep) {
                // do something
            }
        }
    }
}

Then you're probably a victim of excessive nesting.

If you're writing code like this then all I can say is you're probably doing it wrong.

That above example might be written better like so:
void foo(bool bar, bool lol, bool hax, bool meep)
{
    if (!bar)
        return;

    if (!lol && !hax)
        return;

    if (!meep)
        return;

    // do something
}

Generally speaking, you want your code to be like a river: to flow from one point to another, and branch off gracefully, but still continue flowing nonetheless.

In more technical terms, this makes it a lot easier to take appropriate actions at any of those junctions (if, say someone wants logging added for all of those returns in future) and makes your code a lot easier to read.

Please stop and think about what you're doing, and refactor if necessary rather than blindly adding another conditional. It might be easier to add another level of nesting, but you'll suffer in the long term. Think about it.

As a general rule of thumb, I personally think if you have more than three levels of nesting, you might need to think whether you need a new method, or whether you need to rethink your code's flow.

Labels: , , , ,

Friday, 5 November 2010

Qt on Skia

The astute, regular readers I manage to have might have wondered why I have recently been playing with Skia, Google's 2D graphics rendering library, notably used in Chromium and Android. Well, now that my experiment is in a semi-usable state, I decided that it's probably about time I let the cat out of the bag and started talking about what I've been doing.

Over the past week, I've been diving into the Qt graphics stack a bit, for my own fun and amusement, to learn more about how it works and what makes it tick. This has been my first dive into graphics, so it's been a bit of a learning curve containing many gems like:
  • "transform? what's a transform"
  • "clipping? I know I need a haircut, but..."
  • "oh, you mean I actually have to store ARGB values for everything, and the order differs from toolkit to toolkit, and in some cases, from big/little endian?"
But I think I've made some progress in figuring things out slowly. And getting my project of choice, Qt rendering using Skia, to work, slowly but surely - a picture is worth a thousand words, so without further ado:
(Qt's demos/browser/, using Skia rendering)

Of course, it's still not done. There's a lot to implement (proper text rendering not using paths, clipping, supporting QBrush formats other than solid color fills...) and a lot of bugs to fix, as is visible in the screenshot above - and then the work starts in earnest to make this fast, but I'm happy with the progress so far over the past week.

As for my intentions? Well, I don't know. First, we'll have to see if I can get it finished, then, see how it performs vs Qt's own internal raster engine. If it's faster, then perhaps there's reasons to talk to the Qt graphics folks about whether it's worthwhile to include in Qt or not.

Even if this code doesn't make its way into Qt, I'm confident that there will be some valuable lessons and contributions from it. I do plan to write up another post on how to introduce a new graphics system into Qt, and I also have some notes to improve documentation for things like QPaintEngineEx.

If you're interested in tracking my progress, keep an eye on my skia-master branch on Gitorious. Note that if you want to try it yourself, for now you'll need the Qt branch of my Skia clone on github (note that this will only work on x86 with SSE2 support for now), because Skia's upstream SVN repository doesn't have an up to date/working buildsystem, and also contains a few problems that block compilation. I'm aware that this isn't really end-user-friendly, or a very sensible approach, but it will do for the short term. Just build it and copy libskia.a into /src/plugins/graphicssystems/skia/.

I'd like to thank a few people for their assistance:

  • Andreas Kling, for nudging me to indulge my insane self and actually try this
  • John Brooks, for giving me a crash course on how images are actually stored in memory
  • Samuel Rødal, for walking me through the internals of Qt's graphics guts
  • Zack Rusin, for his discussion/help with implementing complex non-joined sub-paths in QSkiaPaintEngine::fill()

I'll leave you with some screenshots showing the earlier work at getting things rendered, just for fun.




the first thing Qt on Skia drew, a test circle and a border. getting somewhere more useful, we rendered some blocks!




Qt on Skia manages to draw shapes in mostly the right place! Qt on Skia draws pixmaps for the first time

Labels: , , , , , , ,

Tuesday, 2 November 2010

Dear Google,

I've now wasted spent a highly enjoyable day trying to build Skia. Your provided, autotools build system is out of date and doesn't work (and let me assure you, dear third party reader, that this issue isn't the only one), which isn't really surprising since the last time you appear to have updated your build system was in March 2009, and there's a large part of your source that it doesn't cover (e.g. everything outside /src/core/). I don't really have the time (or know autotools), otherwise I'd try fix this.

So, I think to myself, Chromium uses Skia. Surely I can build Skia from a Chromium checkout. And you can!

But don't forget to wave a dead chicken, remember to *also* build and copy libskia_opts.a (not just libskia.a), and figure out how to remove parts of Chromium from also being used/linked in with the third party Skia source.

I'm up to that last part, in case you wondered.

Labels: , ,

Sunday, 5 September 2010

facebrick: end of the line for me

Hi,

As many of you might have noticed in recent times, facebrick has ceased to work. This is (obviously) unfortunate, but mostly out of my control. I have attempted to look into this, without any luck, and at this point, I've decided to make it public that I have no desire to further work on Facebrick.

Facebook seem to have changed something on their API, which has caused Facebrick to cease to function, with an error about invalid source IP on the client end (but this is actually misleading: the real problem is that the session proxy appears to no longer work, constantly returning error 100 - invalid parameter).

Now, the why of why I no longer want to work on Facebrick:

  • I no longer (really) use Facebook as often as I did

    I consider other services like Twitter to be (mostly) better oriented towards the way I used Facebook, and they are also much less woeful at trying to make my private data public
  • As a result of the above, I no longer really use Facebrick

    I have been helping brik to keep writing code under GSOC and afterwards, but I haven't really done any development of my own for quite some time.

    This has partly been a result of time constraints (so much to do, so little time) - but a lot of it is simply that I don't use Facebook any more, and hence, have no need or desire to write a Facebook client for mobile usage.
  • Facebook's developer documentation is woeful

    This was bad when I initially started writing Facebrick, but now it's gotten rather critical. Facebrick needs to use what is called a 'session proxy' to talk to Facebook, yet Facebook's example doesn't actually work. (How they expect a third party to squeeze gold from a pile of crap is beyond me...) - see http://wiki.developers.facebook.com/index.php/Session_Proxy

    I (and many others) rolled our own session proxies (for example, http://www.n8gray.org/blog/2010/06/09/a-simple-facebook-session-proxy-for-google-app-engine/) - however, theirs, like mine, all seem to no longer function.

    This bad attitude towards documentation is also evident in things like occasional references to svn.facebook.com, which (helpfully!) stopped existing a long time ago, possibly replaced by github.com/facebook (but none of the documentation will mention that), and likewise, none of the documentation will tell you that the PHP SDK available there is nowhere near the original PHP Facebook API, etc.


I'd like to stress that this has nothing at all to do with my lack of desire to support MaemoMeeGo, it's purely related to the above (my frustrations with Facebook's closed mindedness and bad documentation, however, being the straw that has broken the camel's back). I still use my N900 and am as happy as ever with it.

I'm open to continuing to host the server end of Facebrick, if anyone can get it working again. If you'd like to help, your best bet is to join in the forums thread.

Labels: , , , , ,

Friday, 27 August 2010

Another day, another itch.

(First, an apology in advance, this is a bit of a braindump, and as such, isn't very well structured.)

I've recently been diving into SQL support in Qt, provided by the QtSql module. Specifically, I've been looking into providing IPC on top of QSqlTableModel.

(For those of you who aren't too technically oriented, I'm trying to have multiple processes have the same basedata store, and know when another process modifies the data in some way, to keep all processes in sync).

While I won't go into details on that now, as it isn't complete, it has been an interesting journey, and I think I've found yet another few things I want to add to my todo list. You see, QSqlTableModel is a great class, but it has a number of shortcomings.

It can't be used in a tree

This one is pretty obvious. It is, after all, a table - not a tree. However, making it represent a tree wouldn't be all that difficult I think (a customised implementation of QAbstractItemModel::parent() method looking at e.g. the 'parent' column of a table and returning a QModelIndex to the appropriate row or similar).

I don't believe that this can be solved in the current implementation, though, as QSqlTableModel inherits QSqlQueryModel which inherits QAbstractTableModel - meaning there's a non-trivial amount of tableness about the current implementation.

For some data (think a task manager: you might have subtasks under a task), this is an annoying limitation.

Incremental fetching isn't always supported

I'm actually not positive on this point, but it looks like incremental fetching is left up to the database driver, meaning that on some database types, you won't have incremental fetching.

On a large database containing thousands of rows (or gigabytes of data) this could be a very bad thing if I am correct.

Partial updates aren't really supported

If you delete a row from a database table, what would you expect the client to do after deleting that row?

I know I certainly wouldn't expect it to re-run the query to refetch the data, yet that's exactly what QSqlTableModel seems to do. Not nice.

This also happens on any other conditions of alteration to the data, which strikes me as being rather inefficient.

Furthermore, it isn't really possible to update values on the fly; this proved to be quite an annoyance when implementing IPC notifications of updates. QSqlQueryModel internally contains a QSqlQuery. QSqlQuery has a ::value() method, to retrieve a QVariant containing the data in a given row of a result set. It doesn't, however, provide any method to change that data (even in memory), meaning that once again, a full reload is the only real solution to something else changing the data out from under you.

This isn't really so unexpected, as it's a bit of an odd use-case, but it has made my implementation trickier than I'd wish.

Conclusion?

I wish. I don't know what to do, yet, but I suspect it's going to involve writing code.

Thoughts welcome.

Labels: , , , , ,

Sunday, 8 August 2010

Strings and Qt

One thing which comes up quite often when I'm talking to developers new to Qt is the topic of strings, more specifically, character encoding: how to do it right, what options are available, and best practices.

Having written this a few times now, I thought that perhaps it was about time I write it up in a more permanent location (here) in the hopes that people will stumble across it and magically become enlightened, and end world hunger. ;)

Qt (and C++) have a number of different string types.

QString

Qt has a string type in QtCore called QString. QString, internally, stores data in utf16, and *does* have knowledge of character encoding.

Services across a network (like web services) often want data in utf8. QString, however, stores data in utf16. To get to utf8, you want QString::toUtf8(). To convert from utf8 back to utf16 QString (e.g. parsing input from a web service) see, QString::fromUtf8().

std::string

C++ also has std::string (although you won't find a lot of this in Qt applications). Simply put, it's a wrapper around a C string providing convenience operations and nicer syntax. It still doesn't have such (fairly essential) things like character encoding.

You probably want to avoid using this in an internationalized application or one requiring interaction with network services unless you find your own solution for encoding issues.

QString has ::toStdString() and ::fromStdString() methods if you must use them for whatever reason.

C strings (char*)

Finally, you have C strings (char*) which don't have any idea what encoding is, they are just a bunch of bytes.

Generally speaking, they're latin1 encoded (ASCII), to put them into a QString.. QString::fromLatin1(). If they aren't latin1, see QTextCodec::setCodecForCStrings().

QLatin1String class is also helpful - in particular, this will allow you to compile when using QT_NO_CAST_FROM_ASCII (which itself is helpful to make sure you explicitly give encodings for all of your strings).

Labels: , , , , ,

Tuesday, 27 July 2010

Tracking QSharedPointer leaks

Smart pointers are a great thing. When used properly, they can really help make life easier, and simpler. But things can, and do, occasionally go wrong - and that is when the hurt comes in. Qt provides a number of smart pointer classes, some might say too many, but that's a topic for a whole different discussion, one of which is QSharedPointer.

From the documentation:
"The QSharedPointer class holds a strong reference to a shared pointer
The QSharedPointer is an automatic, shared pointer in C++. It behaves
exactly like a normal pointer for normal purposes, including respect
for constness.

QSharedPointer will delete the pointer it is holding when it goes out
of scope, provided no other QSharedPointer objects are referencing it.

A QSharedPointer object can be created from a normal pointer, another
QSharedPointer object or by promoting a QWeakPointer object to a strong
reference.

Essentially, QSharedPointer works through reference counting, which means if you somehow make a mistake with cleaning up your references, the object your shared pointer refers to won't be deleted, and you've got a hard to track memory leak on your hands. This is precisely what happened to me recently at work, amongst a jungle of a few different libraries, so tracing the problem by hand was really not going to happen, so I needed a miracle, or short of that, a reliable way to track reference count changes on a QSharedPointer instance.

Reading up on QSharedPointer's internals, it became obvious that the reference counting was stored in the dpointer of each QSharedPointer instance. The dpointer is shared amongst QSharedPointer instances referring to the same pointer. So, we should be able to set a watch in gdb to break whenever the refcount changes.

First, we need to find out the address of a QSharedPointer instance, so set a breakpoint just after we first create it:


 (gdb) break main.cpp:22
 Breakpoint 1 at 0x8048806: file main.cpp, line 22.
 (gdb) r
 Starting program: /home/burchr/qsharedpointer/qsharedpointer.
 [Thread debugging using libthread_db enabled]

 Breakpoint 1, main (argc=1, argv=0xbffff444) at main.cpp:22
 22>-    QSharedPointer<MyClass> copy(initial);
 (gdb) p initial
 $1 = {<QtSharedPointer::ExternalRefCount<MyClass>> = {<QtSharedPointer::Basic<MyClass>> = { value = 0x804c438}, d = 0x804c448}, <No data fields>}

Now we have the address, we can set a watch on the QBasicAtomicInt in the dpointer, we can watch the refcount for changes:
 (gdb) watch $1.d->weakref
 Hardware watchpoint 2: $1.d->weakref


Continue debugging, and gdb will break whenever the refcount changes, telling us the old and new values, like so:
 (gdb) c
 Continuing.
 Hardware watchpoint 2: $1.d->weakref

 Old value = {_q_value = 1}
 New value = {_q_value = 2}
 0x08048953 in QBasicAtomicInt::ref (this=0x804c44c)
     at /usr/include/QtCore/qatomic_i386.h:120
 120>                 : "memory");



Much thanks to:

Labels: , , , , , ,

Wednesday, 21 July 2010

Qt: Bootstrapping openness

I witnessed (and was pleased to take part in) some interesting discussions on #qt-labs this afternoon, all stemming from a contribution to Qt3Support being rejected.

A long story short, the contribution - despite looking reasonably valid - was rejected because Qt3Support is effectively unmaintained, and as a result, any changes to it could have negative impacts on users of the support API.

I understand that argument, yet at the same time - I can't help but think it's a bit of a backwards approach to be taking. Typically, a contributor will wander along, find a bitrotting module/project that interests them, throw patches at the previous maintainer - and shortly after doing so, find themselves a de-facto (or indeed official) maintainer through their efforts.

This is a natural progression of things and should really be encouraged, it allows what would otherwise be dead code to live on. However, in Qt3Support's case, the central point was that they'd love someone else to take responsibility for it, but don't want to expend the effort themselves to triage, maintain, and otherwise support it right now.

To me, it's a false saving.

Time you save not maintaining it now (and encouraging potential contributors/future maintainers along the way) is spent being forced to put the code on life support maintenance if you decide you want that code, for whatever reason, later on.

Not to mention that those people who might be happily contributing you ideas or patches (to code old and new) are instead going to be spending their spare time doing something more rewarding than having their hard work rejected in the future.

Openness doesn't just happen overnight. It isn't just in the licensing. It requires real persistent effort and culture change. This is something I plan to keep revisiting over my next few posts.

{Note}: Before commenting, please note that this issue isn't *just* based around Qt3Support. That is one example of the problem, yes, but the patch was actually to QWorkspace which actually wasn't part of Qt3Support, and QtSql is in a similar boat - and there is nothing to port code using QtSql *to*.

Labels: , , , , , ,