简体   繁体   English

关于指针指针,C99的“限制”语义是什么?

[英]What are the semantics of C99's “restrict” with regards to pointers to pointers?

I am doing lots of matrix arithmetic and would like to take advantage of C99's restrict pointer qualifier. 我正在做很多矩阵运算,并希望利用C99的restrict指针限定符。

I'd like to setup my matrices as pointers to pointers to allow for easy subscripting, like so: 我想设置我的矩阵作为指针的指针,以便轻松下标,如下所示:

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;
}

Now, for a matrix multiplication function 现在,对于矩阵乘法函数

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

must I qualify both pointers of the arguments as restricted? 我必须将参数的两个指针限定为受限制吗? It's valid syntax, but I'm having a hard time determining if int *restrict *restrict behaves any differently than int **restrict . 它是有效的语法,但我很难确定int *restrict *restrict行为是否与int **restrict不同。

Then, with the pointers properly restricted, is accessing elements through A[0][col*nrows + row] undefined? 然后,在指针被正确限制的情况下,通过A[0][col*nrows + row]未定义访问元素? (ie, will the compiler assume that I only access the matrix through A[col][row] for values of row such that row < nrow )? (即,将编译器假设我通过访问矩阵A[col][row]为的值row ,使得row < nrow )? Or must I simply remain consistent? 或者我必须保持一致吗?

For the first question, "yes", it will mean something different if you use both restrict qualifiers, specifically, that the pointers also won't be aliased. 对于第一个问题,“是”,如果你同时使用两个restrict限定符,它将意味着不同的东西,特别是指针也不会有别名。 As to whether it makes any difference: theoretically yes, in practice, it depends on the optimizer. 至于它是否有任何区别:理论上是,在实践中,它取决于优化器。

For the second question, "yes", it will assume that anything accessed through a row pointer is only accessed through the row pointer. 对于第二个问题,“是”,它将假定通过行指针访问的任何内容只能通过行指针访问。

You could throw const in there too. 你也可以把const扔在那里。

Finally, if this is gcc at -O2, -O3, or -Os, the compiler is already doing an alias analysis based on types. 最后,如果这是gcc at -O2,-O3或-Os,则编译器已经基于类型进行别名分析。 I'm sure other compilers do this also. 我相信其他编译器也会这样做。 This means that restricting the pointers vs the ints is already understood, leaving only the arrays that could possibly store to each other. 这意味着已经理解了限制指针与int的限制,只留下可能相互存储的数组。

In sum, the optimizer will assume that the pointers aren't being stored into as ints, and it knows it isn't doing any pointer writes during the loop. 总而言之,优化器将假定指针未被存储为int,并且它知道它在循环期间没有执行任何指针写入。

So you will probably get the same code with only the one restrict. 所以你可能会得到只有一个限制的相同代码。

The outer (second) restrict tells the compiler that none of the arrays of pointers (A, B, and out) alias. 外(第二)限制告诉编译器没有指针数组(A,B和out)别名。 The inner (first) restrict tells the compiler that none of the arrays of ints (pointed to by elements of the arrays of pointers) alias. 内部(第一个)限制告诉编译器没有任何int数组(由指针数组的元素指向)别名。

If you access both A[0][col*nrows + row] and A[col][row] then you're violating the inner restrict, so things might break. 如果同时访问A [0] [col * nrows + row]和A [col] [row],那么你违反了内部限制,因此事情可能会中断。

int **restrict only asserts that the memory addressed by Out, A and B don't overlap (except that A and B can overlap, assuming your function doesn't modify either of them). int **restrict只断言Out,A和B寻址的内存不重叠(除了A和B可以重叠,假设你的函数不修改它们中的任何一个)。 This means the arrays of pointers. 这意味着指针数组。 It doesn't assert anything about the contents of the memory pointed to by Out, A, and B. Footnote 117 in n1124 says: 它没有断言Out,A和B指向的内存的内容.n1124中的脚注117说:

