10

I have converted this simple method from C# to C++. It reads a path table and populates a list of lists of ints (or a vector of vectors of ints).

A sample line from the path table would be something like

0 12 5 16 n

I realise there are better ways of doing this in general, but for now I just want to know why my C++ code is taking so much longer. e.g. 10 minutes as opposed to 10 seconds with the C# version. Here is my C++ code. I'm guessing I've done something a bit drastically wrong.

//Parses the text path vector into the engine
void Level::PopulatePathVectors(string pathTable)
{
    // Read the file line by line.
    ifstream myFile(pathTable);

        for (unsigned int i = 0; i < nodes.size(); i++)
        {
            pathLookupVectors.push_back(vector<vector<int>>());

            for (unsigned int j = 0; j < nodes.size(); j++)
            {
                string line;

                if (getline(myFile, line)) //Enter if a line is read successfully
                {
                    stringstream ss(line);
                    istream_iterator<int> begin(ss), end;
                    pathLookupVectors[i].push_back(vector<int>(begin, end));
                }
            }
        }
    myFile.close();
}

Here is the C# version:

private void PopulatePathLists(string pathList)
{
    // Read the file and display it line by line.
    StreamReader streamReader = new StreamReader(pathList);

    for (int i = 0; i < nodes.Count; i++)
    {
        pathLookupLists.Add(new List<List<int>>());

        for (int j = 0; j < nodes.Count; j++)
        {
            string str = streamReader.ReadLine();
            pathLookupLists[i].Add(new List<int>());

            //For every string (list of ints) - put each one into these lists
            int count = 0;
            string tempString = "";

            while (str[count].ToString() != "n") //While character does not equal null terminator
            {
                if (str[count].ToString() == " ") //Character equals space, set the temp string 
                                                  //as the node index, and move on
                {
                    pathLookupLists[i][j].Add(Convert.ToInt32(tempString));
                    tempString = "";
                }
                else //If characters are adjacent, put them together
                {
                    tempString = tempString + str[count];
                }
                count++;
            }
        }
    }
    streamReader.Close();
}

Sorry this is so specific, but I'm stumped.

EDIT - A lot of people have said they have tested this code, and it takes mere seconds for them. All I know is, if I comment out the call to this function, the program loads in seconds. With the function call it takes 5 minutes. Almost exactly. I'm really stumped. What could the problem be?

Here is the PathTable it's using.

EDIT - I tried running the function in a program on its own, and it took a few seconds, but I'm afraid I don't know enough to be able to know how to fix this problem. Obviously it's not the code. What could it be? I checked where it's being called to see if there were multiple calls, but there aren't. It's in a constructor of the game's level and that is only called once.

EDIT - I understand that the code is not the best it could be, but that isn't the point here. It runs quickly on its own - about 3 seconds and that's fine for me. The problem I'm trying to solve is why it takes so much longer inside the project.

