【问题标题】:Enabling Open MP Support in Visual Studio 2017 slows down codes在 Visual Studio 2017 中启用 Open MP 支持会减慢代码速度
【发布时间】:2019-04-17 09:32:36
【问题描述】:

我正在尝试使用 OpenMP 来加速我的神经网络计算代码。当我使用 Visual Studio 2017 时,我需要在属性表中启用 OpenMP 支持。但是,在我这样做之后,即使我没有在代码中包含任何 #pragma omp,代码的某些部分也会减慢大约 5 倍。

我已经隔离了这些部分,发现这个特定的功能导致了问题:

void foo(Eigen::Matrix<float,3,Eigen::Dynamic> inputPts)
{
    std::vector<Eigen::MatrixXf> activation;
    activation.reserve(layerNo);
    activation.push_back(inputPts);

    int inputNo = inputPts.cols();

    for (int i = 0; i < layerNo - 2; i++)
        activation.push_back(((weights[i]*activation[i]).colwise()+bias[i]).array().tanh());

    activation.push_back(((weights[layerNo - 2]*activation[layerNo - 2]).colwise()+bias[layerNo - 2]));

    val = activation[layerNo - 1]/scalingFactor;

    std::vector<Eigen::MatrixXf> delta;
    delta.reserve(layerNo);

    Eigen::Matrix<float, 1, Eigen::Dynamic> seed;
    seed.setOnes(1, inputNo);
    delta.push_back(seed);

    for (int i = layerNo - 2; i >= 1; i--)
    {
        Eigen::Matrix<float,Eigen::Dynamic,Eigen::Dynamic>
                d_temp = weights[i].transpose()*delta[layerNo - 2 - i],
                d_temp2 = 1 - activation[i].array().square(),
                deltaLayer = d_temp.cwiseProduct(d_temp2);

        delta.push_back(deltaLayer);
    }

    grad = weights[0].transpose()*delta[layerNo - 2];
}

两个 for 循环显着减慢速度(从 ~3ms 到 ~20ms)。奇怪的是,虽然这个函数在程序中被调用了很多次,但只有一部分受到影响。

我已经包含了头文件&lt;omp.h&gt;。我不确定这是否是由于到处都在使用的 Eigen 库。我尝试按照official site 中的建议定义EIGEN_DONT_PARALLELIZE 并调用Eigen::initParallel(),但它没有帮助。

奇怪的是我根本没有包含任何parallel pragma,处理 OpenMP 函数不应该有任何开销吗?为什么它还在减速?

【问题讨论】:

    标签: visual-studio-2017 openmp eigen


    【解决方案1】:

    如果启用 OpenMP,Eigen 的矩阵矩阵产品默认是多线程的。问题可能是以下因素的组合:

    1. 您的 CPU 是超线程的,例如,您有 4 个物理内核能够运行 8 个线程。
    2. OpenMP 不允许知道物理内核的数量,因此 Eigen 将启动 8 个线程。
    3. Eigen 的矩阵-矩阵乘积内核经过全面优化,可利用近 100% 的 CPU 容量。因此,没有空间在单个内核上运行两个这样的线程,并且性能显着下降(缓存污染)。

    因此,解决方案是将 OpenMP 线程数限制为物理内核数,例如通过设置 OMP_NUM_THREADS 环境变量。您还可以通过在编译时定义宏 EIGEN_DONT_PARALLELIZE 来禁用 Eigen 的多线程。

    更多信息请关注doc

    更多关于超线程如何降低性能的细节: 使用超线程,您可以在单个内核上以交错方式运行两个线程。他们交替每条指令。如果您的线程使用的 CPU 资源少于一半(就计算而言),那么这是一个胜利,因为您将利用更多的计算单元。但是,如果单个线程已经在使用 100% 的计算单元(如优化良好的矩阵矩阵乘积),那么您会因为 1) 管理两个线程的自然开销和 2) 因为 L1缓存现在由两个不同的任务共享。矩阵-矩阵内核在设计时考虑了精确的 L1 容量。使用两个线程,您的 L1 缓存几乎变得无效。这意味着您最终不是在大多数时间获取非常快的 L1 缓存,而是访问慢得多的 L2 缓存,因此您会获得巨大的性能下降。与 Linux 和 Windows 不同,在 OSX 上我没有观察到这样的性能下降,很可能是因为如果 CPU 已经太忙,系统能够取消调度第二个线程。

    【讨论】:

    • 感谢您的回复。我添加了omp_set_num_threads(2); Eigen::setNbThreads(1); Eigen::initParallel(); 行(参见link),运行时间恢复正常。不幸的是,即使我添加了#pragma 语句,我的时间也没有改善。猜猜并行线程在我的情况下是不够的。只需要澄清一些事情:超线程是如何导致这个问题的?将 OMP_NUM_THREADS 设置为 2 会给我通常的运行时间,但 4 会减慢代码速度。
    • 这是我在回答中试图解释的内容,我用更多细节扩展了答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-30
    • 2018-11-30
    • 1970-01-01
    • 2019-08-09
    • 1970-01-01
    • 2023-03-08
    相关资源
    最近更新 更多