KIO directory listing - CPU slows down SSD
Aaron J. Seigo
aseigo at kde.org
Wed May 28 17:33:42 UTC 2014
On Wednesday, May 28, 2014 16:23:31 Mark Gaiser wrote:
> On Wed, May 28, 2014 at 9:59 AM, David Faure <faure at kde.org> wrote:
> > On Wednesday 14 May 2014 12:50:18 Aaron J. Seigo wrote:
> >> * one entry at a time, serialized
> >> * no changing data once put there
> >> * no getting the data back out
> >>
> >> the class would then just batch up data into its QByteArray and then
> >> shunt
> >> it across the wire when needed. no middle men, no 100s of 1000s of
> >> objects
> >>
> >> and QLists and << operators. just an API sth like:
> >> startEntry
> >> addField
> >> endEntry
> >
> > I like this idea very much. Mark: see, it's quite close to what we were
> > discussing by email recently. And I hadn't read this yet :)
> >
> > Note how this is not specific to the ioslave using QT_STATBUFF :)
>
> To be honest, i was trying to look at the most used case (which is
> kio_file) and optimize that for raw speed.
> But i kinda like the idea of this approach as well since it would work
> on all slaves :) I will try to implement that instead.
>
> What i really like about this approach is that it could eventually
> phase out UDSEntry from the slave side. Meaning that UDSEntry would be
> fully client side (where this stream would be parsed).
that would be good for performance indeed; optimizing UDSEntry would still
have value, of course, since the client side would continue to use it. so
nobody's efforts there would be wasted
> >> internally it could either batch up the fields and then stream them out
> >> when endEntry is called, or (and this is what i would do personally) on
> >> startEntry it would set up the header but with bad data (e.g. field count
> >> of 0) and then start appending the fields as they come in and increment a
> >> field count variable. when endEntry is called, update the header with the
> >> right field count.
> >
> > Ah but batching requires a vector again. Or you meant writing into a
> > bytearray and coming back to update the count - that would rely on
> > QDataStream internals I'm afraid (how to know the offset....).
no internals needed. see attached example.
> > But the alternative is to replace the field count with an "end of fields"
> > special field.
>
> Huh? "batching requires a vector again" .. i don't understand that. It
consider the current code:
void SlaveBase::listEntries(const UDSEntryList &list)
{
KIO_DATA << (quint32)list.count();
foreach (const UDSEntry &entry, list) {
stream << entry;
}
send(MSG_LIST_ENTRIES, data);
}
so David means that because you can't seek in the QDataStream that you'd have
to batch up entries in a temporary data structure so that you could write the
header first and THEN write all the entries.
thankfully, the base assumption is incorrect :)
> SomeDataStreamClass stream;
> int i = 0
> while(QT_READDIR(...) && i++) {
> if(i = 200) { // or whatever the threshold is to send a batch of entries
... and before this happens it needs to set the # of entries it is sending.
something like a finalizeEntries() method. the class could keep state,
incrementing a variable every time startEntry() is called
also, rather than count based i'd probably make it based on the size of the
bytearray. smaller entries would mean more entries sent in a batch, larger
entries might mean less time waiting on the client side for additional items
(though that is likely to be nullified by the extra overhead of multiple
read/writes to the socket and subsequent deserialization trips). in any case,
one can then optimize the size of the transfer
> listEntries(stream);
> // reset stream and i.
> }
> startEntry()
> addField(...)
> addField(...)
> ...
> endEntry()
endEntry would need to do something similar: it would need to seek back into
the bytearray where the fields started and update the # of fields to retain
compatibility with the current code which is:
void UDSEntryPrivate::save(QDataStream &s, const UDSEntry &a)
{
const FieldHash &e = a.d->fields;
s << e.size();
> }
the point is that before listEntries is called, the # of items will need to be
placed at the head of the list so then when deserializing on the client side
the # of entries is known.
in any case, with QDataStream and a QByteArray (as currently used in the code)
you CAN seek backwards into the stream and overwrite some of the bytes and
then even seek back to the end. see the attached example to prove this for
yourself :)
of course, in this case you don't even need to seek back to the end. you just
need to seek back to the point where the count ought to be in the stream and
then serialize a sensible value.
this makes it entirely backwards compatible which means minimal changes
elsewhere in the code which should mean less chance of introducing new bugs.
keeping the wire protocol stable should be a goal unless there is real reason
to do otherwise.
of course, since this is about performance ... what is the cost of that
seeking?
populating a QByteArray via a QDataStream with 104857600 (1024*1024*100) ints
takes close to 19 seconds on my laptop and results in a QByteArray which is
400MB large. (i had to use something that large to be able to reliably measure
results with a naive QTime::elapsed method.) seeking back to the midpoint of
that monster, changing the value, then seeking to the end and adding a new
value takes ... less than a millisecond.
to model what this code would actually need to do (updating the number of
entries at the end of serialization as well as the field count for each entry),
i added a "seek back, serialize an int, seek forward" (to emulate updating a
field count) every for every N ints serialized to the bytearray. with N = 6 (or
17,476,267 field count updates) adds ~4s; N = 60 and N = 100 both add something
that is within the noise range offered by using QTime::elapsed() to measure
such things.
soooo.. i think performance wise this should be just fine, seeing as one would
probably be sending buffers on the order of some 100s of KB rather than 100s of
MB ;)
--
Aaron J. Seigo
-------------- next part --------------
A non-text attachment was scrubbed...
Name: main.cpp
Type: text/x-c++src
Size: 720 bytes
Desc: not available
URL: <http://mail.kde.org/pipermail/kde-frameworks-devel/attachments/20140528/972f6c0f/attachment.cpp>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: This is a digitally signed message part.
URL: <http://mail.kde.org/pipermail/kde-frameworks-devel/attachments/20140528/972f6c0f/attachment.sig>
More information about the Kde-frameworks-devel
mailing list