【问题标题】:Prevent False Sharing without using padding在不使用填充的情况下防止虚假共享
【发布时间】:2015-07-28 20:20:58
【问题描述】:

我目前正在学习 C 语言中的 pthreads 并遇到了错误共享的问题。我想我理解它的概念,并且我尝试过一些实验。

下面是我一直在玩的一个小程序。最终我将把它改成一个程序来获取大量的整数数组并并行求和。

#include <stdio.h>
#include <pthread.h>

#define THREADS 4
#define NUMPAD 14

struct s
{
  int total; // 4 bytes
  int my_num; // 4 bytes
  int pad[NUMPAD]; // 4 * NUMPAD bytes
} sum_array[4];

static void *worker(void * ind) {
    const int curr_ind = *(int *) ind;
    for (int i = 0; i < 10; ++i) {
      sum_array[curr_ind].total += sum_array[curr_ind].my_num;
    }
    printf("%d\n", sum_array[curr_ind].total);
    return NULL;
}

int main(void) {
    int args[THREADS] = { 0, 1, 2, 3 };
    pthread_t thread_ids[THREADS];

    for (size_t i = 0; i < THREADS; ++i) {
        sum_array[i].total = 0;
        sum_array[i].my_num = i + 1;
        pthread_create(&thread_ids[i], NULL, worker, &args[i]);
    }

    for (size_t i = 0; i < THREADS; ++i) {
        pthread_join(thread_ids[i], NULL);
    }
}

我的问题是,是否可以在不使用填充的情况下防止虚假共享?这里struct s 的大小为 64 字节,因此每个结构都在自己的缓存行上(假设缓存行是 64 字节)。我不确定如何在没有填充的情况下实现并行性。

另外,如果我要对一个大小在 1000-50,000 字节之间的数组求和,如何防止错误共享?我可以使用类似的程序来填充它吗?我目前的想法是将大数组中的每个 int 放入struct s 的数组中,然后使用并行性对其求和。但是我不确定这是否是最佳解决方案。

【问题讨论】:

  • 您也可以在 GNU C 或 C++ 中使用 __attribute__((aligned(64))) 或在 MSVC 中使用 __declspec(align(64))

标签: c parallel-processing pthreads false-sharing


【解决方案1】:

分区问题:在worker()中,求和成一个局部变量,然后将该局部变量添加到数组中:

static void *worker(void * ind) {
    const int curr_ind = *(int *) ind;
    int localsum = 0;
    for (int i = 0; i < 10; ++i) {
      localsum += sum_array[curr_ind].my_num;
    }
    sum_array[curr_ind].total += localsum;
    printf("%d\n", sum_array[curr_ind].total);
    return NULL;
}

这可能在循环之后仍然存在错误共享,但这是每个线程一次。线程创建开销 比单个缓存未命中更重要。当然,您可能希望有一个实际执行一些耗时的循环,因为您当前的代码可以优化为:

static void *worker(void * ind) {
    const int curr_ind = *(int *) ind;
    int localsum = 10 * sum_array[curr_ind].my_num;
    sum_array[curr_ind].total += localsum;
    printf("%d\n", sum_array[curr_ind].total);
    return NULL;
}

其运行时间肯定printf()中的线程创建和同步主导。

【讨论】:

    猜你喜欢
    • 2016-09-27
    • 2015-05-25
    • 1970-01-01
    • 2014-09-04
    • 2018-04-18
    • 2011-04-10
    • 2019-03-07
    • 2012-01-10
    • 1970-01-01
    相关资源
    最近更新 更多