EDIT - I commented out all of the game code apart from the main game loop. I placed the method into the initialize section of the code which is run once on start up. Apart from a few methods setting up a window it's now pretty much the same as the program with ONLY the method in, only it STILL takes about 5 minutes to run. Now I know it has nothing to do with dependencies on the pathLookupVectors. Also, I know it's not a memory thing where the computer starts writing to the hard drive because while the slow program is chugging away running the method, I can open another instance of Visual Studio and run the single method program at the same time which completes in seconds. I realise that the problem might be some basic settings, but I'm not experienced so apologies if this does disappointingly end up being the reason why. I still don't have a clue why it's taking so much longer.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dollarslice
  • 9,917
  • 22
  • 59
  • 87
  • 5
    Are you compiling with optimisation? – Mike Seymour Oct 18 '11 at 15:12
  • Why are you using str[count]!='\n' in the C++ version and str[count].ToString()!="\n" in the second? – luiscubal Oct 18 '11 at 15:14
  • 2
    @MikeSeymour I don't think optimisations will account for all of the 9 minutes 50 seconds extra that the C++ method takes. Have you tried debugging both and comparing execution flow? My guess is that with such a difference there is some sort of logic problem that leads the C++ method to loop more. – Justin Oct 18 '11 at 15:14
  • What compiler/version? My first guess is that with every growth of the outer vector all of the elements are being copied, but some STL implementations (Dinkumware in VS) optimize the deep copies away by *moving* the internal vectors (even in C++03, by using `vector::swap`) – David Rodríguez - dribeas Oct 18 '11 at 15:15
  • Maybe buffering could explain the difference? Not sure how C++ does this kind of thing internally. – luiscubal Oct 18 '11 at 15:16
  • in one you are using list and in another one vector, vectors are slower if you are inserting new elements unless you assign a buffer with more less the quantity of the elements. still that shouldn't give you 60 times more time but is a start... – api55 Oct 18 '11 at 15:16
  • 1
    Just a guess: is `size()` an O(1) or an O(n) operation in the collection type you are using? You make O(n^2) calls to it, and if they are each O(n) then that's a total cost of O(n^3), which is rather a lot. However, instead of soliciting random guesses off the internet, why not answer the question yourself by running the code through a profiler and see what it says? – Eric Lippert Oct 18 '11 at 15:19
  • Do you really think anyone can analyze that code snippet without knowing what type all the variables involved are? – PlasmaHH Oct 18 '11 at 15:20
  • I'm not sure if you're checking for the nul terminator (i.e. a character with value `0`), a newline (i.e. character `\n`), or if you really meant the character 'n'. Using `StreamReader` in C#, you're not going to encounter the nul terminator or the newline character, so your `while` loop will never terminate (unless, of course, there really is an `'n'` character on the line). Also, in C# you can write `if (str[count] == 'n')`. No need to convert to string. – Jim Mischel Oct 18 '11 at 15:20
  • @luiscubal in the C# version I was less experienced - it's older code, but I just changed it and it makes no significant difference. – Dollarslice Oct 18 '11 at 15:21
  • @SirYakalot: Have you copied the exact code? I am wondering about the checks with character `'n'` vs. `'\n'` and the like... It does not make sense to have that big of a difference, and that might indicate to some error in the code. There seems to be some things that could be due to the copying to the question... but then again, they might not (have you copied and pasted the *exact* code?) – David Rodríguez - dribeas Oct 18 '11 at 15:21
  • @JimMischel i'm literally checking for the characters "/n", and it does terminate. – Dollarslice Oct 18 '11 at 15:23
  • @EricLippert: In general `size()` in C++ is recommended to be implemented as a O(1) operation in all containers, and mandated to be O(1) for `std::vector` in particular. – David Rodríguez - dribeas Oct 18 '11 at 15:23
  • @DavidRodríguez-dribeas my mistake, example line ends in an 'n' not '/n' – Dollarslice Oct 18 '11 at 15:24
  • 1
    @SirYakalot: Would you mind posting full code for both projects? I'd like to take a look at this in full context. Not here of course, but at some file hosting site, and just give me the link. Not promising any results though. – Benjamin Lindley Oct 18 '11 at 15:41
  • I think this question should be moved to http://codereview.stackexchange.com/ – Klaim Oct 18 '11 at 16:03
  • @BenjaminLindley no of course not. There is actually already a link to the old C# project I did at uni here: http://dl.dropbox.com/u/13519335/AdaptiveAI.zip and the C++ one is here: http://dl.dropbox.com/u/13519335/D3D10DEMO_0.8.zip it does in fact run eventually, but the function populatePathVectors() takes about 10 minutes! Also, the rest after that is not implemented yet. I'm just converting it atm so the C++ can be optimised HUGELY! but I just want to get it working first (although obviously this one function really does need to be sped up.) – Dollarslice Oct 19 '11 at 08:36
  • @BenjaminLindley p.s. the function in question is inside level.cs/level.cpp – Dollarslice Oct 19 '11 at 09:02
  • @Klaim sorry if this is in the wrong place, I was unaware of that site. – Dollarslice Oct 19 '11 at 09:04
  • @SirYakalot It's not really the bad place, I just think having a full code review for this code, done by experimented C++ coders, would help you understand what's wrong. I guess the current answers does just that. – Klaim Oct 19 '11 at 09:39
  • I couldn't get either your programs to work, so I had to isolate the functions, and I didn't get anywhere near the slowdown you mention. The C++ version was a little bit slower than the C#, but by less than a factor of 2. Once I replaced the stringstream conversion to use [stoi](http://en.cppreference.com/w/cpp/string/basic_string/stol) instead, that improved performance by a factor of about 5. – Benjamin Lindley Oct 19 '11 at 17:30
  • Oh right. So do you think it's some setting somewhere? Did you use the path table or did you make your own? – Dollarslice Oct 20 '11 at 12:15
  • @SirYakalot What compiler (version too) are you using? – Tom Kerr Nov 11 '11 at 22:08
  • @TomKerr vs2010. But like I said it runs fast on it's own, so it can't be the compiler. – Dollarslice Nov 12 '11 at 16:24
  • The function itself is quick, but it is slow inside the program. Did you by chances run out the memory? Is you program swapping to disk? – Alessandro Teruzzi Nov 16 '11 at 18:00

