gpt4 book ai didi

c++ - 使用 omp_get_thread_num 索引全局 vector 是否安全?

转载 作者:行者123 更新时间:2023-12-04 07:21:13 27 4
gpt4 key购买 nike

我有一个这样的代码:

thread_local CustomAllocator* ts_alloc = nullptr;

struct AllocatorSetup
{
AllocatorSetup( int threadNum )
{
static std::vector<CustomAllocator> vec( (size_t)omp_get_max_threads() );
ts_alloc = &vec.at( threadNum );
}
~AllocatorSetup()
{
ts_alloc->resetArena();
ts_alloc = nullptr;
}
AllocatorSetup() = delete;
AllocatorSetup( const AllocatorSetup& ) = delete;
void operator=( const AllocatorSetup& ) = delete;
};

template<class E>
inline E* allocateBuffer( size_t count )
{
return (E*)ts_alloc->allocate( count * sizeof( E ), alignof( E ) );
}

void computeThings()
{
#pragma omp parallel for
for( int64_t i = 0; i < 100000; i++ )
{
AllocatorSetup allocSetup{ omp_get_thread_num() };
float* const buffer = allocateBuffer<float>( 1024 * 256 );
// ..some computations here
}
}
多线程调用 computeThings()会断吗同时?
换句话说,在任何给定时间, omp_get_thread_num() 之间的关系是索引和本地线程是一对一还是一对多?

最佳答案

The documentation spells this out.

The omp_get_thread_num routine returns the thread number, within the current team, of the calling thread.


The binding thread set for an omp_get_thread_num region is the current team. The binding region for an omp_get_thread_num region is the innermost enclosing parallel region.


The omp_get_thread_num routine returns the thread number of the calling thread, within the team that is executing the parallel region to which the routine region binds. The thread number is an integer between 0 and one less than the value returned by omp_get_num_threads, inclusive. The thread number of the master thread of the team is 0. The routine returns 0 if it is called from the sequential part of a program.


强调我的。
这意味着如果您有多个团队同时执行,则每个团队中的线程编号为 0 .. nbr_threads_in_team .因此,如果多个线程在不同的并发运行团队中,它们将获得相同的线程编号。
虽然 OpenMP 肯定会重用线程,但如果它真的同时运行两个并行部分,您将同时运行两个线程组。由于单个线程不能同时做两件事,因此这些线程必须不同,但每个团队中的线程编号为 0 .. nbr_threads_in_team .
这意味着您的 computeThings()函数目前不是线程安全的。您可以改为构建 CustomAllocator 的线程安全池。线程可以“借出”一个对象并在线程完成后返回它的对象。
像这样的东西:
void computeThings() {
#pragma omp parallel
{
//Since we're in a parallel block, each thread
//will lend its own CustomAllocator
CustomAllocator& theAllocator = allocatorPool.lend();

#pragma omp for
for (...) {
/* Do stuff with the allocator */
}

allocatorPool.unLend(theAllocator);
}
}
在生产代码中, lend()unLend()当然应该使用 RAII 实现以避免在异常情况下泄漏。请注意,它仅在整个并行 for 循环之前获取一次新资源,而不是在循环的每次迭代中,因此每次调用只需为缓存未命中支付一次。 (除非您在循环中调用该函数本身,否则您的数据不太可能在对 computeThings 的调用之间保留在 L1 中。)如果您确实需要对 L1 友好,即使在多次调用该函数时,您可以关联每个资源都有一个线程 ID,如果“首选资源”仍然可用,则优先将资源返回给之前请求它们的同一线程。
下面是这种缓存的示例,它尝试为每个请求线程提供其首选资源实例:
//Warning, untested!
template<typename T>
class ThreadAffineCache {
std::unordered_map<std::thread::id, std::unique_ptr<T>> m_cache = {};
std::mutex m_mtx{};

public:
std::unique_ptr<T> lend() {
std::unique_lock lk{m_mtx};
auto tid = std::this_thread::get_id();
std::unique_ptr<T> result = std::move(m_cache[tid]);
m_cache.erase(tid);
if (!result) {
if (m_cache.empty()) {
result = std::make_unique<T>();
} else {
auto iter = m_cache.begin();
result = std::move(*iter);
m_cache.erase(iter);
}
}
assert(result);
return result;
}

void unLend(std::unique_ptr<T> obj) {
assert(obj);
std::unique_lock lk{m_mtx};
m_cache[std::this_thread::get_id()] = std::move(obj);
}
}
或者,您可以使用由 pthread_self 返回的值索引的 map 。或 gettid .我不确定 std::thread::id返回一些对 OpenMP 工作线程有意义的东西。在线程终止时进行清理也可能是一个问题。
如果您只需要一条简单的出路,您也可以保护 computeThings带有互斥锁。

关于c++ - 使用 omp_get_thread_num 索引全局 vector 是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68484180/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com