我正在尝试使用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)。奇怪的是,尽管此函数在程序中被多次调用,但仅其中一些会受到影响。
我已包含头文件<omp.h>
。我不确定这是否归因于Eigen库,该库随处可见。我尝试按照official site中的建议定义EIGEN_DONT_PARALLELIZE
并调用Eigen::initParallel()
,但这无济于事。
奇怪的是,我什至根本不包含任何parallel pragma
,处理OpenMP函数应该没有任何开销吗?为什么它仍然放慢速度?
答案 0 :(得分:1)
因此,解决方案是例如通过设置OMP_NUM_THREADS环境变量,将OpenMP线程的数量限制为物理内核的数量。您还可以通过在编译时定义宏EIGEN_DONT_PARALLELIZE
来禁用Eigen的多线程。
doc中的更多信息。
有关超线程如何降低性能的更多详细信息: 使用超线程,您可以在单个内核上以交错方式运行两个线程。他们交替执行每条指令。如果您的线程使用的CPU资源不少于一半(就计算而言),那将是一个胜利,因为您将利用更多的计算单元。但是,如果单个线程已经使用了100%的计算单元(如优化矩阵矩阵产品的情况),则由于1)管理两个线程的自然开销和2)因为L1而导致性能下降缓存现在由两个不同的任务共享。矩阵矩阵内核在设计时考虑到了精确的L1容量。有两个线程,您的L1缓存几乎无效。这意味着您没有访问大多数时间的非常快的L1高速缓存,而是访问了慢得多的L2高速缓存,从而导致性能大幅下降。与Linux和Windows不同,在OSX上我看不到这种性能下降,这很可能是因为如果CPU太忙了,系统便可以取消第二线程的调度。