我正在尝试使用OpenMP实现argmax。如果很短,我有一个计算浮点值的函数:
double toOptimize(int val);
我可以通过以下方式获得最大化值的整数:
double best = 0;
#pragma omp parallel for reduction(max: best)
for(int i = 2 ; i < MAX ; ++i)
{
double v = toOptimize(i);
if(v > best) best = v;
}
现在,我如何获得与最大值相对应的值i
?
修改
我正在尝试这个,但我想确保它是有效的:
double best_value = 0;
int best_arg = 0;
#pragma omp parallel
{
double local_best = 0;
int ba = 0;
#pragma omp for reduction(max: best_value)
for(size_t n = 2 ; n <= MAX ; ++n)
{
double v = toOptimize(n);
if(v > best_value)
{
best_value = v;
local_best = v;
bn = n;
}
}
#pragma omp barrier
#pragma omp critical
{
if(local_best == best_value)
best_arg = bn;
}
}
最后,我应该best_arg
toOptimize
的argmax。
答案 0 :(得分:1)
您的解决方案完全符合标准。无论如何,如果你愿意添加一些语法糖,你可以尝试以下内容:
#include<iostream>
using namespace std;
double toOptimize(int arg) {
return arg * (arg%100);
}
class MaximumEntryPair {
public:
MaximumEntryPair(size_t index = 0, double value = 0.0) : index_(index), value_(value){}
void update(size_t arg) {
double v = toOptimize(arg);
if( v > value_ ) {
value_ = v;
index_ = arg;
}
}
bool operator<(const MaximumEntryPair& other) const {
if( value_ < other.value_ ) return true;
return false;
}
size_t index_;
double value_;
};
int main() {
MaximumEntryPair best;
#pragma omp parallel
{
MaximumEntryPair thread_local;
#pragma omp for
for(size_t ii = 0 ; ii < 1050 ; ++ii) {
thread_local.update(ii);
} // implicit barrier
#pragma omp critical
{
if ( best < thread_local ) best = thread_local;
}
} // implicit barries
cout << "The maximum is " << best.value_ << " obtained at index " << best.index_ << std::endl;
cout << "\t toOptimize(" << best.index_ << ") = " << toOptimize(best.index_) << std::endl;
return 0;
}
答案 1 :(得分:1)
我只是为每个线程创建一个单独的缓冲区来存储val
和idx
,然后选择缓冲区的最大值。
std::vector<double> thread_maxes(omp_get_max_threads());
std::vector<int> thread_max_ids(omp_get_max_threads());
#pragma omp for reduction(max: best_value)
for(size_t n = 2 ; n <= MAX ; ++n)
{
int thread_num = omp_get_num_threads();
double v = toOptimize(n);
if(v > thread_maxes[thread_num])
{
thread_maxes[thread_num] = v;
thread_max_ids[thread_num] = i;
}
}
std::vector<double>::iterator max =
std::max_element(thread_maxes.begin(), thread_maxes.end());
best.val = *max;
best.idx = thread_max_ids[max - thread_maxes.begin()];
答案 2 :(得分:0)
你的解决方案很好。它与临界区有O(nthreads)收敛。但是,可以使用O(Log(nthreads))收敛来完成此操作。
例如,想象有32个线程。 您将首先找到32个线程的本地最大值。然后你可以组合16个线程,然后是8个,然后是4个,然后是2个,然后是1.在五个步骤中,你可以合并本地最大值而不需要一个临界区和过程中的自由线程。但是你的方法会在一个关键部分合并32个步骤中的局部最大值并使用所有线程。
同样的逻辑是减少。这就是为什么最好让OpenMP进行减少,而不是手动使用原子部分。但至少在OpenMP的C / C ++实现中,没有简单的方法来获得O(Log(nthreads))中的max / min。可能有可能使用任务,但我没有尝试过。
实际上,这可能没有什么区别,因为与并行循环相比,合并局部值甚至是临界区的时间可能是微不足道的。尽管“线程”的数量要大得多,但它可能会在GPU上产生更大的差异。