Join 137,179 C++ Programmers for FREE! Get instant access to thousands of C++ experts, tutorials, code snippets, and more! There are 2,366 people online right now. Registration is fast and FREE... Join Now!
The explanation: I'm working on a project for my CS class that has us reading data (integers) from three different files; sorting each one with an Insertion sort, a Shell sort, and a Quick sort; and displaying the processing times for each sort and each file. The files have 10, 10000, and 100000 records respectively. Obviously, an insertion sort on 100000 integers will take some time. I initally wrote the program using arrays while I was testing my sorting algorithms. The insertion sort would take a few minutes (<5) and be done. In order to make the program a bit more versatile, I've switched my arrays to vectors in case the grader decides to slip in unexpected file sizes when testing our programs. But now, the insertion sort has taken well over 20 minutes and still shows no sign of stopping.
My question is, are the access times for an Array and a Vector that different? Other than changing the parameters and adding a new variable to store a.size(), I haven't touched the algorithm. And it's taking far longer than it was before. My understanding is that there is very little difference between the two, especially if just referencing indices.
If it sheds any light, here's the code: (and, if it matters, I'm using Visual Studio 2005)
cpp
//Perform an Insertion sort on a Vector and time it //Param: Vector to sort //Post: Returns the number of clock ticks elapsed during sort clock_t timedInsertionSort(vector<int> &a) { clock_t start, end; // For tracking start and end times unsigned int s = a.size();
start = clock(); // Start time for(unsigned int i = 1; i < s; i++) { // Start at the second element (if there is one) unsigned int j = i; while(j > 0 && a[j] < a[j-1]) { // Move backward and push elements as necessary swap(a[j], a[j-1]); j--; } } end = clock(); // Stop time
return end - start; }
Is it a reference time issue? Is it some special case with this class I'm not aware of? Or something else entirely?
Vector implementations depend on compiler, but they are all far slower then a standard array.
A vector is basically a automatically resizing array. There are usually a lot of additional checks done, in order to make vector safer. This usually includes thing like bounds checking, checking to see if the array needs to grow/shrink, ect... Again, the actual implementation differ based on the compiler.
Generally, when the vector is first created, the array is only meant to hold a few elements. When it fills up, another larger array will be crated, and the old data will be copied into the new array. Again, the implementations are compiler dependent, but he growth rate is typically between 1.5 and 2. As you can imagine, all of these grows will really slow you program down.
Generally, you'll get the best performance if you just throw everything into a array, and then preform a QuickSort on it.
Yeah, I understand the long overhead times when adding elements to it. But the vector is already filled at this point. Does the [] operator really take that much longer? My newfound emperical evidence tells me I accept that it obviously must, but, really?
Update: it finished after almost 50 minutes of sorting. That's a 10x increase over the array's time (with identical data and algorithm).
Yes, there is a lot of overhead by just access the element you want. Again, everything is compiler dependent. Generally, a bounds check will be preformed in operator[]. Well over half of the actual call is overhead, compared to just accessing a array. Plus, you may lose your locality of reference, since you are dealing with several different variables.
Here, the operator[] does not do bounds checking, unlike the at member. If you pre-allocate a vector (by giving it an int parameter when constructing it... vector var(20); (initial size is 20), there should be no significant difference between a vector and an array in terms of performance if the code is inlined. But yes, if you really care about performance, you should preallocate.
The library also provides a sort function.
#include <algorithm>
vector<int> myvec;
......
std::sort(myvec.begin(), myvec.end());
This uses the IntroSort algorithm, which is a QuickSort that changes to HeapSort if the recursion depth is too deep (as worst case performance can be bad for quicksort, depending on how the partitions are made).
This post has been edited by perfectly.insane: 29 May, 2008 - 06:54 PM
You can also call reserve() to set the initial size of the vector.
From what I understood, he didn't know how many elements are in the file, so he could not do this. If you do know ahead of time, you might as well just allocate the array yourself.
From what I understood, he didn't know how many elements are in the file, so he could not do this. If you do know ahead of time, you might as well just allocate the array yourself.
Absolutely. But I don't see what that has to do with comparing performance of a vector vs. an a manually managed array. The vector is replacing the work that you'd be otherwise doing yourself.
A manually allocated array is still going to be faster then a vector. Yes, a vector requires less time to code, but you still have to deal with the additional overhead. Microsoft's implementation of the STL is not the best. A 10x difference in time really isn't worth it if you ask me. A manually allocated array will take far less then 30 minutes to write, at superior performance.
Vectors are used because they can grow. You access time will, in general, suck compared to an array. On the other hand, arrays don't grow and are often subject to size (stack) limitations.
Given that you know your access time for a vector element will always underpreform an array, you'd reasonably try to minimize item requests from the collection. Curiously, your insertion sort is a worst case, because you're constanly asking for values. One simple change to your code will nearly double the speed. That is, holding the value being compared. Your constant swaping is a real performance hit.
for (i=1; i < array_size; i++) { index = numbers[i]; j = i; while ((j > 0) && (numbers[j-1] > index)) { numbers[j] = numbers[j-1]; j--; } numbers[j] = index; } }
However, consider what an insertion sort is doing. It's moving all those elements starting at the bottom, just to make room for the next element. A vector suffers the exact same way an array does for this. However, if you used a "list" container instead, you could optimize how the elements are moved by simply inserting the current element into the correct position.
The template containers are very useful and should generally be prefer to arrays. If you have a specfic task, you probably can find a specific container to do the job.
Well, I turned in the project last night and noticed your post just now (kept the vectors in case the grader decided to slip something unexpected in). The temporary variable vs swapping makes sense now that you've mentioned it. Just out of curiosity I'll change the code to reflect your change and see how much time it saves.
UPDATE: I talked to the programmers where I work and they suggested I compile and run in Release mode instead of Debug mode. That dropped the processing time from 50 minutes to 30 seconds. If anyone cared to know, here are various results on the list of 100,000 records: Debug mode: Using Swaps in Insertion sort: Vector: 2433219 ticks Array: 209594 ticks Using baavgai's suggested change: Vector: 1680359 ticks Array: 18718 ticks Release mode: Using Swaps in Insertion sort: Vector: 29610 ticks Array: 7281 ticks Using baavgai's suggested change: Vector: 27781 ticks Array: 4875 ticks
Looks like Debug mode was the biggest problem. But Vectors and swapping were definite contributors...
This post has been edited by chaosTechnician: 30 May, 2008 - 03:37 PM
UPDATE: I talked to the programmers where I work and they suggested I compile and run in Release mode instead of Debug mode. That dropped the processing time from 50 minutes to 30 seconds.
Yes... debug mode probably sets a definition that when the preprocessor processes the file that contains the vector implementation, bounds checking is included with accesses. I have done some of my own research, and I've come to the conclusion that the performance for vector isn't that great in some implementations. I have a real comparison of sorting on arrays vs. vectors.
This is for STLPort-5.1.4: (compiled with -DNDEBUG and -D_UITHREADS)
CODE
Test N int[] vector<int> Sorted? ==================== ========== ============= ============= ======= std::sort 100 0.03 ms 0.02 ms Yes std::stable_sort 100 0.03 ms 0.02 ms Yes quick_sort 100 0.02 ms 0.02 ms Yes std::sort 1000 0.35 ms 0.30 ms Yes std::stable_sort 1000 0.25 ms 0.23 ms Yes quick_sort 1000 0.22 ms 0.31 ms Yes std::sort 10000 4.24 ms 4.18 ms Yes std::stable_sort 10000 3.07 ms 2.95 ms Yes quick_sort 10000 2.64 ms 2.49 ms Yes std::sort 100000 52.21 ms 52.02 ms Yes std::stable_sort 100000 40.83 ms 40.81 ms Yes quick_sort 100000 30.25 ms 30.33 ms Yes std::sort 1000000 630.66 ms 618.89 ms Yes std::stable_sort 1000000 485.84 ms 486.18 ms Yes quick_sort 1000000 367.21 ms 349.81 ms Yes std::sort 10000000 7735.34 ms 7793.53 ms Yes std::stable_sort 10000000 5236.51 ms 5253.54 ms Yes quick_sort 10000000 4081.56 ms 4081.99 ms Yes
Here's libstdc++v3:
CODE
Test N int[] vector<int> Sorted? ==================== ========== ============= ============= ======= std::sort 100 0.02 ms 0.03 ms Yes std::stable_sort 100 0.03 ms 0.05 ms Yes quick_sort 100 0.02 ms 0.06 ms Yes std::sort 1000 0.17 ms 0.44 ms Yes std::stable_sort 1000 0.18 ms 0.55 ms Yes quick_sort 1000 0.20 ms 0.76 ms Yes std::sort 10000 2.23 ms 5.29 ms Yes std::stable_sort 10000 2.14 ms 6.65 ms Yes quick_sort 10000 2.46 ms 9.13 ms Yes std::sort 100000 26.66 ms 64.63 ms Yes std::stable_sort 100000 31.80 ms 89.62 ms Yes quick_sort 100000 29.47 ms 113.97 ms Yes std::sort 1000000 320.32 ms 727.14 ms Yes std::stable_sort 1000000 374.14 ms 1076.93 ms Yes quick_sort 1000000 336.05 ms 1337.75 ms Yes std::sort 10000000 3749.91 ms 8651.49 ms Yes std::stable_sort 10000000 4038.01 ms 11621.53 ms Yes quick_sort 10000000 3949.48 ms 14734.73 ms Yes
libstdc++ returns a proxy object for vector<T>::begin (__normal_iterator), whereas STLPort returns a direct pointer to the buffer. So the difference here is expected.
I noticed that STLPort's std::sort algorithm is rather slow. quick_sort is my own implementation. It's not very sophisticated. Here's the benchmark code:
template <typename Function> void perform_test(int N, Function f, const char* test, std::ostream& os) { using std::setw; int* ar = new int[N]; std::vector<int> ar2(N); LARGE_INTEGER start, end, start2, end2, freq;
fill_random(ar, ar + N, rand); std::copy(ar, ar + N, ar2.begin()); QueryPerformanceFrequency(&freq); QueryPerformanceCounter(&start); f(ar, ar + N); QueryPerformanceCounter(&end); QueryPerformanceCounter(&start2); f(ar2.begin(), ar2.end()); QueryPerformanceCounter(&end2);
srand(time(0)); for(int n = 100; n <= N; n *= 10) { perform_test(n, sort_test(), "std::sort", std::cout); perform_test(n, stable_sort_test(), "std::stable_sort", std::cout); perform_test(n, quick_sort_test(), "quick_sort", std::cout); } return 0; }
Edit: These numbers should be a bit more accurate with the process priority turned up a bit (and not running 100 other processes at the same time helps also).
This post has been edited by perfectly.insane: 30 May, 2008 - 06:25 PM