【问题标题】:Why is multi threaded operation on one vector slow?为什么一个向量上的多线程操作很慢?
【发布时间】:2020-06-03 00:26:29
【问题描述】:

我有很多键(c 字符串),我想预先计算它们的哈希值。我制作了一个包含关键数据及其散列的结构。我将这些结构推入向量并将向量分组。每组键将由一个线程进行哈希处理。

小例子:

struct Key
{
    char* data;    // mostly 10 character strings
    uint64_t hash; // init with 0 and compute later
};

// hash group of keys
static void hash_keys(size_t idx_start, size_t const& length)
{
    size_t idx_end = idx_start + length;
    for (size_t i = idx_start; i < idx_end; i++)
    {
        Key* k = keys[i];
        // hash key by murmurhash2 or djb2 hash function
        k->hash = external_hash_key(k->data);
    }
}

vector<Key*> keys;

// push all keys into keys vector
external_fill_keys();
size_t num_of_keys = keys.size();

// start threads
vector<thread> workers;

size_t length = num_of_keys / NUM_OF_WORKERS;
size_t remainder = num_of_keys % NUM_OF_WORKERS;

for (size_t i = 0; i < NUM_OF_WORKERS; i++)
    workers.push_back(
        thread(
            hash_keys,
            i * length, length
        )
    );

hash_keys(NUM_OF_WORKERS * length, remainder);

// join threads
for (auto& worker : workers)
    worker.join();

我有大约 3 000 000 把钥匙。如果我用单线程运行代码 - 只需调用 hash_keys(0, keys.size()) - 我会得到 4.0 秒的估计时间。如果我用 4 个工作线程运行代码,我会得到 5.5 秒。

问题是为什么这么慢?不推荐从多个线程中读取相同的向量吗?我怎样才能利用这些线程并在更短的时间内做到这一点?

【问题讨论】:

  • 尝试使用size_t i = idx_start 代替hash_keys。看起来您正在多次散列较早的键。
  • @FrançoisAndrieux 不抱歉。那是一个错字。我最初只使用idx_start 作为它的副本,我不需要局部变量i。我编辑了那个。
  • perf 说什么?
  • 我的猜测是您正在破坏 L2 或 L3 缓存。您可以尝试按线程交错散列,而不是为每个分配一个大块(间隔很远)。任何人都可以猜测char *Key* 都是在内存中创建的,但它们可能很接近。
  • 尝试优化解决方案,例如 tbb::parallel_for

标签: c++ multithreading vector hash


【解决方案1】:

原来我的代码有两个问题:

  • 虚假共享,当一个线程更新一个键的哈希值时,它试图写入与相邻线程相同的缓存行,这大大减慢了执行速度
  • 每个键都是通过单个 new 调用创建的,而不是一次创建多个键(在示例中不可见,此问题发生在函数 external_fill_keys 中)。

解决方案是为每个线程创建独立的键数组,并在加入线程后,将数组连接成一个大数组。

【讨论】:

    猜你喜欢
    • 2018-05-27
    • 2012-09-28
    • 1970-01-01
    • 2014-03-17
    • 1970-01-01
    • 1970-01-01
    • 2014-07-21
    • 1970-01-01
    相关资源
    最近更新 更多