IndexedString vs. implicit sharing
Milian Wolff
mail at milianw.de
Thu Oct 18 08:57:50 UTC 2012
On Thursday 18 October 2012 09:12:45 Andreas Pakulat wrote:
> Hi,
>
> On Wed, Oct 17, 2012 at 9:46 PM, Milian Wolff <mail at milianw.de> wrote:
> > On Wednesday 17 October 2012 20:52:22 Andreas Pakulat wrote:
> >> > But couldn't we use
> >> > QString's in there directly?
> >>
> >> That would mean keeping all the bits/bytes for a QString in memory,
> >> which was the whole reason for dropping their use.
> >
> > Right. I forgot that kind of. Probably makes the implementation of what I
> > want even more complicated or even impossible. Let me give an example
> > here, replace>
> > QString with KUrl to get the same problem:
> > QString qString;
> > qString.fill('.', 1000);
> > KDevelop::IndexedString indexedString(qString);
> > const int repeat = 10000;
> > QVector<QString> strings;
> > strings.resize(repeat);
> > for(int i = 0; i < repeat; ++i) {
> >
> > strings[i] = indexedString.str();
> > QCOMPARE(qString, strings[i]);
> >
> > }
> >
> > What do you expect that does memory wise? Well, it will hog up more and
> > more memory here. That's the Use-Case I'd like to optimize if possible.
> So the implicit sharing of QString fails here? Do you happen to know
> why, since I don't understand how the implicit sharing works all that
> well I'm just wondering wether there's a way to make it work in this
> situation...
Implicit sharing only works if you copy one QString (or any other implicitly
shared data) to another. I.e. this shares memory (replace QShared with any
implicitly shared data type):
QShared orig = ...;
QShared shared1 = orig;
QShared shared2(orig);
QShared shared3(shared1);
...
but that does not, assuming rawData is not also a QShared:
QShared orig(rawData);
QShared copy1(rawData);
And that is exatly what is being done here. The conversion code in
IndexedString uses QString::fromUtf8(const char*, int) to construct a QString.
To leverage implicit sharing here, one needs to keep e.g. a
QHash<IndexedString, QString> around and do something like this:
QString sharedValue(const IndexedString& index, const QString& value) {
static QHash<IndexedString, QString> cache;
auto it = cache.constFind(index);
if (it != cache.constEnd()) {
return it.value();
}
cache.insert(index, value);
return value;
}
But, as you noted in your last email, this brings us a whole new problem:
When should we clean up the cache? There is afaiks no way to get notified that
the ref counter of the implicitly shared QString data dropped to 1 (i.e. only
referenced in the QHash) which would be the correct time to remove it there as
well... If we don't do that, the cache will increase over time and we probably
end up with a much higher memory consumption.
*But* if I look at IndexedStringData I wonder whether that could be leveraged
somehow... Right now it looks as if it stores a refCount and the length of the
string. The actual string data is stored just after that data, see e.g.
IndexedStringRepositoryItemRequest::itemSize(). Now if there would be a way to
construct a QString out of this data and reuse that, but destroy it as soon as
the refcount drops... that would be optimal. But as far as I can see, we can't
just add a QString data member to IndexedStringData, as we cannot/must not
serialize that (it's internals are hidden from us after all)... But well, this
is more or less just guessing on my side. Maybe I misunderstand the
ItemRepository magic. But considering there is code like this in the
ItemRequest:
static void destroy(IndexedStringData* item,
KDevelop::AbstractItemRepository&) {
Q_UNUSED(item);
//Nothing to do here (The object is not intelligent)
}
I assume we could do something intelligent here, no?
> Also does this happen in the codebase without being able to change the
> relevant code? I mean adding the same indexedstring's QString content
> to a list multiple times looks like somethings wrong there...
Of course its not as extreme as in the example shown above. But what did
happen (I fixed it differently, by replacing KUrl with IndexedString) was that
we essentially duplicated the KUrl's of every project file. That was already
quite some data (>200mb for my tests). I wouldn't find it too suprising to see
that we do that at other places as well. I'll continue to investigate. For
now, the "simple" solution is to use IndexedString in place of QString/KUrl
wherever possible.
But that would never solve the case of having e.g. two QAbstractItemModels
that operate on the same list of IndexedStrings. As soon as you plug them into
a view and return something like IndexedString::str() in your model's ::data()
function, you'll have the string twice in memory, not once...
> >> > Maybe that would require us to make IndexedString
> >> > only operate on QString's but I'd be all for that anyways to increase
> >> > type
> >> > safety. But well, food for though...
> >>
> >> What kind of type-safety do you gain by replacing IndexedString with
> >> QString?
> >
> > IndexedString has .str() .c_str() .byteArray() and finally .toUrl() -
> > which
> > one is the correct one?
> >
> > DUContext::url() -> should always be a KUrl
> > Declaration::identifier() -> should alays be a QString
> >
> > That's what I meant with "type-safety" - probably the wrong word.
>
> Ok, then rename IndexString to IndexObject, make the constructor
> private and drop those 4 functions from it. Create 4 subclasses:
> IndexedUrl, IndexedQString, IndexedByteArray and IndexedCString which
> each have one of those 4 functions in their public API. Then change
> all the code to use the suitable Index* class instead of
> IndexedString.
Yes that is exactly what I had in mind.
> Seeing the c-string and bytearray conversion immediately triggers my
> encoding-alarm, how does IndexedString decide which encoding to use
> and are all callers of these two function aware of the encoding being
> used?
It's not encoding aware at all - it asumes UTF-8 everywhere. The callers must
take care to use the proper function, and the byte array is just there as an
optimization at one place (forgot again where). Since we usually only operate
on UTF-8 data this is all fine. Non-UTF-8 data will break our code in many
places, as there are multiple code paths in the DUChain that implicitly assume
UTF-8.
--
Milian Wolff
mail at milianw.de
http://milianw.de
-------------- 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/kdevelop-devel/attachments/20121018/e9dccdd1/attachment.sig>
More information about the KDevelop-devel
mailing list