我正在做大量的矩阵算术并且想利用 C99 的优势 restrict 指针限定符。

我想将矩阵设置为指向指针的指针,以便轻松添加下标,如下所示:

int **A = malloc (ncols * sizeof(int *));
A[0] = malloc (nrows * ncols * sizof(int));
for (int i=1; i < ncols; i++) {
    A[i] = A[0] + i*nrows;
}

现在,对于矩阵乘法函数

void mmultiply ( int nrows, int ncols, int **Out, int **A, int **B);

我必须将参数的两个指针限定为受限的吗?这是有效的语法,但我很难确定是否 int *restrict *restrict 行为与任何不同 int **restrict.

然后,在适当限制指针的情况下,通过以下方式访问元素 A[0][col*nrows + row] 不明确的?(即,编译器会假设我 仅有的 通过访问矩阵 A[col][row] 对于值 row 这样 row < nrow)?或者我必须保持一致?

有帮助吗?

解决方案

对于第一个问题,“是”,就意味着不同的东西,如果你同时使用restrict预选赛,具体而言,指针也不会被混淆。至于是否有什么差别:理论上是,在实践中,这取决于优化

有关的第二个问题,“是”,它会假设任何通过一个行指针访问仅通过行指针访问。

您可以在那里太丢const

最后,如果这是gcc在-O2,-O3,或-OS,编译器已经做基于类型的别名分析。我相信其他的编译器这样做也。这意味着,限制指针VS的整数已经了解了,只留下那些可能存储到对方的阵列。

在总和,优化器将假设指针没有被存储到作为整数,并且它知道它是没有做任何指针在循环过程中写入。

所以,你可能只用一个限制得到相同的代码。

其他提示

外(第二)限制告诉编译器没有指针的阵列(A,B,和出)别名。内(第一)限制告诉没有整数的阵列的别名(由指针的数组的元素指向的)编译器。

如果您访问两个A [0] [COL * NROWS +排]和A [山口] [行]那么你就违反了内部制约,这样的事情可能打破。

int **restrict 仅断言 Out、A 和 B 寻址的内存不重叠(除非 A 和 B 可以重叠,假设您的函数不修改它们中的任何一个)。这意味着指针数组。它不会断言 Out、A 和 B 所指向的内存内容的任何内容。n1124 中的脚注 117 说:

如果标识符P具有类型(INT **限制),则指针表达式P和P+1基于P指定的限制指针对象,但是指针表达式 *P和P [1]不是。

类推 const, ,我怀疑符合资格 restrict times 将断言您想要的内容,即数组中没有任何值指向重叠内存。但阅读该标准后,我无法向自己证明它确实如此。我认为“让 D 是一个普通标识符的声明,它提供了一种将对象 P 指定为类型 T 的限制限定指针的方法”确实意味着 int *restrict *restrict A, ,则 A[0] 和 A[1] 是指定为 int 的限制限定指针的对象。但这是相当沉重的法律术语。

请注意,我不知道您的编译器是否真的会利用这些知识做任何事情。显然可以,只是执行与否的问题。

所以我真的不知道你从传统的 C 二维数组中获得了什么,你只需分配 rows * cols * sizeof(int), ,并索引 A[cols*row + col]. 。那么你显然只需要一次 limit 的使用,并且任何使用它做任何事情的编译器 restrict 将能够跨对 Out 的写入重新排序从 A 和 B 的读取。没有 restrict, ,当然,它不能,所以通过做你正在做的事情,你将自己置于编译器的怜悯之下。如果它不能处理双重限制,只能处理单一限制的情况,那么您的双重间接会导致您的优化付出代价。

乍一看,无论如何,乘法可能比附加的指针间接更快。您显然关心性能,否则您根本不会使用限制,因此在进行此更改之前,我会相当仔细地测试性能(在您关心的所有编译器上),以便稍微更好的语法,而不必记住有多少每次访问数组时,数组中都会有列。

通过 A[0][col*nrows + row] 访问元素是否未定义?

是的,如果元素被其中一次访问修改,因为这使得 A[0] 成为也通过 A[col] 访问的内存的别名。如果只有 A 和 B 是限制限定指针,那就没问题,但如果 A[0] 和 A[col] 是限制限定指针,那就不行了。

我假设您没有修改此函数中的 A,所以实际上该别名没问题。但是,如果您对 Out 执行相同的操作,则行为将是不确定的。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top