9 Answers9

9

I profiled the code with Very Sleepy (Visual C++ 2010, 32-bit Windows XP). I don't know how similar my input data was, but here are the results anyway:

39% of the time was spent in basic_istream::operator>>

12% basic_iostream::basic_iostream

9% operator+

8% _Mutex::Mutex

5% getline

5% basic_stringbuf::_Init

4% locale::_Locimp::_Addfac

4% vector::reserve

4% basic_string::assign

3% operator delete

2% basic_Streambuf::basic_streambuf

1% Wcsxfrm

5% other functions

Some of the stuff seems to be from inlined calls so it's a bit difficult to say where it actually comes from. But you can still get the idea. The only thing that should do I/O here is getline and that takes only 5%. The rest is overhead from stream and string operations. C++ streams are slow as hell.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Timo
  • 5,125
  • 3
  • 23
  • 29
  • this is amazing, thank you so much. so what exactly does basic_istream::operator>> mean? which lines does that refer to? Everything that parses the file? – Dollarslice Oct 19 '11 at 08:41
  • 12
    @SirYakalot: At least the first two calls in the list come from the line `stringstream(tempString) >> result;`. You construct the stream object and then use operator>> to read data. If you want much faster string to int conversion, use the C functions `atoi`, `atol` instead, or some external library optimized for that purpose like StrTk. – Timo Oct 19 '11 at 10:22
  • 3
    C++ streams aren't slow. It's Dinkumware's implementation (the one that's shipped with VS) that is slow. Writing an integer to a stream involves **five locks**, four of them are *global*. None of them is mandated by the standard. – Yakov Galka Nov 11 '11 at 21:30
7

The whileloop in your code seems to be very messy and long, as it is doing things in a way which is not needed:

A simple and fast equivalent code would be this:

int result;
stringstream ss(line);
while ( ss >> result ) //reads all ints untill it encounters non-int
{
    pathLookupVectors[i][j].push_back(result);
}

In C++, such loop is idiomatic as well. Or instead of this manual loop, you could write use std::copy 1:

std::copy(std::istream_iterator<int>( ss ), 
          std::istream_iterator<int>(), 
          std::back_inserter(pathLookupVectors[i][j]));

1. It is taken from @David's comment.

Or even better if you do this, when you push_back the vector itself:

 if (getline(myFile, line)) //enter if a line is read successfully
 {
   stringstream ss(line);
   std::istream_iterator<int> begin(ss), end;
   pathLookupVectors[i].push_back(vector<int>(begin, end));
 }

Done!

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • ah but this would mean each element would contain for example - 0 2 4 8 n. I want each element to simply be 0 then the next contain 2 then 4 and so on. – Dollarslice Oct 18 '11 at 15:26
  • @SirYakalot: What are you trying to say? This code first reads `0` and then `2` , then `4`, then `8` and then exits from the loop when it encounters `n`. – Nawaz Oct 18 '11 at 15:29
  • Alternatively `std::copy( std::istream_iterator( ss ), std::istream_iterator(), std::back_inserter( pathLookupVectors[i][j] ) );` for a one-liner – David Rodríguez - dribeas Oct 18 '11 at 15:34
  • @DavidRodríguez-dribeas: That is even better. Let me add this to my answer. – Nawaz Oct 18 '11 at 15:36
  • Your code is trying to split the string into space-separated numbers. Don't do that: C++ streams, slow though they are, are faster than your code. What you do with each number is up to you. –  Oct 18 '11 at 15:37
  • @DavidRodríguez-dribeas: Or use vector's constructor itself (like I did in the updated answer), right? – Nawaz Oct 18 '11 at 15:42
  • takes about 5 or 6 minutes now. better, but still not quite there! What on earth is taking so much time? – Dollarslice Oct 19 '11 at 14:17
  • @SirYakalot: Can you send me both program so that I can analyse myself,and then get back to you with the results, telling also if there is any problem with your code? I want to see why would it take such much time. I feel like you are still doing something really terrible. – Nawaz Oct 19 '11 at 14:31
  • 1
    @SirYakalot: If you are running debug C++ iterators will be slow due to bounds and overflow checking etc. In release with optimization any of Nawaz' options above should be like lightning. – AJG85 Nov 11 '11 at 16:59
  • @AJG85 sure but it's not slow on its own, it's only slow within the confines of my program, but it definitely IS this block of code slowing it down. I just don't know why it would behave differently in two different programs. – Dollarslice Nov 12 '11 at 16:27
