So in general, cache performance is most of what you should optimize for on things like tile operations so you don't die a death of a million papercuts. See Mike Acton's Data Oriented Programming talks for that (there's tons on youtube, just search for them). This sort of optimization is what will give you huge gains in large repeating operations like those done on tiles.
Memory size is utterly unimportant so long as it doesn't adversely affect cache performance. The typical user has several GB of RAM, and hundreds or thousands of GB to page out to in disk if you really need it. Outside the performance cost of moving that data through levels of caching, memory is free.
The best way to get good cache performance for such things, in general, is incredibly simple. Take the data being computed with at the same time, put it in a plain old 1D array, and rip through that. Lots of random pointers are bad (very bad, in fact), lots of complex data structures is bad. For example:
struct Tile
{
float height;
float temperature;
float humidity;
float grassFlavor;
float renderingSmell;
float radioactivity;
float willComeUpWithNameLater;
float b;
};
If you sent an array of those structures into a function updating the temperature, assuming you had a large number of them, it would be slower than sending in only the data required for the temperature update as it would fill more of the cache quickly. Turning it into something like the following would actually be faster:
vector<float> TileTemperatures;
vector<float> TileRadioactivity;
vector<TileSansTempAndRadioactivity> Tiles;
Or just peeling apart the entire structure into such arrays and sending them as necessary. This will not only make your code faster, it will make it MUCH easier to see what is being used/modified/etc by your program. UpdateTemperature() takes a tile array? Huh, I wonder what that does. UpdateTemperature() takes temperature array and a radioactivity array? I bet that function uses radioactivity to increase the heat in a tile.
( Now coming back to the point about pointers being very bad for this sort of thing. That is actually conditional: If those pointers are to a small chunk of data, that data may stay in the cache and so be reasonably fast. However, if they are just random pointers to places in memory unrelated to one another, that will kill performance. As in, you will get less than 10% of your potential performance. )
Essentially, good code is just a matter of keeping things incredibly simple: fast iterations on flat arrays of data, as little jumping around between things in memory as possible, using only the subset of data you need. Keep things simple for the things you do the most and it will turn out to be optimizing for you. Optimizing simple things is Intel's job, so keep things simple and let them deal with it. Most bad code is the result of trying to be too clever for your own good. Even in cases where things like binary trees would be the 'optimal' data structure, for anything less than around 200 elements, just ripping through a 1D array is faster.
Edit: std::vector is generally fine because it is a relatively lightweight wrapper around flat arrays; additionally, it's a really simple data structure. If you need to maximize performance of it, you can trivially replace it later with your own essentially to-spec implementation. Most of the std:: containers are pretty bad; as such, most people will likely have their own personal versions of the important ones anyway.
And a fun fact about if statements and error checking: If statements are effectively free assuming the code goes down the same branch pretty much every time. Modern CPUs make extremely heavy use of branch prediction, allowing something which repeatedly passes or fails a test to be assumed to continue with that behavior, preventing it from slowing down the pipelining in the CPU and making it nearly free. When it fails to do so, it's called a branch misprediction, and that does incur some cost. However, error detection in particular should never take a different route (unless you have an error, in which case a mere 15 cycles of misprediction performance is the least of your problems). Additionally, the CPU is good enough at prediction that misprediction rate is something like 1% to 18%.
Now keep in mind that I just said 15 cycles. A cache miss? That's 200 cycles. You get one of those any time you access some random pointer which isn't near other data you're interested in. So that optimization I talked about above will speed things up several orders of magnitude more than removing a few if statements checking for out of bounds accesses to arrays.