From 9068c75f2a256bafa2f8d1f70cee8a55cca24cfd Mon Sep 17 00:00:00 2001 From: Michael McLeod Date: Mon, 2 Mar 2026 11:49:54 +0000 Subject: [PATCH 1/3] Add note on higher dimensional arrays --- 07performance/DataStructures.md | 50 ++++++++++++++++++++++++++++++ 07performance/img/BasicVector.png | Bin 0 -> 125621 bytes 07performance/img/VecOfVec.png | Bin 0 -> 156940 bytes 3 files changed, 50 insertions(+) create mode 100755 07performance/img/BasicVector.png create mode 100755 07performance/img/VecOfVec.png diff --git a/07performance/DataStructures.md b/07performance/DataStructures.md index 919bfbaf4..14b4deb3b 100644 --- a/07performance/DataStructures.md +++ b/07performance/DataStructures.md @@ -28,6 +28,56 @@ There is a corresponded operation for removing an element from the end of the li We also have `insert` and `erase` for inserting and removing elements. Removing elements will not require any reallocations, but it does require shifting any data to the right of the elements being deleted. Removing an element is therefore, on average, $O(n)$. Inserting an element works similarly, as it needs to shift any data to the right of the location being inserted into; an additional factor is that like `push_back` it may trigger a reallocation. Insertion is on average $O(n)$. +### Multi-Dimensional Arrays and Column Major vs Row Major ordering + +Mathematical objects such as matrices and tensors can be represented as multi-dimensional arrays. Multi-dimensional arrays can themselves be organised in different ways. + +#### Multi-Dimesional Arrays as Arrays of Arrays + +One potential simple implementation of a 2D, $N \times M$ array (matrix) is: + +``` +std::vector Matrix(N, std::vector(M, 0)); +``` + +where the matix has been initialised to all zeroes. + +where $N$ is the number of rows and $M$ is the number of columns. In this implementation the _rows_ are contiguous in memory, since each row is stored as a vector of size $M$ (the number of columns). This is called **row major ordering**. If rows are contiguous, it means that columns necessarily cannot be contiguous in memory in this representation, and must be separated in heap memory by an arbitrary amount that is at least the size of a row. If you store columns contiguously instead of rows, then you have **column major ordering**. + +Let's review how a vector looks in memory to understand how the memory is laid out. Remember that a vector stored on the stack makes a heap allocation to store its data, which means that under the hood a vector is using a pointer to keep track of the location of the actual data. Below is an example of a vector of ints (green) stored on the stack (blue), with an allocation (yellow) on the heap (red). + +![image](img/BasicVector.png) + +When we have a vector of vectors, each element of the vector's data is itself a vector, which it pointing to a separate location in memory to store its own data. Below is a diagram of a $5 \times 4$ matrix (5 rows, 4 columns); the vector of vectors point to an allocation on the heap containing 5 vectors (one for each row), which themselves each points to a block of memory 4 ints wide. These blocks of memory can in principle be placed anywhere in memory. + +![image](img/VecOfVec.png) + +Each row is clearly contigous but the columns are not. For example, the first column of this matrix would be made up of the first element of each row, which are placed all over memory. + +The result of using a C-style 2D array (`int** Matrix`) is the same in terms of memory layout. + +#### Multi-Dimesional Arrays in a Contiguous Block + +Instead of having all our rows (or columns) placed in independent locations in memory, we can also allocate a contiguous $N \times M$ block of memory to store all of the data that we need, like this: + +``` +std::vector Matrix(N*M, 0); +``` + +where again we have initialised the matrix to all zeroes. + +This represents a matrix as a single "flat" array, so its memory layout is just the same as a regular 1D array. You will find that **this is the more common approach in performant applications**; this layout reduces memory fragmentation, makes it easier to transfer matrix data as a single contiguous buffer, and allows us to get improved cache performance out of algorithms that iterate over all elements. + +The matrix must now be stored in one contiguous block, either row by row (row-major) or column by column (column major). The trick to using this kind of structure is to convert between 2D indices $(i,j)$ (where $0 \le i \lt N$ and $0 \le j \lt M$) to a single index $k$ (where $0 \le k \lt N\times M$). **N.B.** here we will use the matrix convention where indices $(i,j)$ refer to row $i$ and column $j$. + +To understand how to do this, let's consider a row-major matrix. To find the element $(i,j)$, we are trying to find element $j$ of row $i$; this means that the we just need to find the start of row $i$ and then add $j$. Since the matrix is row-major, the start of row $i$ is at index $M \times i$ (the number of rows before it times the length of a row, which is the number of columns). Therefore our formula for **row major** indices is: + +$k_\text{row-major} = M \times i + j$, + +and likewise for indices in **column major** matrices: + +$k_\text{col-major} = N \times j + i$. + ## Linked Lists A _linked list_ is a representation of a list that is stored like a graph: each element of the list consists of its data and a pointer to the next element of the list. A linked list has no guarantees of being stored contiguously, so the only way to navigate the linked list is to follow the pointers from one node to the next; this is in contrast to random access arrays. A common extension of the linked list is the doubly-linked list, which has pointers to the next _and_ previous element in a list. diff --git a/07performance/img/BasicVector.png b/07performance/img/BasicVector.png new file mode 100755 index 0000000000000000000000000000000000000000..4f706126546333c533a623facc046d644c99378f GIT binary patch literal 125621 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 literal 0 HcmV?d00001 diff --git a/07performance/img/VecOfVec.png b/07performance/img/VecOfVec.png new file mode 100755 index 0000000000000000000000000000000000000000..2db261d1313602b22083ea5f6ffdb24540e1013b GIT binary patch literal 156940 zcmeHQ2|SeB`5=9H1SMS5~4OuybKNhyGaTfHBUK@hQ|7myxn_!`Wb6 z>7~)`BoYXCBF_2qgVqEmC#*HSedr}(TwI_=p|&agkSzgE|2p~sXYk>lT>%|1{-R2u zqoz0;qCM?eB^gE9yFkZvu{b+>#*?KLlxb%iF^r4pU*c+y0T`j(|3$0N>XA{KE(8K7 zZ&ag=KFzquala-O4^=j!9_h~ku=)0TnYH1<1o=NHj$m(~ZHMzv!09O%ZYD@?+iz^6 z#DJGG=F>Cik98#;U_cnuk~7pGM9d!OkcKM}<3gmv0_szsorxHn6BXXl)P2wqon3L% ztF@?C*yHdvTQCO*Zcv+0%VF$kfKHFsB;X0O&LL~7WR119rar}m;D8-=g2D2uc3Ea#w{3n zcd}y^4H+CbkOSAy5j*zlLl&6@*lf{6w|=A1#P+(cKnw+SNez2~3(gZl79KPX)aEZ{MYMZIKpc)wa;1G1V3sIBcf}AIFqH-##eDh z$0Hk425u$dFnAOgo-j^!RI6rBbcB!wnE?-b91&~mjIoCLyayOW!7=(^Hp(=Bb{b`% zJ|T+$+yYxXby%^-+1Ox#fAE=+ql4qy07!)r)ppP4$Ms;KBHV>(0`{WW>$*VXh~A#AgIrPzyru zfKR2ya3;}mdb%2c^v?omBsyd<&dgo7zO*#mC?J^rSul;nhfEN&Y{14eoyR~h z{gYr?7HUZ7U626r?*@p{p!%R1L@@ocU>b=KS7_`;urFJzJ}h@Jk2A)+)~ z0uj+#{2R3vKdr<^8ZOPp04O59_@wh>j$AAoe=miMIJtk@$%V`8At09F3=e`N*jP*g za~HOg3zynMFwG%MBMCMph*>sZV;U~EhhUmRn3jfG63MW!3*KOZ8ZNtsfSOaFMpA4n z2Aa7G7t{>1@YS?20%{I{8j)eJMu*=HYPkF!0%}fy8i^B`C}!D!ZK>fBd93O&d4&@`p8ob9w2kY4IUtO_Cmd^ZtmEuD%%^^%9VhmQFgV|T%Iv=udDLw?$ zoB}nHV`HM2Wdk;-;Zl4Es5t~`M2zvzEj3(<4*@l&K#hnoSPV3C7q+E_OYtF~<`k%r zK#_@JmJQgThD-4wpym*$5i!O;cR%1#dezJ6gvOylA#$eOy znKUl!)s+!72CKsh*LpH?I$ve^5D;_BB|v6ev!a=1Bw%A2F3E>rnnRdIl58x)9CH^o zrr~mY2&OrN=`lT0L}CQz_ao)ta(oD=IR$DY$;LFzEE}*bHC&Dl0X2s}ji@o$`_lm6 zut5!%<3m8rDNr*acO>`0if-7ThRg9Gpym{)kwB5f>tOD}1~puc4*@lYK#iy|SPk^I zgBmW!hk%+>phf~kCW=`$U|VX0KSY+s09+5NBaguou`W&+B36Uo=49haV}rgr1oCiMJ_N)Z z@^e`SXFDvx5leJA0FBBB$p?qzlZQ+4A(-Y6rjaBYOT@<9g>9$da(oD;IfQ9Mjlt@3 zd^@P&a(oD=IR)ym!Ysu5faQI_<@gX#a|qOk8snc^YPcL90%}fy8i^B`{szkiY@Zq~ z$A^HLQ=moyMHVbGcVUAXF2{#}nnR#Q)ENKR{a{2TU!60EfSOaFW;6*xy|AJiuBBE$ z_(MdE@xP|VP=L!PB5I6(u^MB{j0)tka$b$00GIS*AQVw!AZm;+hi(AfQ9sTycE
JOzUz~%T5P;&~@ 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 Date: Mon, 2 Mar 2026 11:56:09 +0000 Subject: [PATCH 2/3] Improve wording --- 07performance/DataStructures.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/07performance/DataStructures.md b/07performance/DataStructures.md index 14b4deb3b..fe9ca6237 100644 --- a/07performance/DataStructures.md +++ b/07performance/DataStructures.md @@ -44,7 +44,7 @@ where the matix has been initialised to all zeroes. where $N$ is the number of rows and $M$ is the number of columns. In this implementation the _rows_ are contiguous in memory, since each row is stored as a vector of size $M$ (the number of columns). This is called **row major ordering**. If rows are contiguous, it means that columns necessarily cannot be contiguous in memory in this representation, and must be separated in heap memory by an arbitrary amount that is at least the size of a row. If you store columns contiguously instead of rows, then you have **column major ordering**. -Let's review how a vector looks in memory to understand how the memory is laid out. Remember that a vector stored on the stack makes a heap allocation to store its data, which means that under the hood a vector is using a pointer to keep track of the location of the actual data. Below is an example of a vector of ints (green) stored on the stack (blue), with an allocation (yellow) on the heap (red). +Let's review how a vector is arranged in memory to understand the layout of our vector of vectors. Remember that a vector stored on the stack makes a heap allocation to store its data, which means that under the hood a vector is using a pointer to keep track of the location of the actual data. Below is an example of a vector of ints (green) stored on the stack (blue), with an allocation (yellow) on the heap (red). ![image](img/BasicVector.png) From 350c07e0bb06a52bfdabe4403fea762cecbaf681 Mon Sep 17 00:00:00 2001 From: Michael McLeod Date: Mon, 2 Mar 2026 11:57:58 +0000 Subject: [PATCH 3/3] Clarification on int** --- 07performance/DataStructures.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/07performance/DataStructures.md b/07performance/DataStructures.md index fe9ca6237..d31cd038d 100644 --- a/07performance/DataStructures.md +++ b/07performance/DataStructures.md @@ -48,13 +48,13 @@ Let's review how a vector is arranged in memory to understand the layout of our ![image](img/BasicVector.png) -When we have a vector of vectors, each element of the vector's data is itself a vector, which it pointing to a separate location in memory to store its own data. Below is a diagram of a $5 \times 4$ matrix (5 rows, 4 columns); the vector of vectors point to an allocation on the heap containing 5 vectors (one for each row), which themselves each points to a block of memory 4 ints wide. These blocks of memory can in principle be placed anywhere in memory. +When we have a vector of vectors, each element of the vector's data is itself a vector, which is pointing to a separate location in memory to store its own data. Below is a diagram of a $5 \times 4$ matrix (5 rows, 4 columns); the vector of vectors point to an allocation on the heap containing 5 vectors (one for each row), which themselves each points to a block of memory 4 ints wide. These blocks of memory can in principle be placed anywhere in memory. ![image](img/VecOfVec.png) -Each row is clearly contigous but the columns are not. For example, the first column of this matrix would be made up of the first element of each row, which are placed all over memory. +Each row is clearly contigous but the columns are not. For example, the first column of this matrix would be made up of the first element of each row, which are placed independently throughout memory. -The result of using a C-style 2D array (`int** Matrix`) is the same in terms of memory layout. +The result of using a C-style 2D array (`int** Matrix`) is the same in terms of memory layout, since a C-style 2D arrays is likewise an array of pointers to arrays. #### Multi-Dimesional Arrays in a Contiguous Block