+7  A: 

I would guess "no"! And if the answer happens to be "yes", then it's almost certainly so irregular that it'll be way slower for a convolution-type operation.

EDIT

To qualify my guess, take an example. Let's say we store a[0][0] first. We want a[k][0] and a[0][k] to be similar distances, and proportional to k, so we might choose to interleave the storage of first row and first column (i.e. a[0][0], a[1][0], a[0][1], a[2][0], a[0][2], etc.) But how do we now do the same for e.g. a[1][0]? All the locations near it in memory are now taken up by stuff that's near a[0][0].

Whilst there are other possibilities than my example, I'd wager that you always end up with this kind of problem.

EDIT

If your data is sparse, then there may be scope to do something clever (re Cubbi's suggestion of R-trees). However, it'll still require irregular access and pointer chasing, so will be significantly slower than straightforward convolution for any given number of points.

Oli Charlesworth
+1  A: 

You could think of your 2D matrix as a big spiral, starting at the center and progressing to the outside. Unwind the spiral, and store the data in that order, and distance between addresses at least vaguely approximates Euclidean distance between the points they represent. While it won't be very exact, I'm pretty sure you can't do a whole lot better either. At the same time, I think even at very best, it's going to be of minimal help to your convolution code.

Jerry Coffin
+2  A: 

This sounds like something that could be helped by an R-tree. or one of its variants. There is nothing like that in the C++ Standard Library, but looks like there is an R-tree in the boost candidate library Boost.Geometry (not a part of boost yet). I'd take a look at that before writing my own.

Cubbi
+1 for linking to R-trees, which led me to Hilbert R-trees, which led me to Hilbert ordering.
Jon Purdy
+5  A: 

Given the requirement that you want to store the values contiguously in memory, I'd strongly suggest you research space-filling curves, especially Hilbert curves.

To give a bit of context, such curves are sometimes used in database indexes to improve the locality of multidimensional range queries (e.g., "find all items with x/y coordinates in this rectangle"), thereby aiming to reduce the number of distinct pages accessed. A bit similar to the R-trees that have been suggested here already.

Either way, it looks that you're bound to an M*N array of values in memory, so the whole question is about how to arrange the values in that array, I figure. (Unless I misunderstood the question.)

So in fact, such orderings would probably still only change the characteristics of distance distribution.. average distance for any two randomly chosen points from the matrix should not change, so I have to agree with Oli there. Potential benefit depends largely on your specific use case, I suppose.

ig2r
I just caught onto the Hilbert curves article. It seems that this may be just the ticket. You're right in that the problem is in the ordering and not, strictly speaking, the data structure. I'm wondering whether converting to and from Hilbert ordering would be too slow, but for my application it's more important that the convolution itself be fast. I will experiment with this.
Jon Purdy
You probably don't need the full Hilbert curve. You can get most of the benefits by bit-interleaving (which can be done by using a lookup table for your coordinates and or'ing the results together). Also, whatever curve you use, note that you can use the ordering to take advantage of the new spatial coherence: walking the array in curve order will make better use of the cache than using the original row order.
comingstorm
Agree with ig2r's edit. For every coordinate that you move to being in the "correct" distance, you've moved another coordinate to being in the wrong coordinate. Whilst I understand the use of space-filling curves for things like data indexing (where you're just looking for high probability of locality), in something like convolution, the access pattern is deterministic. But if you do find an improvement, I'd be interested to hear about it (convolutions are something I do a lot!).
Oli Charlesworth
+2  A: 

It is not possible to "linearize" a 2D structure into an 1D structure and keep the relation of proximity unchanged in both directions. This is one of the fundamental topological properties of the world.

Having that that, it is true that the standard row-wise or column-wise storage order normally used for 2D array representation is not the best one when you need to preserve the proximity (as much as possible). You can get better result by using various discrete approximations of fractal curves (space-filling curves).

Z-order curve is a popular one for this application: http://en.wikipedia.org/wiki/Z-order_(curve)

Keep in mind though that regardless of which approach you use, there will always be elements that violate your distance requirement.

AndreyT
Thanks for the link. I'm aware that it's impossible to fully preserve the proximity relationship between elements, but I'm only looking for improvement in the average case, and for relatively large input.
Jon Purdy
+6  A: 
Matthew Hall
Thanks for the suggestion.
Jon Purdy
First time I hear about bricking, you gotta love this website for this... thanks Matthew!
Matthieu M.
A: 

I think you're forgetting that distance in computer memory is not accessed by a computer cpu operating on foot :) so the distance is pretty much irrelevant.

It's random access memory, so really you have to figure out what operations you need to do, and optimize the accesses for that.

Larry Watanabe
+1  A: 

The answer is no. Think about it - memory is 1D. Your matrix is 2D. You want to squash that extra dimension in - with no loss? It's not going to happen.

What's more important is that once you get a certain distance away, it takes the same time to load into cache. If you have a cache miss, it doesn't matter if it's 100 away or 100000. Fundamentally, you cannot get more contiguous/better performance than a simple array, unless you want to get an LRU for your array.

DeadMG
but the cpu could get tired walking all the way from one memory location to another if it's too far away, and wouldn't that slow it down? jk
Larry Watanabe
A: 

You need to reconvert the addresses from memory space to the original array space to accomplish this. Also, you've stressed distance only, which may still cause you some problems (no direction)

If I have an array of R x C, and two cells at locations [r,c] and [c,r], the distance from some arbitrary point, say [0,0] is identical. And there's no way you're going to make one memory address hold two things, unless you've got one of those fancy new qubit machines.

However, you can take into account that in a row major array of R x C that each row is C * sizeof(yourdata) bytes long. Conversely, you can say that the original coordinates of any memory address within the bounds of the array are

r = (address / C) c = (address % C)

so

r1 = (address1 / C)

r2 = (address2 / C)

c1 = (address1 % C)

c2 = (address2 % C)

dx = r1 - r2

dy = c1 - c2

dist = sqrt(dx^2 + dy^2)

(this is assuming you're using zero based arrays) (crush all this together to make it run more optimally)

For a lot more ideas here, go look for any 2D image manipulation code that uses a calculated value called 'stride', which is basically an indicator that they're jumping back and forth between memory addresses and array addresses

Mark Mullin
A: 

This is not exactly related to closeness but might help. It certainly helps for minimation of disk accesses.

one way to get better "closness" is to tile the image. If your convolution kernel is less than the size of a tile you typical touch at most 4 tiles at worst. You can recursively tile in bigger sections so that localization improves. A Stokes-like (At least I thinks its Stokes) argument (or some calculus of variations ) can show that for rectangles the best (meaning for examination of arbitrary sub rectangles) shape is a smaller rectangle of the same aspect ratio.

Quick intuition - think about a square - if you tile the larger square with smaller squares the fact that a square encloses maximal area for a given perimeter means that square tiles have minimal boarder length. when you transform the large square I think you can show you should the transform the tile the same way. (might also be able to do a simple multivariate differentiation)

The classic example is zooming in on spy satellite data images and convolving it for enhancement. The extra computation to tile is really worth it if you keep the data around and you go back to it.

Its also really worth it for the different compression schemes such as cosine transforms. (That's why when you download an image it frequently comes up as it does in smaller and smaller squares until the final resolution is reached.

There are a lot of books on this area and they are helpful.

pgast