Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Returning QList as reference and value has difference in performance
Forum Updated to NodeBB v4.3 + New Features

Returning QList as reference and value has difference in performance

Scheduled Pinned Locked Moved Solved General and Desktop
11 Posts 5 Posters 3.3k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    Sivan
    wrote on last edited by Sivan
    #1

    Hello everyone,

    I am currently working on improving my application's performance and stumbled upon this issue which I don't understand why it is affecting the performance.

    According to Qt's documentation, it is stated that QList is implicitly shared for as long as NO non-const function is called on that list. However, I do see a significant difference in the following case. The below example is an abstraction of my code, in which I can see the performance difference as well

    #include <QApplication>
    #include <QElapsedTimer>
    #include <QDebug>
    
    //#define USE_REF
    //#define USE_FOREACH
    class MyList
    {
    public:
        MyList()
        {
            for (int i = 0; i < 10; ++i)
            {
                topList.append(i);
            }
    
            for (int i = 0; i < 2000; ++i)
            {
                midList.append(i);
            }
    
            for (int i = 0; i < 10; ++i)
            {
                bottomList.append(i);
            }
        }
    
    #ifdef USE_REF
        const QList<int>& GetTopLevelList() const { return topList; }
        const QList<int>& GetMidLevelList() const { return midList; }
        const QList<int>& GetBottomLevelList() const { return bottomList; }
    #else
        QList<int> GetTopLevelList() const { return topList; }
        QList<int> GetMidLevelList() const { return midList; }
        QList<int> GetBottomLevelList() const { return bottomList; }
    #endif
    
    private:
        QList<int> topList;
        QList<int> midList;
        QList<int> bottomList;
    };
    
    int main(int argc, char *argv[])
    {
            MyList myList;
            QElapsedTimer timer;
            timer.start();
    #ifdef USE_FOREACH
            Q_FOREACH(int i, myList.GetTopLevelList())
            {
                Q_FOREACH(int j, myList.GetMidLevelList())
                {
                    Q_FOREACH(int k, myList.GetBottomLevelList())
                    {
                        int w = i + k + j;
                    }
                }
            }
    #else
            const QList<int>& topLevel = myList.GetTopLevelList();
            for (int i = 0; i < topLevel.size(); ++i)
            {
                const QList<int>& midLevel = myList.GetMidLevelList();
                for (int j = 0; j < midLevel.size(); ++j)
                {
                    const QList<int>& lowLevel = myList.GetBottomLevelList();
                    for (int k = 0; k < lowLevel.size(); ++k)
                    {
                        int w =  i + k + j;
                    }
                }
            }
    #endif
            qDebug() << "<<<<< Elapsed = " << timer.elapsed();
    }
    

    Here is the table for the elapsed time for the combination of USE_REF and USE_FOREACH

    • USE_REF = 0, USE_FOREACH = 0 ==> 1ms
    • USE_REF = 0, USE_FOREACH = 1 ==> 3ms
    • USE_REF = 1, USE_FOREACH = 0 ==> 0ms
    • USE_REF = 1, USE_FOREACH = 1 ==> 3ms

    I am specifically interested in the case of USE_REF = 0 and USE_FOREACH = 0, where I am getting the elapsed time as 1ms. Why is this different from USE_REF = 1, and USE_FOREACH = 0, when I am not calling any non-const function (which means there should not be any deep copy)?

    I have also included the case USE_FOREACH and would like to know on why it is impacting the performance so much.

    Tested on: Qt-Version: Qt 5.4.1 MacOS 10.12.2

    Thank you in advance :-)

    jsulmJ JKSHJ J.HilkJ 3 Replies Last reply
    0
    • S Sivan

      Hello everyone,

      I am currently working on improving my application's performance and stumbled upon this issue which I don't understand why it is affecting the performance.

      According to Qt's documentation, it is stated that QList is implicitly shared for as long as NO non-const function is called on that list. However, I do see a significant difference in the following case. The below example is an abstraction of my code, in which I can see the performance difference as well

      #include <QApplication>
      #include <QElapsedTimer>
      #include <QDebug>
      
      //#define USE_REF
      //#define USE_FOREACH
      class MyList
      {
      public:
          MyList()
          {
              for (int i = 0; i < 10; ++i)
              {
                  topList.append(i);
              }
      
              for (int i = 0; i < 2000; ++i)
              {
                  midList.append(i);
              }
      
              for (int i = 0; i < 10; ++i)
              {
                  bottomList.append(i);
              }
          }
      
      #ifdef USE_REF
          const QList<int>& GetTopLevelList() const { return topList; }
          const QList<int>& GetMidLevelList() const { return midList; }
          const QList<int>& GetBottomLevelList() const { return bottomList; }
      #else
          QList<int> GetTopLevelList() const { return topList; }
          QList<int> GetMidLevelList() const { return midList; }
          QList<int> GetBottomLevelList() const { return bottomList; }
      #endif
      
      private:
          QList<int> topList;
          QList<int> midList;
          QList<int> bottomList;
      };
      
      int main(int argc, char *argv[])
      {
              MyList myList;
              QElapsedTimer timer;
              timer.start();
      #ifdef USE_FOREACH
              Q_FOREACH(int i, myList.GetTopLevelList())
              {
                  Q_FOREACH(int j, myList.GetMidLevelList())
                  {
                      Q_FOREACH(int k, myList.GetBottomLevelList())
                      {
                          int w = i + k + j;
                      }
                  }
              }
      #else
              const QList<int>& topLevel = myList.GetTopLevelList();
              for (int i = 0; i < topLevel.size(); ++i)
              {
                  const QList<int>& midLevel = myList.GetMidLevelList();
                  for (int j = 0; j < midLevel.size(); ++j)
                  {
                      const QList<int>& lowLevel = myList.GetBottomLevelList();
                      for (int k = 0; k < lowLevel.size(); ++k)
                      {
                          int w =  i + k + j;
                      }
                  }
              }
      #endif
              qDebug() << "<<<<< Elapsed = " << timer.elapsed();
      }
      

      Here is the table for the elapsed time for the combination of USE_REF and USE_FOREACH

      • USE_REF = 0, USE_FOREACH = 0 ==> 1ms
      • USE_REF = 0, USE_FOREACH = 1 ==> 3ms
      • USE_REF = 1, USE_FOREACH = 0 ==> 0ms
      • USE_REF = 1, USE_FOREACH = 1 ==> 3ms

      I am specifically interested in the case of USE_REF = 0 and USE_FOREACH = 0, where I am getting the elapsed time as 1ms. Why is this different from USE_REF = 1, and USE_FOREACH = 0, when I am not calling any non-const function (which means there should not be any deep copy)?

      I have also included the case USE_FOREACH and would like to know on why it is impacting the performance so much.

      Tested on: Qt-Version: Qt 5.4.1 MacOS 10.12.2

      Thank you in advance :-)

      jsulmJ Offline
      jsulmJ Offline
      jsulm
      Lifetime Qt Champion
      wrote on last edited by
      #2

      @Sivan Well, even if QList is implicitly shared it is still an object. If you return it by value then new QList is created and copy constructor of the old one is called, this is way slower than returning a reference (which is technically same as returning a pointer).

      https://forum.qt.io/topic/113070/qt-code-of-conduct

      S 1 Reply Last reply
      2
      • jsulmJ jsulm

        @Sivan Well, even if QList is implicitly shared it is still an object. If you return it by value then new QList is created and copy constructor of the old one is called, this is way slower than returning a reference (which is technically same as returning a pointer).

        S Offline
        S Offline
        Sivan
        wrote on last edited by
        #3

        @jsulm, Hmm, I just find that 1ms for the above case is too long since Qt's documentation mentioned that this contructor should be very fast. Below is the note from QList Copy Constructor,

        QList::QList(const QList<T> &other)
        Constructs a copy of other.

        This operation takes constant time, because QList is implicitly shared. This makes returning a QList from a function very fast. If a shared instance is modified, it will be copied (copy-on-write), and that takes linear time.

        But anyway, let me convince myself that 1ms is due to the accumulation of the number of time the object is created and the copy constructor is called.

        What about the performance of Q_FOREACH? Why is it different from the normal for loop?

        1 Reply Last reply
        0
        • S Sivan

          Hello everyone,

          I am currently working on improving my application's performance and stumbled upon this issue which I don't understand why it is affecting the performance.

          According to Qt's documentation, it is stated that QList is implicitly shared for as long as NO non-const function is called on that list. However, I do see a significant difference in the following case. The below example is an abstraction of my code, in which I can see the performance difference as well

          #include <QApplication>
          #include <QElapsedTimer>
          #include <QDebug>
          
          //#define USE_REF
          //#define USE_FOREACH
          class MyList
          {
          public:
              MyList()
              {
                  for (int i = 0; i < 10; ++i)
                  {
                      topList.append(i);
                  }
          
                  for (int i = 0; i < 2000; ++i)
                  {
                      midList.append(i);
                  }
          
                  for (int i = 0; i < 10; ++i)
                  {
                      bottomList.append(i);
                  }
              }
          
          #ifdef USE_REF
              const QList<int>& GetTopLevelList() const { return topList; }
              const QList<int>& GetMidLevelList() const { return midList; }
              const QList<int>& GetBottomLevelList() const { return bottomList; }
          #else
              QList<int> GetTopLevelList() const { return topList; }
              QList<int> GetMidLevelList() const { return midList; }
              QList<int> GetBottomLevelList() const { return bottomList; }
          #endif
          
          private:
              QList<int> topList;
              QList<int> midList;
              QList<int> bottomList;
          };
          
          int main(int argc, char *argv[])
          {
                  MyList myList;
                  QElapsedTimer timer;
                  timer.start();
          #ifdef USE_FOREACH
                  Q_FOREACH(int i, myList.GetTopLevelList())
                  {
                      Q_FOREACH(int j, myList.GetMidLevelList())
                      {
                          Q_FOREACH(int k, myList.GetBottomLevelList())
                          {
                              int w = i + k + j;
                          }
                      }
                  }
          #else
                  const QList<int>& topLevel = myList.GetTopLevelList();
                  for (int i = 0; i < topLevel.size(); ++i)
                  {
                      const QList<int>& midLevel = myList.GetMidLevelList();
                      for (int j = 0; j < midLevel.size(); ++j)
                      {
                          const QList<int>& lowLevel = myList.GetBottomLevelList();
                          for (int k = 0; k < lowLevel.size(); ++k)
                          {
                              int w =  i + k + j;
                          }
                      }
                  }
          #endif
                  qDebug() << "<<<<< Elapsed = " << timer.elapsed();
          }
          

          Here is the table for the elapsed time for the combination of USE_REF and USE_FOREACH

          • USE_REF = 0, USE_FOREACH = 0 ==> 1ms
          • USE_REF = 0, USE_FOREACH = 1 ==> 3ms
          • USE_REF = 1, USE_FOREACH = 0 ==> 0ms
          • USE_REF = 1, USE_FOREACH = 1 ==> 3ms

          I am specifically interested in the case of USE_REF = 0 and USE_FOREACH = 0, where I am getting the elapsed time as 1ms. Why is this different from USE_REF = 1, and USE_FOREACH = 0, when I am not calling any non-const function (which means there should not be any deep copy)?

          I have also included the case USE_FOREACH and would like to know on why it is impacting the performance so much.

          Tested on: Qt-Version: Qt 5.4.1 MacOS 10.12.2

          Thank you in advance :-)

          JKSHJ Offline
          JKSHJ Offline
          JKSH
          Moderators
          wrote on last edited by
          #4

          @Sivan said in Returning QList as reference and value has difference in performance:

          • USE_REF = 0, USE_FOREACH = 0 ==> 1ms
          • USE_REF = 0, USE_FOREACH = 1 ==> 3ms
          • USE_REF = 1, USE_FOREACH = 0 ==> 0ms
          • USE_REF = 1, USE_FOREACH = 1 ==> 3ms

          These time differences are very very small. Are you sure they're not caused by jitter in your computer? (For example, the OS might have decided to do some background maintenance just as you started the test, which could add a few ms to your code execution)

          To do a proper test, do both of the following:

          1. Use a large dataset.
            • 2 000 integers is nothing to a modern CPU. Try 200 000 instead.
          2. Do repeated measurements and find the average:
          QElapsedTimer timer;
          QVector<qint64> measurements;
          for (int x = 0; x < 10000; ++x) {
              timer.start();
          
          #ifdef USE_FOREACH
              ...
          #endif
          
              measurements << timer.elapsed();
          }
          
          double totalTime = 0;
          for (time : measurements)
              totalTime += time;
          
          qDebug() << "Average time =" << totalTime / 10000;
          
          // TODO: Export the measurement values to a file.
          // Use a spreadsheet program to plot a histogram
          

          Note: It is possible that the first value in measurement is always larger than the other 9999. If this is the case, remove the 1st value before calculating the average.

          Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

          S 1 Reply Last reply
          3
          • S Sivan

            Hello everyone,

            I am currently working on improving my application's performance and stumbled upon this issue which I don't understand why it is affecting the performance.

            According to Qt's documentation, it is stated that QList is implicitly shared for as long as NO non-const function is called on that list. However, I do see a significant difference in the following case. The below example is an abstraction of my code, in which I can see the performance difference as well

            #include <QApplication>
            #include <QElapsedTimer>
            #include <QDebug>
            
            //#define USE_REF
            //#define USE_FOREACH
            class MyList
            {
            public:
                MyList()
                {
                    for (int i = 0; i < 10; ++i)
                    {
                        topList.append(i);
                    }
            
                    for (int i = 0; i < 2000; ++i)
                    {
                        midList.append(i);
                    }
            
                    for (int i = 0; i < 10; ++i)
                    {
                        bottomList.append(i);
                    }
                }
            
            #ifdef USE_REF
                const QList<int>& GetTopLevelList() const { return topList; }
                const QList<int>& GetMidLevelList() const { return midList; }
                const QList<int>& GetBottomLevelList() const { return bottomList; }
            #else
                QList<int> GetTopLevelList() const { return topList; }
                QList<int> GetMidLevelList() const { return midList; }
                QList<int> GetBottomLevelList() const { return bottomList; }
            #endif
            
            private:
                QList<int> topList;
                QList<int> midList;
                QList<int> bottomList;
            };
            
            int main(int argc, char *argv[])
            {
                    MyList myList;
                    QElapsedTimer timer;
                    timer.start();
            #ifdef USE_FOREACH
                    Q_FOREACH(int i, myList.GetTopLevelList())
                    {
                        Q_FOREACH(int j, myList.GetMidLevelList())
                        {
                            Q_FOREACH(int k, myList.GetBottomLevelList())
                            {
                                int w = i + k + j;
                            }
                        }
                    }
            #else
                    const QList<int>& topLevel = myList.GetTopLevelList();
                    for (int i = 0; i < topLevel.size(); ++i)
                    {
                        const QList<int>& midLevel = myList.GetMidLevelList();
                        for (int j = 0; j < midLevel.size(); ++j)
                        {
                            const QList<int>& lowLevel = myList.GetBottomLevelList();
                            for (int k = 0; k < lowLevel.size(); ++k)
                            {
                                int w =  i + k + j;
                            }
                        }
                    }
            #endif
                    qDebug() << "<<<<< Elapsed = " << timer.elapsed();
            }
            

            Here is the table for the elapsed time for the combination of USE_REF and USE_FOREACH

            • USE_REF = 0, USE_FOREACH = 0 ==> 1ms
            • USE_REF = 0, USE_FOREACH = 1 ==> 3ms
            • USE_REF = 1, USE_FOREACH = 0 ==> 0ms
            • USE_REF = 1, USE_FOREACH = 1 ==> 3ms

            I am specifically interested in the case of USE_REF = 0 and USE_FOREACH = 0, where I am getting the elapsed time as 1ms. Why is this different from USE_REF = 1, and USE_FOREACH = 0, when I am not calling any non-const function (which means there should not be any deep copy)?

            I have also included the case USE_FOREACH and would like to know on why it is impacting the performance so much.

            Tested on: Qt-Version: Qt 5.4.1 MacOS 10.12.2

            Thank you in advance :-)

            J.HilkJ Offline
            J.HilkJ Offline
            J.Hilk
            Moderators
            wrote on last edited by
            #5

            @Sivan said in Returning QList as reference and value has difference in performance:

            I am specifically interested in the case of USE_REF = 0 and USE_FOREACH = 0, where I am getting the elapsed time as 1ms. Why is this different from USE_REF = 1, and USE_FOREACH = 0, when I am not calling any non-const function (which means there should not be any deep copy)?

            I have also included the case USE_FOREACH on why it is impacting the performance so much.

            Tested on: Qt-Version: Qt 5.4.1 MacOS 10.12.2

            Thank you in advance :-)

            Were your tests in debug or release mode?

            Also qt's foreach function is a bad idea. It creates by definition a copy of each element to work with. That's going to impact your performance.

            You should rather sue the "new" for syntax

            per reference to modfy the entries.

            //all ints to 0
            for(int &i : myList)
                 i = 0;
            
            

            or const refence to work with the values

            int sum(0);
            for(const int &i : myList)
                 sum +=i;
            

            Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


            Q: What's that?
            A: It's blue light.
            Q: What does it do?
            A: It turns blue.

            S 1 Reply Last reply
            0
            • VRoninV Offline
              VRoninV Offline
              VRonin
              wrote on last edited by
              #6

              I think you are focusing on the wrong problem tbh.

              changing for (int i = 0; i < topLevel.size(); ++i) into for (int i = 0, maxVal=topLevel.size(); i < maxVal; ++i) probably has a bigger impact on performance than the allocation of a shared data pointer.

              "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
              ~Napoleon Bonaparte

              On a crusade to banish setIndexWidget() from the holy land of Qt

              S 1 Reply Last reply
              2
              • JKSHJ JKSH

                @Sivan said in Returning QList as reference and value has difference in performance:

                • USE_REF = 0, USE_FOREACH = 0 ==> 1ms
                • USE_REF = 0, USE_FOREACH = 1 ==> 3ms
                • USE_REF = 1, USE_FOREACH = 0 ==> 0ms
                • USE_REF = 1, USE_FOREACH = 1 ==> 3ms

                These time differences are very very small. Are you sure they're not caused by jitter in your computer? (For example, the OS might have decided to do some background maintenance just as you started the test, which could add a few ms to your code execution)

                To do a proper test, do both of the following:

                1. Use a large dataset.
                  • 2 000 integers is nothing to a modern CPU. Try 200 000 instead.
                2. Do repeated measurements and find the average:
                QElapsedTimer timer;
                QVector<qint64> measurements;
                for (int x = 0; x < 10000; ++x) {
                    timer.start();
                
                #ifdef USE_FOREACH
                    ...
                #endif
                
                    measurements << timer.elapsed();
                }
                
                double totalTime = 0;
                for (time : measurements)
                    totalTime += time;
                
                qDebug() << "Average time =" << totalTime / 10000;
                
                // TODO: Export the measurement values to a file.
                // Use a spreadsheet program to plot a histogram
                

                Note: It is possible that the first value in measurement is always larger than the other 9999. If this is the case, remove the 1st value before calculating the average.

                S Offline
                S Offline
                Sivan
                wrote on last edited by
                #7

                @JKSH, yes the time difference in this case is very small but it helps in improving the performance of my application to achieve 60fps for example.

                I tried the test as you mentioned. FYI, my initial code had 3 nested "for loops" with the second loop as 2000. I changed it to 200, 000 as you said.
                I ran the test for 1000 times instead of 10, 000 times as it was taking too long. Removing the first loop's case and taking the average of 999, here is what I have got:

                • USE_REF = 1, USE_FOREACH = 0 ==> 31ms

                • USE_REF = 0, USE_FOREACH = 0 ==> 85ms

                1 Reply Last reply
                0
                • J.HilkJ J.Hilk

                  @Sivan said in Returning QList as reference and value has difference in performance:

                  I am specifically interested in the case of USE_REF = 0 and USE_FOREACH = 0, where I am getting the elapsed time as 1ms. Why is this different from USE_REF = 1, and USE_FOREACH = 0, when I am not calling any non-const function (which means there should not be any deep copy)?

                  I have also included the case USE_FOREACH on why it is impacting the performance so much.

                  Tested on: Qt-Version: Qt 5.4.1 MacOS 10.12.2

                  Thank you in advance :-)

                  Were your tests in debug or release mode?

                  Also qt's foreach function is a bad idea. It creates by definition a copy of each element to work with. That's going to impact your performance.

                  You should rather sue the "new" for syntax

                  per reference to modfy the entries.

                  //all ints to 0
                  for(int &i : myList)
                       i = 0;
                  
                  

                  or const refence to work with the values

                  int sum(0);
                  for(const int &i : myList)
                       sum +=i;
                  
                  S Offline
                  S Offline
                  Sivan
                  wrote on last edited by
                  #8

                  @J.Hilk I am running my test in debug mode. Yes, going forward, I am avoiding Q_FOREACH.

                  1 Reply Last reply
                  0
                  • VRoninV VRonin

                    I think you are focusing on the wrong problem tbh.

                    changing for (int i = 0; i < topLevel.size(); ++i) into for (int i = 0, maxVal=topLevel.size(); i < maxVal; ++i) probably has a bigger impact on performance than the allocation of a shared data pointer.

                    S Offline
                    S Offline
                    Sivan
                    wrote on last edited by
                    #9

                    @VRonin That DOES help in improving the performance. But there are still significant difference between the two functions. Applying this method on the test requested by @JKSH, I got the following results:

                    • list itemUSE_REF = 1, USE_FOREACH = 0 ==> 14ms

                    • list itemUSE_REF = 0, USE_FOREACH = 0 ==> 66ms

                    1 Reply Last reply
                    1
                    • VRoninV Offline
                      VRoninV Offline
                      VRonin
                      wrote on last edited by
                      #10

                      The difference you are seeing is the burden of calling a copy constructor and destructor inside the nested loops (even if it's cheap it still has to go on the stack, increase an integer and decrease it again)

                      The below should show no significant difference for return vale and ref.

                      const QList<int>& topLevel = myList.GetTopLevelList();
                      const QList<int>& midLevel = myList.GetMidLevelList();
                      const QList<int>& lowLevel = myList.GetBottomLevelList();
                              for (int i = 0, maxi=topLevel.size(); i < maxi; ++i)
                              {
                      
                                  for (int j = 0, maxj=midLevel.size(); j < maxj; ++j)
                                  {
                                      for (int k = 0, maxk=lowLevel.size(); k < maxk; ++k)
                                      {
                                          int w =  i + k + j;
                                      }
                                  }
                              }
                      

                      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                      ~Napoleon Bonaparte

                      On a crusade to banish setIndexWidget() from the holy land of Qt

                      S 1 Reply Last reply
                      2
                      • VRoninV VRonin

                        The difference you are seeing is the burden of calling a copy constructor and destructor inside the nested loops (even if it's cheap it still has to go on the stack, increase an integer and decrease it again)

                        The below should show no significant difference for return vale and ref.

                        const QList<int>& topLevel = myList.GetTopLevelList();
                        const QList<int>& midLevel = myList.GetMidLevelList();
                        const QList<int>& lowLevel = myList.GetBottomLevelList();
                                for (int i = 0, maxi=topLevel.size(); i < maxi; ++i)
                                {
                        
                                    for (int j = 0, maxj=midLevel.size(); j < maxj; ++j)
                                    {
                                        for (int k = 0, maxk=lowLevel.size(); k < maxk; ++k)
                                        {
                                            int w =  i + k + j;
                                        }
                                    }
                                }
                        
                        S Offline
                        S Offline
                        Sivan
                        wrote on last edited by
                        #11

                        @VRonin , Yes, agree that the above code would show NO significant difference between the return value and ref. I intentionally wrote that code in such a manner because in the original issue that I am trying to solve, each loop depends on its outer loop (i.e the second loop list depends on the first loop list).

                        I will take your's and @jsulm answer on why there is a performance difference between calling the function which return by value and ref .
                        Thanks @VRonin

                        1 Reply Last reply
                        0

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved