Results 1 to 5 of 5

Thread: Chasing QList detach_helper()

  1. #1
    Join Date
    Jan 2009
    Location
    Germany
    Posts
    387
    Thanks
    101
    Thanked 15 Times in 15 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Chasing QList detach_helper()

    Hi there,

    I am writing high performance code for a real time application. A typical use case in this project is the use of a QList as a queue, where first the queue would be clear()-ed, then an unknown number of elements will be push_back()-ed and pop_front()-ed in succession. One of the bottlenecks I am trying to combat is the costly memory allocation with the new operator, which obviously occurs each time a new element is appended to the list. Since the number of elements is unknown and I cannot possibly allocate the right amount of memory upfront, I follow the strategy of not letting the queue clear(), but reuse the memory blocks it already created by using the [] operator to assign a new value to an already allocated slot. I wrote a little wrapper that does this. Let me show you what it looks like.

    Qt Code:
    1. template <typename T>
    2. class List
    3. {
    4. int idxFront;
    5. int idxBack;
    6. QList<T> list;
    7.  
    8. public:
    9. List();
    10. void clear();
    11. void push_back(T const&);
    12. T pop_front();
    13. T& operator[](int i);
    14. };
    15.  
    16. template <typename T>
    17. List<T>::List()
    18. {
    19. idxFront = -1;
    20. idxBack = -1;
    21. }
    22.  
    23. // Clears the list, but not really.
    24. template <typename T>
    25. void List<T>::clear()
    26. {
    27. idxFront = -1;
    28. idxBack = -1;
    29. }
    30.  
    31. // Appends a new item to the back of the queue.
    32. template <typename T>
    33. void List<T>::push_back(T const& elem)
    34. {
    35. //qDebug() << "push_back()" << idxFront << idxBack << list.size();
    36. if (idxBack == -1)
    37. {
    38. if (list.empty())
    39. list.push_back(elem);
    40. else
    41. list[0] = elem;
    42. idxBack = 0;
    43. idxFront = 0;
    44. }
    45. else if (idxBack < list.size()-1)
    46. {
    47. idxBack++;
    48. list[idxBack] = elem;
    49. }
    50. else
    51. {
    52. idxBack++;
    53. list.push_back(elem);
    54. }
    55. }
    56.  
    57. // Removes the first item from the List.
    58. template <typename T>
    59. T List<T>::pop_front()
    60. {
    61. //qDebug() << "pop_front()" << idxFront << idxBack << list.size();
    62. if (idxFront == idxBack)
    63. {
    64. int tmpidx = idxFront;
    65. idxFront = -1;
    66. idxBack = -1;
    67. return list.at(tmpidx);
    68. }
    69. return list.at(idxFront++);
    70. }
    71.  
    72. // Index based access to the elements in the List.
    73. template <typename T>
    74. T& List<T>::operator[](int i)
    75. {
    76. return list[idxFront+i];
    77. }
    To copy to clipboard, switch view to plain text mode 


    When profiling this code with valgrind embedded in Qt Creator, it turns out that the push_back() operation spends the vast majority of its time calling QList::detach_helper(int). This is bothersome as the QList inside this wrapper should never ever detach(), and because I don't understand why it does. :) In the push_back() method QList::empty(), QList::size(), QList::append(T), and QList::operator[int] are used. size() and empty() are out. When I open the source of QList::append(T), I cannot see any direct or indirect calls to detach_helper(int) occuring. It calls detach_helper_grow(), which is a different function. What remains is the QList::operator[int]. Now this operator does call detach(), which then calls detach_helper() if the list is shared, but why would it be shared? The QList is private inside my List class and nothing else is ever touching it. So why does this call ever occur?

  2. #2
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Chasing QList detach_helper()

    Maybe QList is not the best container to base this on, see https://marcmutz.wordpress.com/effective-qt/containers/

    Cheers,
    _

  3. The following 2 users say thank you to anda_skoa for this useful post:

    Cruz (22nd January 2017), d_stranz (21st January 2017)

  4. #3
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Chasing QList detach_helper()

    The QList is private inside my List class and nothing else is ever touching it. So why does this call ever occur?
    As the great article linked by anda_skoa says, there are major differences between Qt and STL containers. QList in particular is implicitly shared and will be copied on write. So if one of your List class methods modifies (or by being non-const is interpreted as potentially modifying) the wrapped QList, it could cause a copy.

    Your "T & operator[]( int )" for example, causes a copy on write, even if it is on the right-hand side of an expression because it is a mutable operation. If you declared a const version of that, you would avoid the copy when used correctly: "const T & operator[]( int ) const".
    Last edited by d_stranz; 21st January 2017 at 16:39.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  5. #4
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Chasing QList detach_helper()

    QList is also a "node based" container, where each element is basically another object that holds the actual entry.
    There are some optimizations for simple types that fit into the space of a pointer, but it is never less.

    Cheers,
    _

  6. #5
    Join Date
    Jan 2009
    Location
    Germany
    Posts
    387
    Thanks
    101
    Thanked 15 Times in 15 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Chasing QList detach_helper()

    I have done some performance test on my own with my code and realistic data, and it turns out that using QVector as a base for my List leads to better performance. This is no surprise given that document about the container classes and also the Qt documentation recommends to prefer QVector. I chose QList at first because I thought that the sequential growth will lead to copying the entire vector multiple times during the growth phase, but this doesn't seem to be a problem after all and QVector outperforms QList. I also tried std::deque and it loses badly. What remains a mystery to me is the repeated detachment of the QList. I removed the detach() call in the operator[] in qlist.h and yes, it started performing much better. Since the private QList in my List class is not shared, this detach() call should have no effect. But oh well, since I'm using QVector now, I can't be bothered to follow up on this any further.

    Thanks for the help!

Similar Threads

  1. QList::clear() and QList::reserve()
    By Momergil in forum Qt Programming
    Replies: 0
    Last Post: 13th June 2014, 13:33
  2. how to confirm the value of one QList in other QList quickly?
    By weixj2003ld in forum Qt Programming
    Replies: 5
    Last Post: 8th June 2012, 20:48
  3. Cast QList<Foo*> to QList<const Foo*>
    By vfernandez in forum Qt Programming
    Replies: 0
    Last Post: 4th October 2010, 16:04
  4. Replies: 4
    Last Post: 20th August 2010, 13:54
  5. QList: Out of memory - without having defined QList
    By miroslav_karpis in forum Qt Programming
    Replies: 1
    Last Post: 27th March 2009, 08:42

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.