繁体   English   中英

std :: unordered_map:多线程插入?

[英]std::unordered_map: multithreading insertions?

我有一堆数据(0和ULLONG_MAX之间的一个巨大的整数列表),我想提取所有唯一值。 我的方法是创建一个unordered_map,使用整数列表值作为键,并为地图值使用一次性bool。 我迭代列表并为每个键插入一次性值。 最后,我迭代地图以获得所有唯一键。 挺直的。

但是,我的列表是如此之大(100亿),我想多线程这个过程。 我知道一种天真的线程方法是行不通的,因为unordered_map插入会影响底层数据结构,所以它不是线程安全的。 并且在每次插入时添加锁定将是缓慢的并且可能否定任何线程加速。

但是,大概不是每次插入都会改变数据结构(只有那些不适合现有分配的存储桶?)。 有没有办法在插入之前检查特定插入是否需要unordered_map重新分配? 这样我只能在地图变更时锁定线程,而不是在每次插入时锁定。 然后,在每次插入之前,线程仅检查锁是否存在...而不是完全锁定/解锁。 那可能吗?

并行化的基本规则打破了工作,处理碎片,然后组合碎片。

散列/项目查找是整个shebang中最昂贵的部分,因此我们将专注于并行化。

如果您绝对需要将结果作为哈希表,我会收到一些坏消息:您必须自己编写。 话虽这么说,让我们开始吧。

首先,让我们连续解决问题。 这很简单。 以下函数采用向量和回调。 我们将获取向量,将其转换为unordered_set ,并将unordered_set给回调。 简单? 是。

现在,因为我们将在一个线程上执行此操作,所以我们无法立即执行此操作。 相反,我们将返回一个不带参数的lambda。 当调用lambda时,就会创建unordered_set并将其提供给回调。 这样,我们可以将每个lambda赋予它自己的线程,并且每个线程将通过调用lambda来运行该作业。

template<class Vector, class Callback>
auto lazyGetUnique(Vector& vector, Callback callback) {
    using Iterator = decltype(vector.begin());
    auto begin = vector.begin();
    auto end = vector.end();
    using elem_t = typename std::iterator_traits<Iterator>::value_type;

    //We capture begin, end, and callback
    return [begin, end, callback]() {
        callback(std::unordered_set<elem_t>(begin, end));
    };
}

现在 - 这个回调应该做什么? 答案很简单:回调应该将unordered_set的内容分配给一个向量。 为什么? 因为我们要合并结果,合并向量比合并unordered_set要快得多。

让我们编写一个函数来给我们回调:

template<class Vector>
auto assignTo(Vector& v) {
    return [&](auto&& contents) {
        v.assign(contents.begin(), contents.end());
    };
}

假设我们想要获取向量的唯一元素,并将它们分配回该向量。 现在这很简单:

std::vector<int> v = /* stuff */;
auto new_thread = std::thread( lazyGetUnique(v, assignTo(v)) ); 

在此示例中,当new_thread完成执行时, v将仅包含唯一元素。

让我们来看看完成所有功能的完整功能。

template<class Iterator>
auto getUnique(Iterator begin, Iterator end) {
    using elem_t = typename std::iterator_traits<Iterator>::value_type;

    std::vector<elem_t> blocks[4];

    //Split things up into blocks based on the last 4 bits
    //Of the number. This allows us to guarantee that no two blocks
    //share numbers. 
    for(; begin != end; ++begin) {
        auto val = *begin; 
        blocks[val & 0x3].push_back(val); 
    }

    //Each thread will run their portion of the problem.
    //Once it's found all unique elements, it'll stick the result in the block used as input
    auto thread_0 = std::thread( lazyGetUnique(blocks[0], assignTo(blocks[0])) );
    auto thread_1 = std::thread( lazyGetUnique(blocks[1], assignTo(blocks[1])) );
    auto thread_2 = std::thread( lazyGetUnique(blocks[2], assignTo(blocks[2])) );

    //We are thread_3, so we can just invoke it directly
    lazyGetUnique(blocks[3], assignTo(blocks[3]))(); //Here, we invoke it immediately

    //Join the other threads
    thread_0.join();
    thread_1.join();
    thread_2.join(); 

    std::vector<elem_t> result;
    result.reserve(blocks[0].size() + blocks[1].size() + blocks[2].size() + blocks[3].size());

    for(int i = 0; i < 4; ++i) {
        result.insert(result.end(), blocks[i].begin(), blocks[i].end());
    }

    return result;
}

这个函数将东西分成4个块,每个块都是不相交的。 它在4个块中的每个块中找到唯一元素,然后组合结果。 输出是一个向量。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM