[Development] Question about QCoreApplicationData::*_libpaths

Иван Комиссаров abbapoh at gmail.com
Sat Jan 16 15:44:47 CET 2016


The question is not good COW or bad. The question is - can/should Qt use
(for some reasons) it's own containers, or not. Yes, stl containers are
complex, still *theoretically*, Qt can have standard-compatible QVector.

About COW. On current work, we try to make all classes thread safe.
Consider class Manager with a std::map in it. Map can be huge, so the
easiest way to return std::shared_ptr to the map:
class Manager
{
public:
    using Map = std::map<std::string, std::string>;
    using MapConstPtr = std::shared_ptr<const Map>;

    MapConstPtr getMap() const;
};

However, in multithreaded app this won't work! Imagine, Manager will try to
modify map while someone holds pointer to it. First, pointer should be
protected with mutex both in getter and the place when manager modifies the
map. Second, the map should be copied before modification.
The alternative is to always copy map on each get() operation.

2016-01-16 15:06 GMT+03:00 Bubke Marco <Marco.Bubke at theqtcompany.com>:

> On January 16, 2016 06:08:14 Kevin Kofler <kevin.kofler at chello.at> wrote:
>
> > Bubke Marco wrote:
> >> Actually this convince is hurting you if you need performance. I would
> >> prefer the convenience on top of lower level api.
> > [and at the end:]
> >> It really depends what you want to do. I would prefer it we had a CoW
> >> wrapper around std vector. Best of both worlds.
> >
> > The question is whether such a wrapper would be able to support all
> > functionality of the current QVector or whether it would run into some
> > limitations. I suspect we would lose at least some optimizations from
> > Q_DECLARE_TYPEINFO. There might also be some API that cannot be
> implemented,
> > or at least not efficiently. (This could be even worse for other
> containers
> > than QVector.) So, it sounds like a great idea in theory, but I wonder
> how
> > practical it is in practice. I guess I would have to see a practical
> > implementation to know for sure.
>
> Is std::is_trivially_copyable amd other type traits are your friends. They
> make it still easier than the qt workaround.
> >
> > What I know for sure is that my priority queue abstraction on top of
> > std::priority_queue can support only a very limited API, and it would be
> the
> > same if QQueue were to be implemented on top of std::queue: The current
> > QQueue publicly inherits the underlying QList. The STL API, instead,
> wraps
> > the underlying vector and only offers the queue primitives, a completely
> > different philosophy that leads to a much more limited API. Of course,
> this
> > could be worked around by wrapping std::vector directly (or maybe
> > std::dequeue), or by simply keeping QQueue implemented on top of QList
> and
> > only changing QList. But there might be other such API limitations in the
> > STL.
>
> I would not change all types.  The hash implementations of the stl are not
> that day to my knowledge. I care about interfaces and there you want to use
> a vector in the most cases. Maybe a flat unordered map on top of vector
> would be nice.
>
> I would be discourage the use of QList and promote vector instead. I don't
> use the extras features of QList  so I think most people do but QList is
> can be very suboptimal if you use it with entries which are larger than a
> pointer.
>
> There are other pitfalls in the qt containers,  e.g.
>
> if (container.contains(key))
>    auto value = container.value(key);
>
> auto iterator = container.find(key);
> if (iterator! = container.den())
>   auto value = iterator.value();
>
> The first looks nice but leads to two lookups. And I have seen it very
> often.
>
>
> > And QList might not be implementable at all on top of the STL, at least
> not
> > the flexible way it is now (array of pointers, except when the type can
> be
> > put in the place of the pointer).
> >
> >> I prefer algorithms which are based on iterators to handwritten loops.
> >> They are easier to parallise in the future too.
> >
> > Well, then I have good news for you:
> > some_algorithm(myQVector.begin(), myQVector.end());
> > is perfectly safe. If myQVector was shared, the begin() or end() call,
> > whichever is executed first, will detach, and then some_algorithm will
> > operate on a new deep copy of myQVector that nothing else can possibly
> have
> > seen yet. If myQVector was not shared to begin with, then it is of course
> > safe too and there is no detach in that case.
> >
> > The iterators are only problematic when you keep an iterator across an
> > assignment, such as:
> > QVector<int>::iterator evil = myQVector.begin();
> > QVector<int> broken = myQVector;
> > *evil = 666;
> > where "evil" will corrupt "broken".
>
> I understand the problem. But my problem is more that I have the atomic
> pointer which can slow your code done unexpectedly.
>
> > IMHO, this is simply a user error and a case of "don't do that then".
> This
> > issue is explicitly warned about in the Qt documentation.
>
> I buy your argument but documentation is not the solution. You put the
> burden on the programmer to understand that his value semantic is not quite
> a value semantic. Do you look in the documentation all the time before you
> use a function? ;-) Sometimes 'lets document it' is an excuse for a
> interface with unexpected behavior. :-)
>
> > If you really need
> > a copy of myQVector at this place, then you can use this:
> > QVector<int>::iterator evil = myQVector.begin();
> > QVector<int> unbroken = myQVector;
> > unbroken.data(); // force detach
> > *evil = 666;
> > and everything will magically work again.
> >
> >> Atomics on the other side can produce strange performance bugs with
> false
> >> sharing. I don't believe they are the future in a many core environment
> >> where you share cache lines very often.
> >
> > Well, we could in principle do CoW without atomics (Qt 3 did them that
> way),
> > but the drawback then is that we would lose thread-safety (which is of
> > course why Qt 4 introduced atomic reference counts).
>
> Hmm,  isn't sharing of writeable cache lines between cores a bad idea
> anyway. I write cache lines because your cach lines are the atomic
> entities, not your data. And we go in the direction of more and more cores.
> So CoW is maybe not a good idea anymore.
>
> http://www.gotw.ca/publications/optimizations.htm
>
> >> Hmm most other languages I know provide  more convenience than Qt but
> are
> >> slower. I think you pick C++ in the context of speed. So we should
> provide
> >> a wrapper around std vector with cow.
> >
> > Well, most other languages I know provide *less* convenience than Qt but
> are
> > slower. ;-) See also my rant about Java elsewhere in this thread. Its
> > "everything is a reference" explicit sharing (which I have also seen in
> many
> > other languages, even and especially ones marketed to beginners: Visual
> > Basic, Python and many more) is a poor substitute for CoW. That sharing
> > issue you can run into when using iterators on Qt containers (see above)
> is
> > how those languages *always* work: If you do not explicitly clone the
> > object, writing to any "copy" will actually affect them *all*. And even
> > Java's "final" will not protect you, because it is only the equivalent
> of a
> > Foo * const, not of a const Foo * or const Foo &. (That's why immutable
> > objects are so popular in those programming languages, but those are of
> > course a major performance issue if you need to make any modifications,
> > because you are deep-copying all the time.)
>
> I used that in small talk and python extensively and I like the idea to
> make copy explicit with a clone function. It is expensive and sold be done
> only explicitly.
>
> You have measured that immutable objects are a major performance obstacle?
> You can optimize them quite well. And with multicore environments they
> scale much better than CoW.
>
>
> >> QList is really a trap.
> >>
> >> struct Entry {
> >>   QString text;
> >>   bool isHtml;
> >> } ;
> >>
> >> QList<Entry> list;
> >>
> >> Do you see the problem?
> >
> > I see that this is going to create a vector of Entry * and so require a
> lot
> > of allocations of size sizeof(void *) + 1 + padding. Congratulations, you
> > found the worst case of QList. Now imagine a future version of your code
> > changes your Entry to look like this:
> > struct Entry {
> >   QString text;
> >   bool isHtml;
> >   int data[10000];
> > } ;
> > and suddenly, the QList strategy will not be that bad an idea anymore,
> > especially if you find yourself doing random inserts or removals.
> > (Incidentally, they plan to change QString in Qt 6 with an alleged
> > "optimization" that will actually make your struct look not all that
> > different from that: They want to make QString big so that they can store
> > small strings without an allocation. I call that the "small string
> > pessimization". I strongly doubt the saved allocation is worth passing
> > around huge blobs for a type as common as QString.) Good luck waiting for
> > the memory move of your QVector or std::vector if you delete element 5000
> > out of a 10000-element vector of this new Entry type.
>
> Short string optimization is five to ten  times faster than malloc which
> is no surprise. And many syrings are small.
>
> And it would be still malloc with short string optimization because the
> entry is bigger than a pointer. ;-)
>
> If you provide data structures which are trivially copy able the simply
> could be moved in memory for a vector which is quite fast.
>
> I don't say that there is no use case for a pointed vector but I can
> always do
> Vector<Entry*>
>
> You mostly traverse this data structures very often and a pointed vectors
> are not that optimal because you get much more cach misses which are really
> expensive.
>
> Most structures are not that big so you optimize not for the common case
> and there are many measurements that show that your pointer can breaking
> the prefetcher. So maybe in theory of its faster but not in practice.
>
> Kevin,  you come up with performance arguments but please show me your
> measurements because most I have seen show the opposite. And with my
> limited understanding of processors and memory it is reasonable
> understandable why it is so.  ;-)
>
> Download something like google benchmark and do micro benchmarks. Then we
> have arguments which stands on a stable fundament. We can always look at
> the code to see what you are measure and find out what is happen.
>
> I think this argumentation should be productive. We should find a better
> solution but not fight for our 'optimal' solution. So a open discussion
> should be much more productive than the repetitions of the same arguments
> again and again. I think for that type of argumentation you have always to
> try to get in the context of the other. But like we know we are lazy so
> repetition is much easier.  ;-)
>
> Why not search for a better solution and not spending too much energy in a
> trench fight for 'my solution'.
>
> >         Kevin Kofler
> >
> > _______________________________________________
> > Development mailing list
> > Development at qt-project.org
> > http://lists.qt-project.org/mailman/listinfo/development
>
> --
> Sent from cellphone, sorry for the typos
> _______________________________________________
> Development mailing list
> Development at qt-project.org
> http://lists.qt-project.org/mailman/listinfo/development
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/development/attachments/20160116/54571c2b/attachment.html>


More information about the Development mailing list