7

Based on your update it is pretty clear that the function you posted by itself is not causing the performance problem, so while there are many ways in which you can optimize it it seems that is not going to help.

I presume you can reproduce this performance problem every time you run your code, correct? Then I would like to suggest that you do the following tests:

  • if you are compiling your program in debug mode (i.e. no optimizations), then recompile for release (full optimizations, favoring speed) and see if that makes a difference.

  • To check if the extra time is spent on this suspected function you can add printf statements at the start and end of the function that include timestamps. If this is not a console app but a GUI app and printfs are not going anywhere, then write to a log file. If you are on Windows, you can alternatively use OutputDebugString and use a debugger to capture the printfs. If you are on Linux, you can write to the system log using syslog.

  • Use a source code profiler to determine where is all that time spent. If the difference between calling this function or not is several minutes, then a profiler will surely give a clue as to what is happening. If you are on Windows, then Very Sleepy is a good choice, and if you are on Linux you can use OProfile.

Update: So you say that a release build is fast. That likely means that the library functions that you use in this function have slow debug implementations. The STL is know to be that way.

I'm sure you need to debug other parts of your application and you don't want to wait all those minutes for this function to complete in debug mode. The solution to this problem is to build your project in release mode, but change the release configuration in the following way:

  • disable optimizations only for the files you want to debug (make sure optimizations remain enabled at least for the file that has the slow function). To disable optimizations on a file, select the file in the Solution Explorer, right click, select Properties, then go to Configuration Properties|C/C++/Optimization. Look at how all the items in that page are set for the Debug build, and copy all of those in your Release build. Repeat for all the files that you want to be available to the debugger.

  • enable debugging info (the pdb file) to be generated. To do this, select the Project at the top of the Solution Explorer, right click, select Properties. Then go to Configuration Properties|Linker|Debugging and copy all the settings from the Debug build into the Release build.

With the above changes you will be able to debug the parts of the release binary that were configured as above just like you do it in the debug build.

Once you are done debugging you will need to reset all those settings back, of course.

I hope this helps.

Miguel Grinberg
  • 65,299
  • 14
  • 133
  • 152
  • 1
    recompiling for release makes it fast, the only problem is (and I know this sounds obvious) but doesn't that mean I'm not debugging it? I need it to be fast enough that I don't have to wait 5 minutes every time I make a change to the code and want to see the results. Obviously this is not always possible, but it IS fast in a project of it's own, so I just don't know why it's not fast here too. – Dollarslice Nov 14 '11 at 18:45
  • Then that's the answer to your question. You must be using some library functions that are slow in the debug version. The debug STL is particularly slow because it adds a lot of checks and assertions. See the edit in my answer for how to solve your problem. – Miguel Grinberg Nov 16 '11 at 07:41
  • So this turns the release configuration into a configuration that debugs certain parts? – Dollarslice Nov 16 '11 at 09:19
  • and also, sorry to be such a noob, but how exactly do I make these changes? I'm not sure where to look! – Dollarslice Nov 16 '11 at 09:24
  • Are you using visual studio on windows? If so check out these links:http://msdn2.microsoft.com/en-US/library/aa985965(VS.80).aspx and http://msdn2.microsoft.com/en-US/library/aa985982(VS.80).aspx the second may be the one you are after. – morechilli Nov 16 '11 at 12:32
  • 2
    I too think Miguel is on to something. I myself have often noticed that a break point (especially conditional break points) in the wrong place of code can make it **extremely** slow. Check so you don't have any breakpoints anywhere, but given that you seem to be unexperienced in Visual Studio I'd guess you don't have too many break points? – DaedalusAlpha Nov 16 '11 at 16:19
  • @SirYakalot: I have added a bit more detail into what you need to do to configure a Release project for the debugger. Good luck! – Miguel Grinberg Nov 16 '11 at 23:54
4

I'm not exactly sure what is going on here, but I see a few ways in which you can optimize your code. If this doesn't get you there, then there might be something else going on.


