Using an iterator results in incrementing a pointer (for incrementing) and for dereferencing into dereferencing a pointer.
With an index, incrementing should be equally fast, but looking up an element involves an addition (data pointer+index) and dereferencing that pointer, but the difference should be marginal.
at()
also checks if the index is within the bounds, so it could be slower.
Benchmark results for 500M iterations, vector size 10, with gcc 4.3.3 (-O3), linux 2.6.29.1 x86_64:
at()
: 9158ms
operator[]
: 4269ms
iterator
: 3914ms
YMMV, but if using an index makes the code more readable/understandable, you should do it.
2021 update
With modern compilers, all options are practically free, but iterators are very slightly better for iterating and easier to use with range-for loops (for(auto& x: vs)
).
Code:
#include <vector>
void iter(std::vector<int> &vs) {
for(std::vector<int>::iterator it = vs.begin(); it != vs.end(); ++it)
*it = 5;
}
void index(std::vector<int> &vs) {
for(std::size_t i = 0; i < vs.size(); ++i)
vs[i] = 5;
}
void at(std::vector<int> &vs) {
for(std::size_t i = 0; i < vs.size(); ++i)
vs.at(i) = 5;
}
The generated assembly for index()
and at()
is identical ([godbolt])(https://godbolt.org/z/cv6Kv4b6f), but the loop setup for iter()
is three instructions shorter:
iter(std::vector<int, std::allocator<int> >&):
mov rax, QWORD PTR [rdi]
mov rdx, QWORD PTR [rdi+8]
cmp rax, rdx
je .L1
.L3: ; loop body
mov DWORD PTR [rax], 5
add rax, 4
cmp rax, rdx
jne .L3
.L1:
ret
index(std::vector<int, std::allocator<int> >&):
mov rax, QWORD PTR [rdi]
mov rdx, QWORD PTR [rdi+8]
sub rdx, rax
mov rcx, rdx
shr rcx, 2
je .L6
add rdx, rax
.L8: ; loop body
mov DWORD PTR [rax], 5
add rax, 4
cmp rdx, rax
jne .L8
.L6:
ret