if identifier p has type (int **restrict), then the pointer expressions p and p+1 are based on the restricted pointer object designated by p, but the pointer expressions *p and p[1] are not. 如果标识符p具有类型(int ** restrict),则指针表达式p和p + 1基于由p指定的受限指针对象,但指针表达式* p和p [1]不是。

By analogy with const , I suspect that qualifying with restrict twice will assert what you want, which is that none of the values in the array points to overlapping memory. 通过类比const ,我怀疑使用restrict两次restrict将断言你想要的东西,即数组中没有任何值指向重叠的内存。 But reading the standard, I can't prove to myself that it actually does. 但是阅读标准,我无法向自己证明它确实存在。 I reckon that "Let D be a declaration of an ordinary identifier that provides a means of designating an object P as a restrict-qualified pointer to type T" does indeed mean that for int *restrict *restrict A , then A[0] and A[1] are objects designated as a restrict-qualified pointer to int. 我认为“设D是普通标识符的声明,它提供了一种将对象P指定为类型T的限制限定指针”的方法,这确实意味着对于int *restrict *restrict A ,则A [0]和A [1]是指定为int的限制限定指针的对象。 But it's pretty heavy legalese. 但这是非常重的法律术语。

I have no idea whether your compiler will actually do anything with that knowledge, mind you. 请注意,我不知道你的编译器是否真的会对这些知识做任何事情。 Clearly it could, it's a question of whether it's implemented. 显然它可能,这是一个是否实施的问题。

So I don't really know what you've gained over a conventional C 2-D array, where you just allocate rows * cols * sizeof(int) , and index with A[cols*row + col] . 所以我真的不知道你在传统的C 2-D数组中获得了什么,你只需要分配rows * cols * sizeof(int) ,并使用A[cols*row + col]索引。 Then you clearly only need one use of restrict, and any compiler that does anything with restrict will be able to re-order reads from A and B across writes to Out. 然后,你清楚地只需要一个使用限制,并没有做任何事情与任何编译器restrict将能够重新排列从A读取和B跨越写入输出。 Without restrict , of course, it can't, so by doing what you're doing, you're throwing yourself on your compiler's mercy. 当然,没有restrict ,它不能,所以通过做你正在做的事情,你就是在抛弃编译器的怜悯。 If it can't cope with double-restrict, only the single restrict case, then your double-indirection has cost you the optimization. 如果它无法应对双限制,只有单一的限制情况,那么你的双重间接已经花费了你的优化。

At first guess, multiplication is likely to be faster than an additional pointer indirection anyway. 首先猜测,乘法可能比另外的指针间接更快。 You obviously care about performance or you wouldn't be using restrict at all, so I'd test performance fairly carefully (on all compilers you care about) before making this change for the sake of slightly nicer syntax and not having to remember how many columns there are in your array every time you access it. 您显然关心性能,或者您根本不会使用限制,因此我会在进行此更改之前相当仔细地测试性能(在您关注的所有编译器上),以便稍微更好的语法并且不必记住多少每次访问时,列中都有列。

is accessing elements through A[0][col*nrows + row] undefined? 通过A [0] [col * nrows + row]未定义访问元素?

Yes, if the element is modified by one of the accesses, because this makes A[0] an alias for memory also accessed via A[col]. 是的,如果元素被其中一个访问修改,因为这使A [0]成为通过A [col]访问的内存的别名。 That'd be fine if only A and B were restrict-qualified pointers, but not if A[0] and A[col] are. 如果只有A和B是限制限定指针,那就没问题了,但是如果A [0]和A [col]都没有。

I assume that you don't modify A in this function, so actually that alias is fine. 我假设你没有在这个函数中修改A,所以实际上别名是好的。 If you did the same thing with Out, though, behavior would be undefined. 但是,如果你对Out做了同样的事情,行为将是未定义的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM