Disclamer:我正在使用英特尔编译器2017,如果您想知道我为什么这样做,请转到问题的最后。
我有这段代码:
class A{
vector<float> v;
...
void foo();
void bar();
}
void A::foo(){
for(int i=0; i<bigNumber;i++){
//something very expensive
//call bar() many times per cycle;
}
}
void A::bar(){
//...
v.push_back(/*something*/);
}
现在,我们假设我要并行foo()
,因为它非常昂贵。但是,由于#pragma omp parallel for
,我无法简单地使用v.push_back()
。
据我所知,这里有两种选择:
#pragma omp critical
v
的本地版本,然后我们在并行部分的末尾联合它们,或多或少按照here解释。解决方案1.通常被认为是一个糟糕的解决方案,因为竞争条件会产生一致的开销。
但是,解决方案2.需要以这种方式修改bar()
:
class A{
vector<float> v;
...
void foo();
void bar(std::vector<float> &local_v);
}
void A::foo(){
#pragma omp parallel
{
std::vector<float> local_v;
#pragma omp for
for(int i=0; i<bigNumber;i++){
//something very expensive
//call bar(local_v) many times per cycle;
}
#pragma omp critical
{
v.insert(v.end(), local_v.begin(), local_v.end());
}
}
}
void A::bar(std::vector<float> &local_v){
//...
v.push_back(/*something*/);
}
到目前为止一切顺利。现在,我们假设不仅有v
,而且还有10个向量,比如v1
,v2
,...,v10
,或者无论如何10个共享变量。此外,我们假设bar
不是直接在foo()
内调用,而是在许多嵌套调用之后调用。像foo()
调用foo1(std::vector<float> v1, ..., std::vector<float> v10)
调用foo2(std::vector<float> v1, ..., std::vector<float> v10)
,重复此嵌套调用很多次,直到最后一次调用bar(std::vector<float> v1, ..., std::vector<float> v10)
。
所以,这看起来像是一个可维护性的噩梦(我必须修改所有嵌套函数的所有标题和调用)......但更重要的是:我们同意通过引用传递是有效的,但它&#39;总是一个指针副本。如您所见,这里有很多指针被复制多次。所有这些副本是否有可能导致效率低下?
实际上我最关心的是性能,所以如果你告诉我&#34; nah,它很好,因为编译器是超级智能的,他们做了一些巫术,所以你可以复制一万亿个引用,并且有性能没有下降&#34;那就没事了,但我不知道这样的巫术是否存在。
为什么我这样做:
我试图并行化this代码。特别是,我将while
here重写为可以并行化的for
,但如果您按照代码进行回调,则会发现回拨调用来自here的onAffineShapeFound
,它会修改共享对象keys
的状态。对于许多其他变量,这种情况会发生,但这是最深的&#34;此代码的大小写。
答案 0 :(得分:1)
在a::Bar()
和a::Bar(std::vector<float> & v)
之间的直接比较中,不同之处在于第二个版本必须将堆栈的大小增加8个字节,而不是原始版本必须做的。在性能方面,这是一个非常小的效果:无论函数是否包含参数,都必须调整堆栈指针(因此唯一真正的区别是单个指针复制,甚至可能根据编译器进行优化) ),就函数本身的实际性能而言,不断向std::vector
添加元素将是一个更昂贵的操作,特别是如果向量需要重新分配(这可能经常发生,取决于向量需要获得的大小,这意味着这些成本将远远超过指针副本的成本。
所以,简短的版本:坚持参考。