【问题标题】:OpenMP - Why does the number of comparisons decrease?OpenMP - 为什么比较次数减少?
【发布时间】:2017-02-22 15:23:29
【问题描述】:

我有以下算法:

int hostMatch(long *comparisons)
{
    int i = -1;
    int lastI = textLength-patternLength;
    *comparisons=0;

    #pragma omp parallel for schedule(static, 1) num_threads(1)
    for (int k = 0; k <= lastI; k++)
    {
        int j;
        for (j = 0; j < patternLength; j++)
        {
            (*comparisons)++;
            if (textData[k+j] != patternData[j])
            {
                j = patternLength+1; //break    
            }
        }
        if (j == patternLength && k > i)
            i = k;
    }

    return i;
}

当更改 num_threads 时,我得到以下比较次数的结果:

  • 01 = 9949051000
  • 02 = 4992868032
  • 04 = 2504446034
  • 08 = 1268943748
  • 16 = 776868269
  • 32 = 449834474
  • 64 = 258963324

为什么比较次数不是恒定的?这很有趣,因为比较的数量随着线程数量的翻倍而减半。 (*comparisons)++ 是否存在某种竞争条件,如果变量正在使用,OMP 会跳过增量?

我目前的理解是 k 循环的迭代在线程之间几乎均匀分布。每次迭代都有一个私有整数 j 以及一个整数 k 的私有副本,以及一个添加到比较直到终止的非并行 for 循环。

【问题讨论】:

  • 如果你知道竞态条件是什么,你为什么要在那里激怒它。您甚至说某些变量在线程之间被拆分,并且比较显然不是其中之一,所有线程都相同。

标签: c for-loop parallel-processing openmp


【解决方案1】:

将操作声明为atomic update

#pragma omp atomic update
(*comparisons)++;

请注意,此处的关键部分是不必要的,而且成本更高。 atomic update 可以在任何标量类型的左值表达式的原始二元或一元运算上声明。

但这仍然不是最优的,因为*comparisons 的值需要一直在 CPU 缓存之间移动,并且执行昂贵的锁定指令。相反,您应该使用减少。为此,您需要另一个局部变量,指针在这里不起作用。

int hostMatch(long *comparisons)
{
    int i = -1;
    int lastI = textLength-patternLength;
    long comparisons_tmp = 0;

    #pragma omp parallel for reduction(comparisons_tmp:+)
    for (int k = 0; k <= lastI; k++)
    {
        int j;
        for (j = 0; j < patternLength; j++)
        {
            comparisons_tmp++;
            if (textData[k+j] != patternData[j])
            {
                j = patternLength+1; //break    
            }
        }
        if (j == patternLength && k > i)
            i = k;
    }

    *comparisons = comparisons_tmp;

    return i;
}

附: schedule(static, 1) 似乎是个坏主意,因为这将导致 textData 上的内存访问模式效率低下。把它放在外面,让编译器来做这件事。如果测量表明它没有有效地工作,请给它一些更好的提示。

【讨论】:

    【解决方案2】:

    你自己说过(*comparisons)++; 有竞争条件。它是一个必须序列化的关键部分(我不认为 (*pointer)++ 是原子操作)。

    所以基本上你通过两个线程读取相同的值(即 2)两次,然后都增加它(3)并将其写回。所以你得到的是 3 而不是 4。你必须确保不在并行化函数/循环的本地范围内的变量操作不重叠。

    【讨论】:

    • 如果你修改一个值 afaik 它总是先读取然后计算然后写入。有一些原子操作以保证它们没有竞争条件的方式完成它们的任务,但这些通常被明确地描述为这样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-18
    • 1970-01-01
    • 1970-01-01
    • 2013-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多