簡體   English   中英

使稀疏矩陣快速相乘

[英]Make Sparse Matrix Multiply Fast

該代碼使用C ++ 11編寫。 每個進程都有兩個矩陣數據(稀疏)。 可以從此處的輸入鏈接描述中下載測試數據

測試數據包含2個文件:a0(稀疏矩陣0)和a1(稀疏矩陣1)。 文件中的每一行都是“ ijv”,表示稀疏矩陣行i,列j的值為v。i,j,v都是整數。

使用c ++ 11 unordered_map作為稀疏矩陣的數據結構。

unordered_map<int, unordered_map<int, double> > matrix1 ;
matrix1[i][j] = v ; //means at row i column j of matrix1 is value v;

以下代碼耗時約2分鍾。 編譯命令是g++ -O2 -std=c++11 ./matmult.cpp

g ++版本是4.8.1,Opensuse 13.1。 我的電腦的信息:英特爾®酷睿™i5-4200U CPU @ 1.60GHz,4G內存。

#include <iostream>
#include <fstream>
#include <unordered_map>
#include <vector>
#include <thread>

using namespace std;

void load(string fn, unordered_map<int,unordered_map<int, double> > &m) {
  ifstream input ;
  input.open(fn);
  int i, j ; double v;
  while (input >> i >> j >> v)  {
    m[i][j] = v;
  }
}

unordered_map<int,unordered_map<int, double> > m1;
unordered_map<int,unordered_map<int, double> > m2;
//vector<vector<int> > keys(BLK_SIZE);

int main() {
  load("./a0",m1);
  load("./a1",m2);

  for (auto r1 : m1) {
    for (auto r2 : m2) {
      double sim = 0.0 ;
      for (auto c1 : r1.second) {
        auto f = r2.second.find(c1.first);
        if (f != r2.second.end()) {
           sim += (f->second) * (c1.second) ;
        }
      }
   }
  }
  return 0;
}

上面的代碼太慢了。 如何使其運行更快? 我使用多線程。 新代碼如下,編譯命令為g++ -O2 -std=c++11 -pthread ./test.cpp 大約花了1分鍾。 我希望它更快。

如何使任務更快? 謝謝!

#include <iostream>
#include <fstream>
#include <unordered_map>
#include <vector>
#include <thread>

#define BLK_SIZE 8

using namespace std;

void load(string fn, unordered_map<int,unordered_map<int, double> > &m) {
  ifstream input ;
  input.open(fn);
  int i, j ; double v;
  while (input >> i >> j >> v)  {
    m[i][j] = v;
  }
}

unordered_map<int,unordered_map<int, double> > m1;
unordered_map<int,unordered_map<int, double> > m2;
vector<vector<int> > keys(BLK_SIZE);

void thread_sim(int blk_id) {
  for (auto row1_id : keys[blk_id]) {
    auto r1 = m1[row1_id];
    for (auto r2p : m2) {
      double sim = 0.0;
      for (auto col1 : r1) {
        auto f = r2p.second.find(col1.first);
        if (f != r2p.second.end()) {
          sim += (f->second) * col1.second ;
        }
      }
    }
  }
}

int main() {

  load("./a0",m1);
  load("./a1",m2);

  int df = BLK_SIZE - (m1.size() % BLK_SIZE);
  int blk_rows = (m1.size() + df) / (BLK_SIZE - 1);
  int curr_thread_id  = 0;
  int index = 0;
  for (auto k : m1) {
    keys[curr_thread_id].push_back(k.first);
    index++;
    if (index==blk_rows) {
      index = 0;
      curr_thread_id++;
    }
  }
  cout << "ok" << endl;
  std::thread t[BLK_SIZE];
  for (int i = 0 ; i < BLK_SIZE ; ++i){
    t[i] = std::thread(thread_sim,i);
  }
  for (int i = 0; i< BLK_SIZE; ++i)
    t[i].join();

  return 0 ;
}

在大多數情況下,使用稀疏矩陣會比使用嵌套映射使用更有效的表示形式。 典型選擇是壓縮稀疏行(CSR)或壓縮稀疏列(CSC)。 有關詳細信息,請參見https://en.wikipedia.org/wiki/Sparse_matrix

您尚未指定示例運行的時間或希望運行平台的時間。 這些是此示例中的重要設計約束。

我可以考慮改善以下幾個方面的效率:-

  1. 改善數據存儲方式
  2. 改善多線程
  3. 改進算法

第一點針對系統存儲稀疏數組和接口以使數據能夠被讀取的方式。 如果速度並不重要,但是可以使用更具體的數據結構來解決此問題,則嵌套的unordered_maps是一個不錯的選擇。 最好的情況是,您可能會找到一個比嵌套地圖提供更好的數據存儲方式的庫,最壞的情況下,您可能必須自己准備一些東西。

第二點涉及該語言支持多線程的方式。 多線程系統的原始規范旨在獨立於平台,並且可能會錯過某些系統可能具有的便捷功能。 確定要定位的系統並使用OS線程系統。 您將對線程的工作方式有更多控制,可能會減少開銷,但會失去跨平台支持。

第三點需要一些工作。 在給定數據性質的情況下,乘矩陣的方法確實是最有效的方法。 我不是這些方面的專家,但是可以考慮,但是需要一些努力。

最后,您始終可以非常清楚自己所運行的平台,並進入匯編程序設計領域。 現代CPU是復雜的野獸。 他們有時可以並行執行操作。 例如,您可以執行SIMD運算或並行整數和浮點運算。 這樣做確實需要對正在發生的事情有深刻的了解,並且有一些有用的工具可以幫助您。 英特爾確實有一個稱為VTune的工具(現在可能是其他名稱),該工具可以分析代碼並突出顯示潛在的瓶頸。 最終,您將想要消除CPU閑置等待某些事情發生的算法區域(例如,等待來自RAM的數據),方法是尋找其他可以讓CPU進行的事情或改進算法(或兩者)。

最終,為了提高整體速度,您需要了解導致速度下降的原因。 這通常意味着知道如何分析代碼並了解結果。 探查器是用於此的通用工具,但也有特定於平台的工具。

我知道這不是您想要的,但是快速編寫代碼確實非常困難且非常耗時。

暫無
暫無

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

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