C++ CPU 未充分利用

C++ CPU not full utilized

本文关键字:CPU C++      更新时间:2023-10-16

>我在C++遇到了奇怪的性能下降,有人可以帮助我确定问题所在吗?

我正在做一个基于特征库的样本外预测问题。我使用扩展窗口方法来合并新的观察向量,并使用LBFGS方法来最大化似然函数。

具体来说,在 for 循环中,我选择数据mYY = mY.block(0,0,24,756+i) Y 的子集块,并使用这个数据子集通过函数估计参数callLBFGS并将估计值存储在 vP 向量中(我通过引用传递vP)。接下来,我将vP的值复制到结果矩阵mRet中,并将mRet写入csv文件。

我之所以将 WriteCSV 放入循环中,是因为callLBFGS非常耗时。如果优化失败,我想中止程序和调试,但我不想丢失已经优化的估计值,所以我每次完成循环时都会将其写入 csv。我完全看不出将WriteCSV放入循环中的问题,因为callLBFGS通常需要长达 10 分钟的时间,但将 102 x 40 的矩阵写入 csv 通常需要几毫秒。

这是我的问题:我写的callLBFGS函数是使用 OpenMP 的多线程,没有把WriteCSV函数放在循环内,CPU 利用率为 95%-100%,但循环内有WriteCSV函数,CPU 利用率下降到 50%-60%。

这很奇怪,超出了我的知识范围,考虑到callLBFGS消耗的时间比WriteCSV多数千倍。CPU 不应该花太多时间在调度和分叉新线程上。有人可以帮助我确定问题吗?非常感谢!

MatrixXd mY = mData.transpose();
double adFunc;
char *Result = "BFGSEst.csv";
VectorXd vP(102);
MatrixXd Y;
Matrix<double,102,40> mRet;
vP = IniPar.col(0);
for (int i = 0; i < 40; ++i) {
    Y = mY.block(0,0,24,756+i);
    callLBFGS(vP,&adFunc,Y,10000);
    mRet.col(i) = vP;
    WriteCSV(Result,mRet);  /*this is the killer*/
}

我的函数调用LBFGS和WriteCSV看起来像这样:

void callLBFGS(VectorXd &vP, double *adFunc, MatrixXd &Y, int MaxIter);
void WriteCSV(char *filename, MatrixXd X);

编辑*非常感谢您的所有回复。下面是我对WriteCSV的实现,相当幼稚。澄清一下,我说callLBFGS最多需要 10 分钟是每次调用callLBFGS最多需要 10 分钟(多维最小化)。因此,整个循环需要数小时才能完成。我知道如果callLBFGSWriteCSV消耗的时间相似,那么CPU无法充分利用也就不足为奇了。但这里是WriteCSV花费的时间比callLBFGS少得多,我不应该期望在callLBFGS的过程中CPU使用率下降到那么大的程度。

void WriteCSV(char *filename, MatrixXd X){
  ofstream myfile;
  myfile.open (filename);
  for (int i = 0; i < X.rows(); ++i)
  {
      for (int j = 0; j < X.cols(); ++j)
      {
          myfile<<X(i,j);
          if (j!=X.cols()-1)
          {
             myfile<<",";
          }else{
             myfile<<"n";
          }
      } 
  }
  myfile.close();
}

抱歉,这应该只是一个评论,但我还不能

在 MatrixXd<> 中可能会无形地发生惰性评估。

惰性求值可能发生在 WriteCSV(...) 中,再加上必然的单线程 IO 会降低您的总 CPU 利用率。

延迟

或延迟评估可以在任何地方,但可能隐藏在简单的检索或复制语句中,例如:

mRet.col(i) = vP;

myfile<<X(i,j);

其他海报可能都更正确,我赞同将 IO 线程化的建议。

考虑到您按值传递MatrixXd,最快的解决方法是启动一个单独的线程来执行实际写入、分离并立即返回。

作为一个小小的改进,我会写','而不是","但这更像是一种风格。

对于 I/O 操作,您手中没有太多东西,因此您无法对此执行太多操作。因此,请将优化添加到 CPU 绑定进程或进程的 CPU 绑定部分!因为这在很大程度上掌握在你手中。此 writeCSV 函数是一个 I/O 绑定进程,它执行 40 次,而您未将其包含在循环中时执行一次。

我在代码中看到两个问题:

  1. 您正在使用C++流,这些流不以其高性能:)
  2. 而闻名
  3. 您正在等待文件 I/O 完成,然后再开始下一次迭代。

为了解决第一个问题,我会使用一个很好的旧printf()。对于第二个 - 异步 I/O;创建传入矩阵的副本(您已经传递了它的副本),启动 I/O 并返回。