How big are your strings? As you are passing them in your C++ version, you are making copies because you are "passing by value". Try passing it by constant reference:

void Level::PopulatePathVectors(const string &pathTable)

This passes the object by reference, meaning it is not making a copy. Then, it is customary to make it const to ensure that it is not getting modified in your function.


Use .append or += to extend tempString. I believe you are making a new string object, then replacing the old one with just +, while += and .append are going to modify the current one in place:

tempString.append(line[count]);

You can also tweak out a bit more performance by declaring your variables at the top and then reassigning into them. This will prevent them from getting recreated every time. For example, put string line; before your for-loop, because it's going to get overwritten anyways.

There are a few places you can do this, such as with tempString.

Donald Miner
  • 38,889
  • 8
  • 95
  • 118
4

Here are a few things that I haven't seen anyone else mention. They are somewhat vague, but being unable to reproduce things makes it hard to go into specifics on all of it.

Poor man's profiling.

While the code is running, just keep interrupting it. Usually you'll see the same stack frame over and over.

Start commenting stuff out. If you comment out your splitting and it completes instantly, then its pretty clear where to start.

Some of the code is dependent, but you could read the full file into memory then do the parsing to create an obvious separation on where its spending its time. If both finish quickly independently, then it's probably interaction.

Buffering.

I don't seen any buffering on your reads. This becomes especially important if you are writing anything to disk. The arm on your disk will jump back and forth between your read location, then write location, etc.

While it doesn't look like you are writing here, your main program may have more memory being used. It is possible that after you reach your high water, the OS starts paging some of the memory to disk. You'll thrash when you are reading line by line while the paging is happening.

Usually, I'll set up a simple iterator interface to verify everything is working. Then write a decorator around it to read 500 lines at a time. The standard streams have some buffering options built in as well, and those may be better to use. I'm going to guess that their buffering defaults are pretty conservative.

Reserve.

std::vector::push_back works best when you also use std::vector::reserve. If you can make most of the memory is available before entering a tight loop, you win. You don't even have to know exactly how much, just guess.

You can actually beat std::vector::resize performance with this as well, because std::vector::resize uses alloc and std::vector::push_back will use realloc

That last bit is contested, though I've read otherwise. I have no reason to doubt that I'm wrong, though I will have to do more research to confirm or deny.

Nevertheless, push_back can run faster if you use reserve with it.

String splitting.

I've never seen a C++ iterator solution that was performant when it comes to dealing with gb+ files. I haven't tried that one specifically, though. My guess at why is that they tend to make a lot of small allocations.

Here is a reference with what I usually use.

Split array of chars into two arrays of chars

Advice on std::vector::reserve applies here.

I prefer boost::lexical_cast to stream implementations for maintenance concerns, though I can't say its more or less performant than stream implementations. I will say it is exceedingly rare to actually see correct error checking on stream usage.

STL shenanigans.

I'm intentionally vague on these, sorry. I usually write code that avoids the conditions, though I do remember some of the trials and tribulations that co-workers have told me about. Using STLPort avoids a good chunk of these entirely.

On some platforms, using stream operations have some weird thread safety enabled by default. So I've seen minor std::cout usage absolutely destroy an algorithm's performance. You don't have anything here, but if you had logging going on in another thread that could pose problems. I see a 8% _Mutex::Mutex in another comment, which may speak to its existence.

It's plausible that a degenerate STL implementation could even have the above issue with the lexical parsing stream operations.

There are odd performance characteristics on some of the containers. I don't I ever had problems with vector, but I really have no idea what istream_iterator uses internally. In the past, I've traced through an misbehaving algorithm to find a std::list::size call doing full traversal of the list with GCC, for instance. I don't know if newer versions are less inane.

The usual stupid SECURE_CRT stupidity should stupidly be taken care of. I wonder if this is what microsoft thinks we want to spend our time doing?

Community
  • 1
  • 1
