Ioannis Vranos said:
John Carson wrote:
Your code tweaked and improved:
By making start and finish of type clock_t, you make this line:
cout << (finish - start)/CLOCKS_PER_SEC << endl;
perform integer division, so you get an integer result. This is anything but
an improvement. Cast one of those terms to a double in order to get a
non-zero result for vector.
C:\c>temp
List elapsed time is 2
Vector elapsed time is 0
C:\c>
Of course if I had reserved enough space for vector before, they
would have the same performance.
What? Your figures show vector with superior performance (infinitely
superior, actually, but I won't claim that) and you think that reserving
enough space for the vector would even things up?
Check the table in 17.1.2 on page 464 of TC++PL3.
Vectors have no additional time-cost advantage than lists for back
operations, list has O(1) while vector has O(1)+ (the + is when
reallocations take place). You have O(1) for back operations with
vector while its size() is smaller its capacity() (which can be
adjusted with reserve()).
You seem to be under the impression that O properties say all that is
relevant. All O results tell you is how time varies with n. They tell you
nothing about the base time when n==1. Two operations can both be O(1) yet
one operation can take a million times as long as the other.
As I understand it, when you add an element to the end of a list, memory is
allocated for that final element with a call to new. Calls to new are
expensive.
When you add an element to the end of a vector, there is no call to new if
there is sufficient capacity. This makes the vector operation quicker than
the list operation. If there is insufficient capacity in the vector, then
new memory is allocated and all elements need to be copied over from the old
memory to the new. This makes the vector operation slower than the list
operation.
You seem to be misunderstanding the meaning of O(1)+. This does not mean
"more than O(1)". It means, to quote Stroustrup, that "occasionally a
significant extra cost is incurred" --- extra relative to what normally
happens, not extra relative to what is required for O(1) performance.
To further explain: capacity is added to vectors by multiplying pre-existing
capacity by some constant factor. This means that the number of calls to new
diminishes relative to n as n increases, e.g., if you start with a capacity
of 1 and double each time, then you need 3 calls to get 8 elements, and 6
calls to get 64 elements. Thus with 8 elements, each element requires 3/8 of
a call to new, whereas with 64 elements each element requires 6/64 of a call
to new. With 1 million elements, each element requires roughly 1/50,000 of a
call to new. Thus this aspect of insertion is *less* than O(1). (This
argument assumes that calls to new cost the same regardless of the amount of
memory allocated which probably isn't true, especially when the vector gets
large; this qualification, however, doesn't seem sufficiently important to
overturn the conclusion in most cases.) The total amount of copying from old
memory to new is proportionate to n, so this aspect is O(1).
On two computers (a slow Pentium II and a fast laptop), I get a consistent
result that adding to the back of list takes around the same amount of time
regardless of whether it is 1 million elements to a single list or 100
elements to 10,000 lists.
With a vector, by contrast, adding 1 million elements to a single vector
takes about half the time it takes to add 100 elements to 10,000 vectors.
This is consistent with my "less than O(1)" argument above.
When the number of elements gets larger, the story becomes more complicated
(the peculiarities of the memory manager come into play and stack memory
becomes an issue, given that I declared the arrays on the stack, so it is
better to allocate them statically). Neither container seems to have O(1)
performance. However, it remains the case that
1. the vector is faster than the list.
2. the vector is 2-6 times faster when using a single large container than
when using multiple small containers for the same total capacity.
Of course, vector memory must be contiguous, so vectors may have problems if
memory becomes fragmented.