This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

reducing needless normal_iterator usage in vector


At least, I think it's needless.

There are a few places in std::vector where we pass begin() and end() to
other member functions, using them in expressions where pointer arithmetic
would do just as well.

The reason is simply because that's how the code is in the HP/SGI codebase.
But they used raw pointers as their iterator typedef; typing "begin()"
was simply clearer code.  Since we use a wrapper class for type safety,
there can be tiny performance hits, slowly accumulating.  For simple uses,
the clarity doesn't seem to be worth the risk of extra code, especially
since "_M_start" is just as clear as "begin()".

Replacing simple expressions like "begin() + n" with "_M_start + n"
skips the normal_iterator ctor and the normal_iterator's overloaded ops,
and gets right to the pointer arithmetic that we eventually do anyway.

For example, changing

   reference operator[](size_type __n) { return *(begin() + __n); }

to

   reference operator[](size_type __n) { return *(_M_start + __n); }

gives us the following difference in the generated assembly.  With begin():

  std::vector<int, std::allocator<int> >::operator[](unsigned):
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %ebx
    subl    $36, %esp
    leal    -8(%ebp), %ebx
    leal    -12(%ebp), %eax
    movl    %eax, (%esp)
    movl    8(%ebp), %eax
    movl    %eax, 4(%esp)
    call    std::vector<int, std::allocator<int> >::begin()
    subl    $4, %esp
    leal    -12(%ebp), %eax
    movl    %ebx, (%esp)
    movl    %eax, 4(%esp)
    movl    12(%ebp), %eax
    movl    %eax, -16(%ebp)
    leal    -16(%ebp), %eax
    movl    %eax, 8(%esp)
    call    __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >::operator+(int const&) const
    subl    $4, %esp
    leal    -8(%ebp), %eax
    movl    %eax, (%esp)
    call    __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >::operator*() const
    movl    -4(%ebp), %ebx
    leave
    ret

But with _M_start:

  std::vector<int, std::allocator<int> >::operator[](unsigned):
	pushl	%ebp
	movl	%esp, %ebp
	movl	8(%ebp), %edx
	movl	12(%ebp), %eax
	sall	$2, %eax
	addl	(%edx), %eax
	popl	%ebp
	ret

Note that I wrote "for simple uses" above; I'm proposing only to make the
change in small obvious functions.  For larger member functions, I don't
think it matters, but that's only my opinion... much like the rest of this
message has been.  :-)

Thoughts?

Phil

-- 
If ye love wealth greater than liberty, the tranquility of servitude greater
than the animating contest for freedom, go home and leave us in peace.  We seek
not your counsel, nor your arms.  Crouch down and lick the hand that feeds you;
and may posterity forget that ye were our countrymen.            - Samuel Adams


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]