Tom Kerr
  • 10,444
  • 2
  • 30
  • 46
  • (1) `realloc` cannot be (usefully) used on arrays of objects. Ergo, `std::vector` does not use `realloc`. (2) According to http://stackoverflow.com/questions/228908/is-listsize-really-on, GCC is the one with the linear `std::list`. This theoretically makes `std::splice` much faster, which is a major reason to use lists. Also, use `list::empty()` instead. (3) Judging by the other posts, Dinkumware shenanigans it is, due to him using debug builds. (4) STL != C++ Standard Library – Mooing Duck Nov 17 '11 at 00:49
  • "Poor man's profiling" is [Mike Dunlavey's method](http://stackoverflow.com/questions/375913/what-can-i-use-to-profile-c-code-in-linux/378024#378024). – Peter Mortensen Sep 05 '15 at 09:44
3

Both List.Add and vector::push_back reallocate memory from time to time as the container grows. C++ vector stores subvectors by value, so all their data (which seem to be huge in your case) is copied again and again. In contrast, C# list stores sublists by reference, so sublists' data is not copied during reallocation.

Typical vector implementation doubles its capacity during reallocation. So if you have 1 million of lines, subvectors will be copied log(2,1000000) ≈ 10 times.

Move semantics introduced in C++11 is supposed to eliminate this effect. Until that, try vector< shared_ptr< vector<int> > >, list< vector<int> >, or, if you know future size in advance, use vector::reserve() to avoid reallocations.

hamstergene
  • 24,039
  • 5
  • 57
  • 72
  • sorry, i'm a bit of a newbie. How and why would I use both of the lines you have suggested? Thanks in advance! – Dollarslice Oct 18 '11 at 15:35
  • This is the main reason why I asked about the compiler/version in a comment. VS's STL (which is licensed from Dinkumware) efficiently grows `std::vector>` by doing some clever metaprogramming tricks: Detect that the contained type has an efficient `swap` operation, allocate a new buffer of empty vectors (rather than copying), and use `swap` to move the vectors in the original buffer to the new buffer. `std::vector<>::swap` usually takes 3 pointer swaps (9 assembly instructions -- again, depending on the STL and defines it might take more, specially in DEBUG) which efficient – David Rodríguez - dribeas Oct 18 '11 at 15:40
  • @SirYakalot the quickest would be to replace `pathLookupVectors` type with list of list of vectors, or deque of deque of vectors. It's hard to suggest more without seeing the rest of the code and how it is used. – hamstergene Oct 18 '11 at 15:42
1

Haven't tested the code but how many ints does it typically load? Consider what happens when each of your vectors reaches its capacity. A vector grows inefficiently - O(n) I believe. C#'s List doesn't have this behaviour.

Consider using std::deque, std::list or some other container that has better growth behaviour. See this article for more info.

Ilian
  • 5,113
  • 1
  • 32
  • 41
  • there are 744 vectors in the main vector, each containing another 744 vectors of ints. so... about half a million ints. – Dollarslice Oct 18 '11 at 15:28
  • 1
    A `vector` may grow inefficiently, but its amortized big-O complexity is O(1), not O(n). – Robᵩ Oct 18 '11 at 15:32
  • Ouch. You're being hit by the reallocation/growth behaviour. As a quick test to see if this assumption is correct, try replacting all `vector`s with `list`s. – Ilian Oct 18 '11 at 15:34
  • @Rob Doesn't it have to copy each element to the new internal buffer? Making it O(n)? – Ilian Oct 18 '11 at 15:36
  • 1
    Yes, it does, but it doesn't have to do it every time. When you grow a `vector` by `push_back`, it only reallocates occasionally, and then by a large amount. The C++ standard requires amortized O(1) behavior from `push_back()`. – Robᵩ Oct 18 '11 at 15:49
  • I agree that `push_back` is amortized O(1). By grow, I mean what `vector` does when it reallocates its storage and its O(n). I'll back that up with an [article](http://drdobbs.com/184401375). Relevant quote: "Therefore, we can conclude that reallocating a vector with a size of n takes O(n) time". – Ilian Oct 18 '11 at 21:06
  • actually sorry, there are half a million lines but many times that amount of ints. – Dollarslice Oct 19 '11 at 14:58
1

If you have extremely large number of elements, you'll be punished with re-allocation and copy every time vector is pushed-back. Try using a different container in C++.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Daniel Mošmondor
  • 19,718
  • 12
  • 58
  • 99
1

Since your function itself is not slow1, the reason for the program being slow must be that some code that uses the product of this function becomes slower when pathLookupVectors has been populated.

I think running a profiler on your program would be the best way to find out, but you could also look through your code and find every piece of code that depends on pathLookupVectors and consider if it could be the bottleneck you're searching for.

1. Established in your latest edit.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kleist
  • 7,785
  • 1
  • 26
  • 30
  • This is a really good idea, unfortunately nothing that depends on the lookup vectors is being instantiated or run in this build. Very strange! – Dollarslice Nov 14 '11 at 18:27