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()
,因为它是非常昂贵的。但是,由于v.push_back()
,我不能简单地使用#pragma omp parallel for
。
据我所知,这里还有两种选择:
- 我们使用
#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)
。因此,这看起来像一个可维护性的噩梦(我必须修改所有嵌套函数的所有标题和调用)......但更重要的是:我们同意通过引用是有效的,但它总是一个指针复制。正如你所看到的,这里很多指针被复制了很多次。所有这些副本是否可能导致效率低下?其实我最关心的就是性能,所以如果你告诉我“没关系,这很好,因为编译器是超级智能的,他们做了一些魔术,所以你可以复制一万亿次引用,并且性能没有下降”,那么它会很好,但我不知道这样的魔法是否存在。
为什么我这样做: 我试图并行this代码。特别是,我将while
here改写为for
,它可以并行化,但如果您按照代码进行操作,则会发现调用的onAffineShapeFound
被调用,这将修改共享对象keys
的状态。这发生在其他许多变量中,但这是此代码的“最深”情况。
不知道你是否可以用openmp来完成,但你也可以初始化矢量到完整大小,然后给每个线程一段矢量工作。是的,他们都将在同一个向量上工作,但由于它们都不会触及相同的元素,并且没有任何线程修改向量的状态,所以它应该是线程安全的。 – NathanOliver
@NathanOliver感谢您的回答。这是不可能的,因为没有办法事先知道'键'的大小。 – justHelloWorld
“*正如你所看到的,这里有很多指针被复制了很多次,有可能所有这些副本都会导致效率低下吗?*” - 与之相比,究竟是什么?如果'T'是一个小的类型,比如'char'或者甚至是'int',唯一一次'void foo(T&t)'比'void foo(T t)'效率低。在任何其他情况下,使用引用的性能几乎总是更好。 – Xirema