限制访问属于单独对象的已分配数组

问题描述 投票:0回答:1

我正在考虑非标准

__restrict
关键字在 C 和 C++ 中的实用性,以及如何通过仔细声明(不相交)值对象来模拟其效果。

限制通常通过可能或可能不重叠内存地址的间接内存访问来解释:

int foo(Pointer1 a, Pointer2 b) {  // adding non-standard `restrict` keyword might hint that dreaded_function_call is never called
    *a = 5;
    *b = 6;
    if(*a == *b) dreaded_function_call();  // in isolation this function may or may not be called
}

如果编译器可以证明

a
b
不重叠,那么
dreaded_function_call()
永远不会在编译中被调用或引用。

这正是我在本示例中实现的目标,至少使用 GCC,

dreaded_function_call
甚至没有出现在生成的机器代码中。

#include<vector>

template<class It> void modify_v(It);

void dreaded_function_call();

template<class It1, class It2>
void foo(It1 a, It2 b) {
    *a = 5;
    *b = 6;
    if(*a == *b) dreaded_function_call();
}

int main() {
    std::vector<int> v1(10); modify_v(v1.begin());
    std::vector<int> v2(10); modify_v(v2.begin());

    foo(v1.begin() + 5, v2.begin() + 5);
}

但是,如果我稍微更改代码以通过单独的函数调用生成向量本身,我就会失去这种优化。 我可以观察到这一点,因为生成的代码将分支并仍然考虑调用

dreaded_function_call
的可能性。

std::vector<int> make_v();
...
int main() {
    std::vector<int> v1 = make_v();
    std::vector<int> v2 = make_v();

    foo(v1.begin() + 5, v2.begin() + 5);
}

这是怎么回事?

make_v
通过副本返回,因此
v1
v2
应该是不相交的,就像上面的情况一样。 然而编译器并没有做同样的优化。

这是否只是错过了优化机会,或者说这种优化完全无效?

这里是用 GCC 说明这两种情况的编译代码:https://godbolt.org/z/WKKzs1edc

(Clang 给出相同的行为。)

c++ allocation restrict-qualifier
1个回答
0
投票

每个

std::vector<int>
都持有一个指针,该指针指向保存元素的实际内存区域。

从标准中的库规范中我们知道,两个

std::vector<int>
对象不可能引用相同的元素对象或相同的存储。

但是,从实际的核心语言角度来看,没有什么可以阻止您编写一个

make_v
函数,该函数始终返回内部指针初始化为相同值的
std::vector<int>
对象。特别是,即使没有公共成员函数来实现这一点,也总是可以使用技巧来直接访问私有数据成员。

当然,您尝试执行此操作的任何方式都会在库规则下产生未定义的行为,但编译器(有一些较小的例外)不知道或考虑库规范,而是仅使用标准库代码就像任何其他代码一样。

在第一个示例中,编译器可以通过内联看到实际的

operator new
调用。对
operator new
的两次调用必须始终生成指向不相交内存的指针,而不会干扰
operator delete
调用。这是编译器优化器内置的知识。

© www.soinside.com 2019 - 2024. All rights reserved.