tbb :: combinable :: local()太慢了

时间:2015-05-23 00:35:04

标签: c++ multithreading tbb

我已经并行执行了一个worker函数。这个worker函数经常调用tbb :: combineable :: local(),结果很慢。 我想我需要实现自己的可组合。

我想在每个MyCombineable中预先分配包含元素的向量,并通过从thread-id派生的一些整数来访问它。这个索引可以在worker函数中确定一次,并传递给每个可合并:: local()的调用。 但是为了做到这一点,我需要知道tbb的线程池中的线程数及其thread-id。

还是其他任何想法?

2 个答案:

答案 0 :(得分:2)

而不是实现自己的.local方法,您是否尝试将调用缓存为参考并且不经常进行查找,也可能每个任务一次处理多个项目。

要查看线程进入,您可以在调度任何并发工作之前在tbb中实现task_scheduler_observer,然后通过调度某些任务来预热调度程序,以便在您预先分配线程ID时查看线程ID。 / p>

你也可以尝试在concurrent_unordered_map之上实现自己的东西来获取线程id并使用它来将索引存储到一个向量中,但是我怀疑你会发现如果你查找线程的成本仍然很高注意到可组合的查找成本。

答案 1 :(得分:0)

使用下面的代码,local()函数不再显示在分析中。 一个人需要检索当前线程的索引一次(使用getTid() - 见下文)然后可以通过传递索引来调用不同对象的快速local()实现。

// *s_iNumberOfThreads is passed to tbb::task_scheduler_init()
// and represents the number of threads
// still don't know how to get the number of threads in tbb's threadpool
template<typename T>
struct Combinable
{       std::vector<T*, tbb::cache_aligned_allocator<T*> > m_s;
        tbb::cache_aligned_allocator<T> m_sAllocator;
        __forceinline void allocate(void)
        {       try 
                {       for (std::size_t i = 0, iMax = m_s.size(); i < iMax; ++i)
                                m_s[i] = m_sAllocator.allocate(1);
                } catch (...)
                {       deallocate();
                        throw;
                }
        }
        __forceinline void deallocate(void)
        {       for (std::size_t i = 0, iMax = m_s.size(); i < iMax; ++i)
                        if (m_s[i])
                                m_sAllocator.deallocate(m_s[i], 1);
        }
        __forceinline Combinable(const Combinable&_r)
                :m_s(*s_iNumberOfThreads)
        {       allocate();
                for (std::size_t i = 0, iMax = m_s.size(); i < iMax; ++i)
                        *m_s[i] = *_r.m_s[i];
        }
        __forceinline Combinable &operator=(const Combinable&_r)
        {       if (&_r != this)
                        for (std::size_t i = 0, iMax = m_s.size(); i < iMax; ++i)
                                *m_s[i] = *_r.m_s[i];
                return *this;
        }
        __forceinline Combinable(void)
                :m_s(*s_iNumberOfThreads)
        {       allocate();
                clear();
        }
        __forceinline ~Combinable(void)
        {       deallocate();
        }
        __forceinline T &local(const unsigned int _i)
        {       return *m_s[_i];
        }
        __forceinline const T &local(const unsigned int _i) const
        {       return *m_s[_i];
        }
        template<typename C>
        __forceinline T combine(const C &_r) const
        {       T d(0.0);
                for (std::size_t i = 0, iMax = m_s.size(); i < iMax; ++i)
                        d = _r(d, *m_s[i]);
                return d;
        }
        __forceinline void clear(void)
        {       for (std::size_t i = 0, iMax = m_s.size(); i < iMax; ++i)
                        *m_s[i] = 0.0;
        }
};  
static tbb::combinable<unsigned int> s_sThreadId((unsigned int)~0);
static long s_iMaxThreadId;
    // this function returns the index to be passed to local()
__forceinline unsigned int getTid(void)
{       unsigned int &i = s_sThreadId.local();
        if (i == ~0)
                // could use tbb::atomic here -- but it is difficult to
                // initialize
                // or even better std::atomic 
                // (but I cannot use this currently 
                // due to compatibility reason)
#ifdef _MSC_VER
                return i = _InterlockedIncrement(&s_iMaxThreadId) - 1;
#else
                return i = __sync_add_and_fetch(&s_iMaxThreadId, 1) - 1;
#endif
        else
                return i;
}