簡體   English   中英

稀疏x密集矩陣乘法性能低效

[英]sparse x dense matrix multiplication performance under-efficient

背景 :我使用Eigen進行人工神經網絡,其中典型的維度是每層大約1000個節點。 因此,大多數操作是將大小為〜(1000,1000)的矩陣M與大小為1000的向量或一批B向量相乘, 其被表示為大小為Bx1000的矩陣

在訓練神經網絡之后,我正在使用修剪 - 這是一種常見的壓縮技術,最終得到稀疏矩陣(非空參數的密度在10%到50%之間)。

目標 :我想使用稀疏矩陣進行壓縮,其次是性能優化,但這不是主要目標

問題 :我正在比較不同批量大小的稀疏和密集矩陣乘法(僅計算乘法時間)的性能,我觀察以下(使用Eigen 3.2.8,MacBook Pro 64位,沒有open_mp,並使用標准g ++):

  • 當B = 1(矩陣x向量)時 - 密度為10%或30%的稀疏矩陣運算比密集矩陣運算更有效 - 這似乎是預期的結果:執行的操作少得多
  • 對於B = 32:
    • 密集矩陣運算所需的時間只是B = 1所需時間的10倍 - 這很酷 - 它是否顯示出一些矢量化效應?
    • 稀疏矩陣運算所需的時間是B = 1所需時間的67倍 - 這意味着它比獨立處理32個向量的效率低

M稀疏/密集的MxN乘法時間(ms),以及大小為1000xB的N.

相同的數字,但顯示稀疏和密集矩陣的一批不同大小的每個矢量的時間。 當批量大小增加時,我們清楚地看到密集矩陣的時間減少,並且稀疏矩陣的增強顯示出一些錯誤。 B = 1時標准化

代碼 :我對稀疏和密集矩陣使用以下類型:

typedef SparseMatrix<float> spMatFloat;
typedef Matrix<float, Dynamic, Dynamic, RowMajor> deMatRowFloat;

我正在進行基准測試的操作如下:

o.noalias()=m*in.transpose();

其中o是密集矩陣(1000xB), m是密集矩陣(1000x1000)或使用m.sparseView()獲得的相應稀疏矩陣, in是密集矩陣(Bx1000)

完整代碼如下(20個不同隨機矩陣的平均時間,並且每次乘法運行50次) - B = 32且B = 1的時間低於。

歡迎任何反饋/直覺!


batch   1   ratio   0.3 dense   0.32    sparse  0.29
batch   32  ratio   0.3 dense   2.75    sparse  15.01

#include <Eigen/Sparse>
#include <Eigen/Dense>
#include <stdlib.h>
#include <boost/timer/timer.hpp>

using namespace Eigen;
using namespace boost::timer;

typedef SparseMatrix<float> spMatFloat;
typedef Matrix<float, Dynamic, Dynamic, RowMajor> deMatRowFloat;

void bench_Sparse(const spMatFloat &m, const deMatRowFloat &in, deMatRowFloat &o) {
  o.noalias()=m*in.transpose();
}

void bench_Dense(const deMatRowFloat &m, const deMatRowFloat &in, deMatRowFloat &o) {
  o.noalias()=m*in.transpose();
}

int main(int argc, const char **argv) {
  float ratio=0.3;
  int iter=20;
  int batch=32;
  float t_dense=0;
  float t_sparse=0;

  deMatRowFloat d_o1(batch,1000);
  deMatRowFloat d_o2(batch,1000);
  for(int k=0; k<iter; k++) {
    deMatRowFloat d_m=deMatRowFloat::Zero(1000,1000);
    deMatRowFloat d_b=deMatRowFloat::Random(batch,1000);
    for(int h=0;h<ratio*1000000;h++) {
      int i=rand()%1000;
      int j=rand()%1000;
      d_m(i,j)=(rand()%1000)/500.-1;
    }
    spMatFloat s_m=d_m.sparseView();
    {
      cpu_timer timer;
      for(int k=0;k<50;k++) bench_Dense(d_m,d_b,d_o1);
      cpu_times const elapsed_times(timer.elapsed());
      nanosecond_type const elapsed(elapsed_times.system+elapsed_times.user);
      t_dense+=elapsed/1000000.;
    }
    {
      cpu_timer timer;
      for(int k=0;k<50;k++) bench_Sparse(s_m,d_b,d_o2);
      cpu_times const elapsed_times(timer.elapsed());
      nanosecond_type const elapsed(elapsed_times.system+elapsed_times.user);
      t_sparse+=elapsed/1000000.;
    }
  }
  std::cout<<"batch\t"<<batch<<"\tratio\t"<<ratio<<"\tdense\t"<<t_dense/50/iter<<"\tsparse\t"<<t_sparse/50/iter<<std::endl;
}

ggael建議之后的新結果 :我嘗試了不同的可能組合,發現在更改MB RowMajor / ColMajor時確實存在巨大的性能差異。

總結一下,我感興趣做M*B ,其中M是(1000,1000), B是(1000,批處理):我有興趣比較M稀疏/密集的性能和批量增長時的性能。

我測試了3種配置:

  • M密,B密
  • M稀疏,B密
  • M稀疏,B密集,但M * B的乘法是逐列手動完成的

結果如下 - 其中數字是每列的比率時間,B = 32 /時間,B = 1,矩陣M,密度為0.3:

這里

最初報告的問題是更糟糕的情況(M ColMajor,B RowMajor)。 對於(M RowMajor,B ColMajor),在B = 32和B = 1之間有5倍的加速,並且稀疏矩陣的性能幾乎等於密集矩陣。

在Eigen中,對於密集代數,矩陣矢量和矩陣矩陣產品都經過高度優化,並充分利用了矢量化。 正如您所觀察到的,基質矩陣產品具有更高的效率。 這是因為通過增加算術運算次數和存儲器訪問次數之間的比率,以及利用存儲器高速緩存,可以進一步優化矩陣矩陣產品。

然后關於稀疏密集型產品,有兩種策略:

  1. 一次處理密集的右側一列,從而多次掃描稀疏矩陣。 對於此策略,最好使用密集矩陣的列主存儲(右側和結果)。 在Eigen 3.2中,可以通過手動掃描列來模擬此策略。
  2. 僅掃描稀疏矩陣一次,並處理密集右側的行,並產生最嵌套的循環。 這是Eigen 3.2中的默認策略。 在這種情況下,最好使用密集矩陣的行主存儲( Matrix<float,Dynamic,32,RowMajor> )。

最后,在任何一種情況下,您都可以嘗試使用稀疏矩陣的行主要和列主要存儲,並確定稀疏矩陣的策略和存儲順序的哪種組合在您的情況下最有效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM