Good question!
There are two reasons why this is slower on hardware:
- getRows and setRows is implemented in C++. Our compiled TypeScript code is pretty fast, but C++ is just a bit faster
- The images are stored in memory column by column rather than row by row.
To clarify the second point, here’s a more detailed explanation:
You can think of the memory of the device as one long array of numbers. Since it’s a 1d array and images are 2d, we need to “flatten” the image to store it in memory. This is done by storing all of the columns one after another end to end. For a 2x2 image, it looks something like this:
Memory address: 0x0 0x1 0x2 0x3
Pixel: (0,0) (0,1) (1,0) (1,1)
(this isn’t quite accurate because each pixel is actually half a byte, but it’s good enough for this example)
When we want to access a specific pixel, we’d do so like this:
color = memory[x * height + y]
Because all of the pixels in a given column are stored next to each other, reading/writing an entire column is fast because it’s all in one contiguous piece of memory.
Now why does being a contiguous piece of memory matter?
Well, computers have several layers of memory that they use to store data. You’re probably familiar with two of these: the hard drive and RAM. RAM is way faster than the hard drive, so when the computer needs to access something often it will move the section of memory it lives in into RAM. It’s expensive to put something into RAM, but it ends up being way, way, faster in the long run if it’s being accessed multiple times. This strategy works best when things being accessed are next to each other in memory, because then only one chunk of data needs to be moved into RAM at a time.
When you access a lot of memory addresses far away from each other, the computer needs to keep moving more stuff into RAM every time something is missing. That’s why it’s better to access stuff in the order it’s stored.
In reality, most computers have more than just two layers of memory. Each one gets smaller and faster the closer it is to the top.