In the past I worked with the tango library with the D language, that did try 
to avoid heap allocations and in my own blip library I had build most of the 
text output on a Sink: a functor that accepts a string.
For graphical user interfaces that is not an issue still I think that a Sink 
defined as
```
using Sink = std::function(void(QStringView));
```
can give a nice lightweight “text streaming” interface.
I played a bit with it, but my goal wasn’t developing that part, but I wanted 
to come back and benchmark it to see if my feeling was correct.

I basically benchmarked 
```
 void writeQString(QTextStream &s, int repeat = 100) {
    s << QLatin1String("start\n");
    for (int i = 0; i < repeat; ++i) {
        QString base = QStringLiteral(u"bla bla");
        for (int j = 0; j < 3; ++j) {
            s << base.repeated(j);
            s << QStringLiteral(u"%1 bla %2 %3\n").arg(QStringLiteral(u"  
").repeated(j), base, base + base);
        }
    }
    s << QLatin1String("end\n");
}
```


Against a version using a Sink
```
void repeatOut(Sink sink, int nrepeat, std::function<void(Sink)> dumper) {
    for (int i = 0; i < nrepeat; ++i)
        dumper(sink);
}

 void writeDumper(QTextStream &s, int repeat = 100) {
    Sink sink = [&s](QStringView str){ s << str; };
    sink(u"start\n");
    for (int i = 0; i < repeat; ++i) {
        QStringView base = u"bla bla";
        for (int j = 0; j < 3; ++j) {
            repeatOut(sink, j, [base](Sink sink) { sink(base); });
            [base, j](Sink sink) {
                repeatOut(sink, j, [](Sink sink) { sink(u"  "); });
                sink(u" bla ");
                sink(base);
                sink(u" ");
                sink(base);
                sink(base);
                sink(u"\n");
            }(sink);
        }
    }
    sink(u"end\n");
}
```
What I learnt is that

1) you have to be careful about comparing with String::repeated because adding 
a large string enlarges the cache of the QTextStram and makes comparison 
invalid (QString seems then much faster).

Not having understood point 1 I looked at passing all function objects as const 
& (which was faster), and finally using a non owing functor reference (like 
function_view discussed by Vittorio Romeo in 
https://vittorioromeo.info/index/blog/passing_functions_to_functions.html ), 
which was even faster (in D1 delegates were non owning).

The full source of my benchmark  is available at 
https://github.com/fawzi/qtIoTest and on my macBook 2.3 GHz i9 with flash 
storage I got the following numbers:

Debug
"QStringAlloc" write: "485.847010" writeFlush: "485.879594" ioTime: 
"486.158937" flushFails: 0
"LambdaAndSink" write: "987.612934" writeFlush: "987.640574" ioTime: 
"987.993028" flushFails: 0
"LambdaAndSinkCRef" write: "843.016367" writeFlush: "843.030152" ioTime: 
"844.147218" flushFails: 0
"LambdaAndSinkView" write: "483.614625" writeFlush: "483.630823" ioTime: 
"483.777161" flushFails: 0

Release
"QStringAlloc" write: "305.894019" writeFlush: "305.924705" ioTime: 
"306.177800" flushFails: 0
"LambdaAndSink" write: "102.757184" writeFlush: "102.768369" ioTime: 
"102.955962" flushFails: 0
"LambdaAndSinkCRef" write: "91.224731" writeFlush: "91.232051" ioTime: 
"91.642540" flushFails: 0
"LambdaAndSinkView" write: "79.895681" writeFlush: "79.915456" ioTime: 
"80.602022" flushFails: 0

In debug mode the function_view Is just as fast, but in release mode it is 
*much* faster, indeed all streaming options win over the plain String 
allocation.

So I have two points in my conclusion:

a) I think that it could make sense, if there is interest, to use Sink and 
void(Sink) functions (or function_view) more, and provide corresponding API 
(one can for example define a Dumpable that can be  instantiated with either a 
QStringView of a void(Sink) function, and thus have format(Sink, QStringView, 
Dumpable, Dumpable,…), or even a wrapper around a Sink that provides nicer 
stream like interface.
Strict GUI programming might not gain much, but any string output would be a 
bit faster, and I think, cleaner.

b) Independently from the text streaming discussion, a non owning function 
pointer like function_view could be nice to have in Qt.
It happens in several places that one wants to pass a function as argument that 
will be called immediately and not stored, for example as comparing function to 
sort.
In these case function_view being lighter weight and higher performance might 
make the API even more attractive (any long term storing / full closure use 
still needs std::function).

Bonus note
QTexCodec streaming use is ugly, QTextDecoder is a bit better, but by not 
exposing the existence of partially decoded code points in any way is still 
incomplete, fixing that if not already scheduled for Qt6 it should probably be 
added.
_______________________________________________
Development mailing list
[email protected]
https://lists.qt-project.org/listinfo/development

Reply via email to