首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >OpenMP减慢了不相关的串行循环

OpenMP减慢了不相关的串行循环
EN

Stack Overflow用户
提问于 2021-02-15 18:25:29
回答 1查看 121关注 0票数 2

我有两个无关的for循环,一个是串行执行的,另一个是使用OpenMP并行构造执行的。

我使用的OpenMP-Thread越多,下一个串行代码就会变得越慢。

代码语言:javascript
运行
复制
class Foo {
public:
    Foo(size_t size) {
        parallel_vector.resize(size, 0.0);
        serial_vector.resize(size, 0.0);
    }

    void do_serial_work() {
        std::mt19937 random_number_generator;
        std::uniform_real_distribution random_number_distribution{ 0.0, 1.0 };

        for (size_t i = 0; i < serial_vector.size(); i++) {
            serial_vector[i] = random_number_distribution(random_number_generator);
        }
    }

    void do_parallel_work() {
#pragma omp parallel for
        for (auto i = 0; i < parallel_vector.size(); ++i) {
            for (auto integration_steps = 0; integration_steps < 30; integration_steps++) {
                parallel_vector[i] += (0.05 - parallel_vector[i]) / 30.0;
            }
        }
    }

private:
    std::vector parallel_vector;
    std::vector serial_vector;
};

void test_with_size(size_t size, int num_threads) {
    std::cout << "Testing with " << num_threads << " and size: " << size << "\n";
    omp_set_num_threads(num_threads);

    Foo foo{ size };

    long long total_dur_1 = 0;
    long long total_dur_2 = 0;

    for (auto i = 0; i < 500; i++) {
        const auto tp_1 = std::chrono::high_resolution_clock::now();
        foo.do_serial_work();
        
        const auto tp_2 = std::chrono::high_resolution_clock::now();
        foo.do_parallel_work();

        const auto tp_3 = std::chrono::high_resolution_clock::now();
        const auto dur_1 = std::chrono::duration_cast(tp_2 - tp_1).count();
        const auto dur_2 = std::chrono::duration_cast(tp_3 - tp_2).count();

        total_dur_1 += dur_1;
        total_dur_2 += dur_2;
    }

    std::cout << total_dur_1 << "\t" << total_dur_2 << "\n";
}

int main(int argc, char** argv) {
    test_with_size(100000, 1);
    test_with_size(100000, 2);
    test_with_size(100000, 4);
    test_with_size(100000, 8);

    return 0;
}

速度减慢发生在我的本地机器上,这是一台Win10笔记本电脑,配备有4核英特尔酷睿i7-7700和24 GB的超线程内存。该编译器是VisualStudio 2019中的最新版本。在RelWithDebugMode中编译(来自CMake,包括/O2/openmp)。

当我使用一台更强大的机器时就不会出现这种情况,一台CentOS 8配备2x英特尔至强白金9242,每台都有48核,没有超线程,内存为769 GB。编译器是gcc/8.3.1。编译时使用g++ --std=c++17 -O3 -fopenmp

Win10 i7-7700上的计时:

代码语言:javascript
运行
复制
Testing with 1 and size: 100000
3043846 10536315
Testing with 2 and size: 100000
3276611 5350204
Testing with 4 and size: 100000
3937311 2735655
Testing with 8 and size: 100000
5002727 1598775

在CentOS 8上,2x至强白金9242:

代码语言:javascript
运行
复制
Testing with 1 and size: 100000
727756  4111363
Testing with 2 and size: 100000
731649  2069257
Testing with 4 and size: 100000
734019  1056157
Testing with 8 and size: 100000
752584  544373

所以我最初的想法是“缓存上的压力太大了”。然而,当我从并行部分中删除除循环之外的几乎所有内容时,速度又慢了一次。

更新了并行部分,并完成了以下工作:

代码语言:javascript
运行
复制
void do_parallel_work() {
#pragma omp parallel for
        for (auto i = 0; i < 8; ++i) {
            //for (auto integration_steps = 0; integration_steps < 30; integration_steps++) {
            //    parallel_vector[i] += (0.05 - parallel_vector[i]) / 30.0;
            //}
        }
    }

已更新并行部分的Win10上的计时:

代码语言:javascript
运行
复制
Testing with 1 and size: 100000
3206293 636
Testing with 2 and size: 100000
3218667 2672
Testing with 4 and size: 100000
3928818 8689
Testing with 8 and size: 100000
5106605 10797

了解OpenMP 2.0标准(VS只支持2.0) (请在此处找到:https://www.openmp.org/specifications/),它在2.7.2.5行7,8中说:

在没有显式default子句的情况下,默认行为与指定了default(shared)时相同。

在2.7.2.4行30:

团队中的所有线程都访问共享变量的相同存储区域。

对我来说,这排除了OpenMP线程复制串行_向量,这是我能想到的最后一个解释。

我很高兴对这件事的任何解释/讨论,即使我只是明显错过了一些东西。

编辑:

出于好奇,我也在我的Win10机器上使用WSL进行了测试。运行gcc/9.3.0,计时为:

代码语言:javascript
运行
复制
Testing with 1 and size: 100000
833678  2752
Testing with 2 and size: 100000
762877  1863
Testing with 4 and size: 100000
816440  1860
Testing with 8 and size: 100000
991184  2350

老实说,我不清楚为什么windows可执行文件在同一台机器上运行的时间比linux要长得多(针对VC++的优化/O2是最大的),但有趣的是,同样的工件不会出现在这里。

EN

Stack Overflow用户

回答已采纳

发布于 2021-02-25 04:15:54

默认情况下,Windows上的OpenMP有200ms的自旋锁。这意味着当你离开omp块时,所有的omp工作线程都在旋转,等待新的工作。如果你有许多相邻的omp块,这是有好处的。在您的例子中,线程只消耗CPU功率。

要禁用/控制自旋锁,您有以下几个选项:

  1. 定义环境变量OMP_WAIT_POLICY并将其设置为PASSIVE为了完全禁用自旋锁定,
  2. 切换到英特尔OMP运行时随OneAPI一起提供。然后,您可以通过定义以下内容来完全控制旋转锁定时间KMP_BLOCKTIME环境变量,
  3. 安装Visual Studio 2019预览版(即将正式发布)并使用llvm omp。然后,您还可以通过定义以下内容来控制自旋锁定时间KMP_BLOCKTIME环境变量。
票数 2
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66206296

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档