vector::push_back performance

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Antonios Christofides

    #1

    vector::push_back performance

    Hi,

    As I read in the archives, the performance problem caused by memory
    reallocations during vector::push_ba ck is a common one. My first C++
    program is suffering from it: 300 thousand push_backs result,
    according to the profiler, in 20 reallocations; these 20 reallocations
    account for 3.6 seconds (Celeron 1.3G), which is 40% of total
    execution time.

    What I don't understand: why is the reallocation code so complex? I
    studied the library source and I have a hard time understanding it,
    but it seems to be copying the vector item by item in each
    reallocation. Why wouldn't a "realloc" suffice?

    And, given that I don't know the vector size beforehand, is there
    anything else I can do other than trying deqeue or a guessed
    vector::reserve ?

    In case it matters, I'm using gcc 3.3 with its standard c++ library on
    a Debian sarge, but portability is also an issue.

    Thanks!
  • Rolf Magnus

    #2
    Re: vector::push_ba ck performance

    Antonios Christofides wrote:
    [color=blue]
    > Hi,
    >
    > As I read in the archives, the performance problem caused by memory
    > reallocations during vector::push_ba ck is a common one. My first C++
    > program is suffering from it: 300 thousand push_backs result,
    > according to the profiler, in 20 reallocations; these 20 reallocations
    > account for 3.6 seconds (Celeron 1.3G), which is 40% of total
    > execution time.
    >
    > What I don't understand: why is the reallocation code so complex? I
    > studied the library source and I have a hard time understanding it,
    > but it seems to be copying the vector item by item in each
    > reallocation. Why wouldn't a "realloc" suffice?[/color]

    That's what a "realloc" does, too. You usually can't easily make an already
    allocated memory block bigger (what would you do with data after it?), so a
    new block must be allocated and the data be copied over to it, then the old
    one destroyed.
    [color=blue]
    > And, given that I don't know the vector size beforehand, is there
    > anything else I can do other than trying deqeue or a guessed
    > vector::reserve ?[/color]

    Not much.
    [color=blue]
    > In case it matters, I'm using gcc 3.3 with its standard c++ library on
    > a Debian sarge, but portability is also an issue.[/color]

    Comment

    • Victor Bazarov

      #3
      Re: vector::push_ba ck performance

      Antonios Christofides wrote:[color=blue]
      > As I read in the archives, the performance problem caused by memory
      > reallocations during vector::push_ba ck is a common one. My first C++
      > program is suffering from it: 300 thousand push_backs result,
      > according to the profiler, in 20 reallocations; these 20 reallocations
      > account for 3.6 seconds (Celeron 1.3G), which is 40% of total
      > execution time.[/color]

      Three hundred thousand push_backs into a vector without reserve? Seems
      unjustified.
      [color=blue]
      > What I don't understand: why is the reallocation code so complex? I
      > studied the library source and I have a hard time understanding it,
      > but it seems to be copying the vector item by item in each
      > reallocation. Why wouldn't a "realloc" suffice?[/color]

      What would 'realloc' do? Place the objects each at a different memory
      location without letting the object know? That's not right. Objects
      may need to know where they have been constructed. They might want to
      let other classes or objects know of their location, etc.
      [color=blue]
      > And, given that I don't know the vector size beforehand, is there
      > anything else I can do other than trying deqeue or a guessed
      > vector::reserve ?[/color]

      Nope. Using standard containers requires acknowledging the trade-offs.
      If you need fast push_back and you don't know the size, you should
      probably use 'std::list' or 'std::deque'. If you need random access
      afterwards, don't push-back without reserving. I bet any decent book
      on Standard containers talks about how to pick the container well-suited
      for your task. Of course, that assumes that you know what your task is.
      [color=blue]
      > In case it matters, I'm using gcc 3.3 with its standard c++ library on
      > a Debian sarge, but portability is also an issue.[/color]

      It doesn't matter, at least not here.

      V

      Comment

      • Phlip

        #4
        Re: vector::push_ba ck performance

        Antonios Christofides wrote:
        [color=blue]
        > As I read in the archives, the performance problem caused by memory
        > reallocations during vector::push_ba ck is a common one. My first C++
        > program is suffering from it: 300 thousand push_backs result,
        > according to the profiler, in 20 reallocations; these 20 reallocations
        > account for 3.6 seconds (Celeron 1.3G), which is 40% of total
        > execution time.[/color]

        Do you think you could go to an algorithm where you push less back?
        [color=blue]
        > What I don't understand: why is the reallocation code so complex? I
        > studied the library source and I have a hard time understanding it,
        > but it seems to be copying the vector item by item in each
        > reallocation. Why wouldn't a "realloc" suffice?[/color]

        Read Herb Sutter's way-cool GOTW series, and his books /Exceptional C++/. He
        impugns the container class of choice should usually be std::deque<>, not
        std::vector<>. It frags not memory like std::list<>, and it's optimal to
        push things to both the beginning and end.

        --
        Phlip



        Comment

        • Andrew Koenig

          #5
          Re: vector::push_ba ck performance

          "Antonios Christofides" <anthony@itia.n tua.gr> wrote in message
          news:dc5.4159da 31.d0edf@voltai re...
          [color=blue]
          > As I read in the archives, the performance problem caused by memory
          > reallocations during vector::push_ba ck is a common one. My first C++
          > program is suffering from it: 300 thousand push_backs result,
          > according to the profiler, in 20 reallocations; these 20 reallocations
          > account for 3.6 seconds (Celeron 1.3G), which is 40% of total
          > execution time.[/color]

          I'm skeptical.

          Here's a little program:

          #include <vector>

          int main()
          {
          std::vector<int > v;
          for (std::vector<in t>::size_type i = 0; i != 1000000; ++i)
          v.push_back(i);
          return 0;
          }

          When I run this program on my machine (admittedly faster than 1.3G, but no
          more than twice as fast), it runs in three *hundredths* of a second. And it
          calls push_back a million times, not 300,000 times.

          This behavior suggests to me that your vector must contain objects of a
          class that is much more expensive to copy than int.

          So I think we need to see more information about your program in order to
          understand the source of the performance problem.


          Comment

          • Method Man

            #6
            Re: vector::push_ba ck performance

            > That's what a "realloc" does, too. You usually can't easily make an
            already[color=blue]
            > allocated memory block bigger (what would you do with data after it?), so[/color]
            a[color=blue]
            > new block must be allocated and the data be copied over to it, then the[/color]
            old[color=blue]
            > one destroyed.
            >[/color]

            All the texts I have read state that, when a dynamic array needs to be
            extended, it is better/faster to 'realloc' instead of creating a new array
            and copying the elements manually.

            So why is 'realloc' more efficient?


            Comment

            • Victor Bazarov

              #7
              Re: vector::push_ba ck performance

              "Method Man" <a@b.c> wrote...[color=blue][color=green]
              >> That's what a "realloc" does, too. You usually can't easily make an[/color]
              > already[color=green]
              >> allocated memory block bigger (what would you do with data after it?), so[/color]
              > a[color=green]
              >> new block must be allocated and the data be copied over to it, then the[/color]
              > old[color=green]
              >> one destroyed.
              >>[/color]
              >
              > All the texts I have read state that, when a dynamic array needs to be
              > extended, it is better/faster to 'realloc' instead of creating a new array
              > and copying the elements manually.[/color]

              You've been reading too many C texts, haven't you?
              [color=blue]
              > So why is 'realloc' more efficient?[/color]

              I am not sure how to answer this question. Imagine you have a tree which
              has grown too far up and needs trimming. You decide to trim it a foot off
              the ground because it's too inefficient to climb up and trim every branch
              that could use a trimming. You lose the tree. Why is cutting it down more
              efficient than doing it right? There is no answer. Another analogy: a TV
              set and a need to deliver it from the 7th floor to the truck parked outside.
              It can be done on an elevator, it could be done on the stairs. Or, somebody
              might decide that it's more efficient to lower it down through the window.
              Without ropes. Hey, a couple of seconds and it's down on the ground, no?
              Why is throwing it down more efficient than using the elevator (or stairs)?

              For POD you can do realloc. For non-POD classes (general case) realloc will
              simply not work. Efficient or not.

              V


              Comment

              • Siemel Naran

                #8
                Re: vector::push_ba ck performance

                "Method Man" <a@b.c> wrote in message news:oJq6d.4788
                [color=blue]
                > All the texts I have read state that, when a dynamic array needs to be
                > extended, it is better/faster to 'realloc' instead of creating a new array
                > and copying the elements manually.
                >
                > So why is 'realloc' more efficient?[/color]

                Because if more space exists at the end of the existing array to yield a
                larger array, then realloc will just claim that space. Say you do
                malloc(512u) and the program reserves bytes 0x101 to 0x300 for your array.
                Say bytes 0x301 to 0x0500 are free for anyone to use. If no other objects
                request this space and you realloc the array to 1024u bytes, then the system
                may just mark bytes 0x0101 to 0x0500 as in use by your array (so no-one else
                can claim it).

                There's no garuantee that space is available, but if it is, we save copying
                lots of bytes.


                Comment

                • Siemel Naran

                  #9
                  Re: vector::push_ba ck performance

                  "Antonios Christofides" <anthony@itia.n tua.gr> wrote in message
                  [color=blue]
                  > What I don't understand: why is the reallocation code so complex? I
                  > studied the library source and I have a hard time understanding it,
                  > but it seems to be copying the vector item by item in each
                  > reallocation. Why wouldn't a "realloc" suffice?[/color]

                  For user types, especially those managing dynamic memory like std::string or
                  std::deque, we have to call the overloaded copy constructor or operator= to
                  the copy.
                  [color=blue]
                  > And, given that I don't know the vector size beforehand, is there
                  > anything else I can do other than trying deqeue or a guessed
                  > vector::reserve ?[/color]

                  What about std::list? What is wrong with std::deque?

                  If you have a large object, it might be a good idea to make it reference
                  counted. You can use boost::shared_p tr or the like.


                  Comment

                  • Method Man

                    #10
                    Re: vector::push_ba ck performance


                    "Victor Bazarov" <v.Abazarov@com Acast.net> wrote in message
                    news:tEr6d.1720 09$3l3.160912@a ttbi_s03...[color=blue]
                    > "Method Man" <a@b.c> wrote...[color=green][color=darkred]
                    > >> That's what a "realloc" does, too. You usually can't easily make an[/color]
                    > > already[color=darkred]
                    > >> allocated memory block bigger (what would you do with data after it?),[/color][/color][/color]
                    so[color=blue][color=green]
                    > > a[color=darkred]
                    > >> new block must be allocated and the data be copied over to it, then the[/color]
                    > > old[color=darkred]
                    > >> one destroyed.
                    > >>[/color]
                    > >
                    > > All the texts I have read state that, when a dynamic array needs to be
                    > > extended, it is better/faster to 'realloc' instead of creating a new[/color][/color]
                    array[color=blue][color=green]
                    > > and copying the elements manually.[/color]
                    >
                    > You've been reading too many C texts, haven't you?
                    >[/color]

                    Well yea.

                    I was talking about arrays of POD types, but I didn't make that clear in my
                    post. Of course non-POD types can have constructors and overloaded
                    assignment operators, so my question wouldn't make sense in that case.
                    [color=blue][color=green]
                    > > So why is 'realloc' more efficient?[/color]
                    >
                    > I am not sure how to answer this question. Imagine you have a tree which
                    > has grown too far up and needs trimming. You decide to trim it a foot off
                    > the ground because it's too inefficient to climb up and trim every branch
                    > that could use a trimming. You lose the tree. Why is cutting it down[/color]
                    more[color=blue]
                    > efficient than doing it right? There is no answer. Another analogy: a TV
                    > set and a need to deliver it from the 7th floor to the truck parked[/color]
                    outside.[color=blue]
                    > It can be done on an elevator, it could be done on the stairs. Or,[/color]
                    somebody[color=blue]
                    > might decide that it's more efficient to lower it down through the window.
                    > Without ropes. Hey, a couple of seconds and it's down on the ground, no?
                    > Why is throwing it down more efficient than using the elevator (or[/color]
                    stairs)?[color=blue]
                    >
                    > For POD you can do realloc. For non-POD classes (general case) realloc[/color]
                    will[color=blue]
                    > simply not work. Efficient or not.
                    >[/color]

                    Your analogies didn't really help in my understanding of realloc. I was
                    looking for something like -- 'realloc' is never/sometimes/always more
                    efficient than malloc'ing a new array and manually copying from the old
                    array (of PODs). Then justify the choice.


                    Comment

                    • Antonios Christofides

                      #11
                      Re: vector::push_ba ck performance

                      > > All the texts I have read state that, when a dynamic array needs[color=blue][color=green]
                      > > to be extended, it is better/faster to 'realloc' instead of
                      > > creating a new array and copying the elements manually.
                      > >
                      > > So why is 'realloc' more efficient?[/color][/color]

                      Except for what was already said, I believe that even in the cases
                      when realloc needs to allocate a new memory block rather than extend
                      the existing one, it uses memmove or some similar operation which will
                      be faster than manually copying the elements if its implementation
                      uses specialized mass-byte-copying CPU instructions.

                      Comment

                      • Magnus

                        #12
                        Re: vector::push_ba ck performance


                        "Andrew Koenig" <ark@acm.org> skrev i melding
                        news:dop6d.6445 18$Gx4.176588@b gtnsc04-news.ops.worldn et.att.net...[color=blue]
                        >
                        > I'm skeptical.
                        >
                        > Here's a little program:
                        >
                        > #include <vector>
                        >
                        > int main()
                        > {
                        > std::vector<int > v;
                        > for (std::vector<in t>::size_type i = 0; i != 1000000; ++i)
                        > v.push_back(i);
                        > return 0;
                        > }
                        >
                        > When I run this program on my machine (admittedly faster than 1.3G, but no
                        > more than twice as fast), it runs in three *hundredths* of a second. And[/color]
                        it[color=blue]
                        > calls push_back a million times, not 300,000 times.
                        >
                        > This behavior suggests to me that your vector must contain objects of a
                        > class that is much more expensive to copy than int.
                        >
                        > So I think we need to see more information about your program in order to
                        > understand the source of the performance problem.
                        >
                        >[/color]

                        How do you time your execution time? Is there _one_ way to time this, or
                        might your method differ from the OP method?

                        - Magnus


                        Comment

                        • Antonios Christofides

                          #13
                          Re: vector::push_ba ck performance

                          Siemel Naran wrote:[color=blue]
                          > For user types, especially those managing dynamic memory like
                          > std::string or std::deque, we have to call the overloaded copy
                          > constructor or operator= to the copy.[/color]

                          Thank you for your responses. The contained type is indeed a class
                          that contains, among other things, a string and another user-class
                          object. I'll go back to Stroustrup to re-read about shallow and deep
                          copies and copy constructors and so on, and I'll come back either to
                          summarize or to ask more questions (the latter seems more likely :-)

                          Comment

                          • Rolf Magnus

                            #14
                            Re: vector::push_ba ck performance

                            Victor Bazarov wrote:
                            [color=blue]
                            > Antonios Christofides wrote:[color=green]
                            >> As I read in the archives, the performance problem caused by memory
                            >> reallocations during vector::push_ba ck is a common one. My first C++
                            >> program is suffering from it: 300 thousand push_backs result,
                            >> according to the profiler, in 20 reallocations; these 20 reallocations
                            >> account for 3.6 seconds (Celeron 1.3G), which is 40% of total
                            >> execution time.[/color]
                            >
                            > Three hundred thousand push_backs into a vector without reserve? Seems
                            > unjustified.[/color]

                            From all we know, the number of elements could be between 1 and 30 Million.
                            After all, the OP said he doesn't know the number of elements beforehand.
                            So how much would you reserve?

                            Comment

                            • Rolf Magnus

                              #15
                              Re: vector::push_ba ck performance

                              Method Man wrote:
                              [color=blue][color=green]
                              >> That's what a "realloc" does, too. You usually can't easily make an
                              >> already allocated memory block bigger (what would you do with data after
                              >> it?), so a new block must be allocated and the data be copied over to it,
                              >> then the old one destroyed.[/color]
                              >
                              > All the texts I have read state that, when a dynamic array needs to be
                              > extended, it is better/faster to 'realloc' instead of creating a new array
                              > and copying the elements manually.
                              >
                              > So why is 'realloc' more efficient?[/color]

                              It might just behave like push_back() and allocate more memory than
                              requested so that the next realloc doesn't need to copy the data to a new
                              block.

                              Comment

                              Working...