使稀疏矩阵快速乘法

时间:2015-02-07 09:39:15

标签: c++ multithreading matrix

代码是使用C ++ 11编写的。每个过程都有两个矩阵数据(稀疏)。可以从enter link description here

下载测试数据

测试数据包含2个文件:a0(稀疏矩阵0)和a1(稀疏矩阵1)。文件中的每一行都是“i j v”,表示稀疏矩阵Row 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。我的电脑信息:Intel(R)Core(TM)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 ;
}

2 个答案:

答案 0 :(得分:0)

大多数情况下,使用稀疏矩阵时,使用比您拥有的嵌套映射更高效的表示。典型的选择是压缩稀疏行(CSR)或压缩稀疏列(CSC)。有关详细信息,请参阅https://en.wikipedia.org/wiki/Sparse_matrix

答案 1 :(得分:0)

您尚未指定您希望运行示例的时间或您希望运行的平台。这些是本例中的重要设计约束。

我可以考虑几个方面来提高效率: -

  1. 改善数据存储方式
  2. 改善多线程
  3. 改进算法
  4. 第一点是针对系统存储稀疏数组和接口的方式,以便能够读取数据。当速度不重要时,嵌套的unordered_maps是一个不错的选择,但可能有更多特定的数据结构可用于解决此问题。最好的情况是,您可以找到一个库,它提供了比嵌套地图更好的存储数据的方法,最糟糕的是,您可能需要自己想出一些东西。

    第二点是指语言支持多线程的方式。多线程系统的原始规范旨在与平台无关,可能会错过某些系统可能具有的便利功能。确定要定位的系统并使用操作系统线程系统。您可以更好地控制线程的工作方式,可能会减少开销,但会失去跨平台支持。

    第三点需要一些工作。在给定数据性质的情况下,您是否真正成倍增加基质的方式。我不是这方面的专家,但需要考虑,但需要花费一些力气。

    最后,您可以始终非常具体地了解您正在运行的平台,并进入装配编程的世界。现代CPU是复杂的野兽。他们有时可以并行执行操作。例如,您可以执行SIMD操作或执行并行整数和浮点运算。这样做需要深入了解正在发生的事情,并且有一些有用的工具可以帮助您。英特尔确实有一个名为VTune的工具(现在可能还有其他东西)可以分析代码并突出潜在的瓶颈。最终,您将希望消除CPU空闲等待发生某些事情的算法区域(如从RAM等待数据),或者通过为CPU执行其他操作或改进算法(或两者)。

    最终,为了提高整体速度,您需要知道什么在减慢速度。这通常意味着知道如何分析代码并理解结果。 Profilers是这方面的通用工具,但也有特定于平台的工具。

    我知道这不是你想要的,但是快速编写代码非常困难且非常耗时。