/* M is the number of rows; N is the number of columns. */
void matrix_shift(int M, int N, int A[M][N]) {
size_t rowbytes = N * sizeof(int);
int temprow[N];
memcpy(temprow, A, rowbytes); // store first row
memmove(A, A + 1, (M-1) * rowbytes); // shift up
memcpy(A + (M-1), temprow, rowbytes); // replace last row
}
这样做很简单,并且依赖于在任何常见平台上都应高度优化的例程。有一个额外的行被复制,但在正方形矩阵的情况下,这是一个小的低效率。
我刚刚看到了你有关4x4矩阵的评论。在现代x86 CPU上,一个大小为4x4的int
数组可以适配于单个缓存行(缓存行大小为64B)。在这种情况下,您需要编译器生成类似以下的代码:
## matrix address in [rdi]
movups xmm0, [rdi]
movups xmm1, [rdi+16]
movups xmm2, [rdi+32]
movups xmm3, [rdi+48]
movups [rdi], xmm1 ; doing all the stores after all the loads avoids any possible false dependency
movups [rdi+16], xmm2
movups [rdi+32], xmm3
movups [rdi+48], xmm0
vmovups ymm
加载,一个vmovups ymm
存储,一个vmovups xmm
存储(到末尾),以及一个vextractf128
存储(到开头)。4
的调用者中时,John的memcpy将优化为以下内容。如果您在矩阵末尾留出另一行的空间,您可以将第一行复制到此额外空间,并传递指向原来第二行的指针。
这使您可以暂时以不同的方式查看矩阵,但这不是可重复的过程。
如果您有一个大缓冲区,您可以继续通过这种方式旋转矩阵行,直到到达保留空间的末尾并必须将数组复制回缓冲区的顶部。这最小化了复制开销,但确实意味着您正在触及一些新的内存。
int *rows[M]
只需要一个&A[i][0]
的循环,所以这只是数学问题,而不是多次加载或分配。rows[i]
,然后使用j
索引。
temprow
在编译时能够正常大小,如果您需要多次执行此操作,则可能会有所帮助(但是否应该多次执行此操作是另一回事!)。 - John Zwinckmov
指令的memcpy/memmove,不像gcc 5.3版本(其中仍然调用memmove)。 - Peter Cordes