这是这个问题的后续问题,限制访问属于单独对象的已分配数组,在发现跨编译单元按值返回无助于提示编译器有关不相交的内存分配时。
我有两个函数
make_v1
和 make_v2
,它们按值返回一个类似数组的对象。
(例如,这可能是std::vector
,重要的是复制构造函数明确分配内存)。
#include<vector>
template<class It> void modify_v(It);
void dreaded_function();
template<class It1, class It2>
void foo(It1 a, It2 b) {
*a = 5;
*b = 6;
if(*a == *b) dreaded_function(); // this is never called if a and b do not overlap
}
int main() {
std::vector<int> v1 = make_v1();
std::vector<int> v2 = make_v2();
foo(v1.begin(), v2.begin());
}
正如您在编译的代码中看到的,没有假设优化对
dreaded_function
的调用。
这只是指针别名的一个示例,只是为了诊断情况。
众所周知,指针别名会造成最糟糕的性能问题。
https://godbolt.org/z/345PKrsnx
我可以在 C++ 中(或者可能使用扩展)向编译器提供哪些高级提示,表明迭代器范围位于不能重叠的内存区域?
如果这是 C,或者如果我使用指针和 C++ 扩展,我可以使用
__restrict
和指针接口,但我想要更高级的东西。
(感谢@TedLygmo 指出 __restrict 在某种程度上适用于 std::vector 迭代器).
我发现的唯一选择是对副本非常明确:
...
std::vector<int> v1 = static_cast<std::vector<int> const&>(make_v1());
std::vector<int> v2 = static_cast<std::vector<int> const&>(make_v2());
这至少让clang相信它可以进行优化。
https://godbolt.org/z/nYfKeTKqj
但这很丑,而且也是复制,而且如果以后
make_v
成为包含功能,解决方案也会有成本。
(而且它仍然不能说服 GCC!)
这是我可以让编译器比
v1
和v2
内存不重叠的唯一方法吗?有没有更精简的解决方案?
[[假设]]最终应该为此工作吗?
此时,我最希望的是一些关键字/扩展名“模拟”
make_v1
/make_v2
的结果复制到v1
/v2
。
我找到了一种巧妙的方法来做到这一点。 我意识到我可以在成员(指针)变量中使用
__restrict
关键字。
所以,基本上,如果我用限制指针“重新实现”向量迭代器,那么原则上我就可以摆脱这个问题。
实际上,这适用于 GCC,而不是 clang)。
template<class Iterator>
struct restricted_vector_iterator {
restricted_vector_iterator(Iterator it) : p_{&*it} {}
decltype(auto) operator*() const {return *p_;}
decltype(auto) operator++(int) {++p_; return *this;}
private:
typename Iterator::pointer __restrict p_;
};
...
foo(restricted_vector_iterator{v1.begin()}, restricted_vector_iterator{v2.begin()});
https://godbolt.org/z/4KasW3W7h
当然这并不理想。
首先,因为每个关键迭代器都必须被包装/重新实现(__restrict 不能应用于迭代器类,只能应用于所包含的指针)。 其次,让受限制的指针在野外保持活动状态听起来有点危险,也许我可以使这个包装迭代器不可复制或类似的东西。 第三,我不清楚指针算术如何工作(指针是从其他受限制的指针计算出来的,也受限制吗?其中?) 第四,它不可移植,既因为
__restrict
是一个扩展,又因为最终它似乎只能在 GCC 上运行。
如果您有任何意见请告诉我。