压缩稀疏列矩阵的并行构造

时间:2016-06-04 13:10:19

标签: python matrix parallel-processing scipy sparse-matrix

具有m列的压缩稀疏列矩阵(csc),定义为here

它有三个数组:Data,Indices和Indptr。

  • indptr是1D数组,大小为m + 1
  • indices是矩阵的所有非零条目的行索引。
  • indices [indptr [i]:indptr [i + 1]]是第i列的所有非零条目的行索引。这些非零项的值在数据[indptr [i]:indptr [i + 1]]
  • 中定义

具有n行的压缩稀疏行(csr)矩阵具有类似的definition

问题:

Cpu适用于n个作业。第p个作业产生具有n行和m列的稀疏矩阵(csc或csr)的第q行的数据。 p和q不必相等,只要哪个作业对应于记录哪一行。

如何有效地从作业中收集数据并生成csc矩阵?

目前的解决方案:

方法A.

  1. 创建了名为shared_data,shared_indices,shared_indptr(它们定义了csr矩阵),shared_ordering的空数组。 shared_data和shared_indices的大小取决于非零条目数量的估计。设置shared_indptr [0] = 0.这些数组由作业共享。
  2. 作业共享非零条目数(count_nz)和输入csr矩阵(count_row)的行数的计数器。
  3. 访问1和2中描述的资源需要获取锁定。
  4. 每个作业都知道它尝试放入csr矩阵的非零条目(job_indices)的列索引。它还知道非零条目(job_data)的值以及非零条目(job_nz)的数量。
  5. 每个工作都试图获得锁定。
  6. 获得锁定后,工作会执行以下操作。然后释放锁定
  7. after acquiring lock
    shared_data[counter_nz:(counter_nz + job_nz)] = job_data
    shared_indices[counter_nz:(counter_nz + job_nz)] = job_indices
    shared_indptr[count_row + 1] = shared_indptr[count_row] + job_nz
    shared_ordering[counter_row] = job_id
    counter_nz  += job_nz
    counter_row += 1
    release lock.
    
    1. 通过包装共享数组创建一个csr矩阵对象(如scipy.sparse.csr_matrix((shared_data,shared_indices,shared_indptr))。

    2. 将csr转换为csc矩阵(目标是制作csc矩阵而不是csr矩阵......)

    3. 这种方法的问题是锁实际上会杀死性能。就像十几个人试图抓住相同的工具来处理项目的不同部分。

      方法B

      每个cpu都可以处理m个作业,并使用A中描述的方法构建csc矩阵。这种方式使用了更多的内存,但矩阵的生成速度更快。

      方法C

      一个cpu在m作业上工作并构建一个csc矩阵。这比12 cpu机器上的接近A大约多两倍的时间。

      方法D(2016年6月5日更新)

      有cpu核心。最终csc矩阵和shared_ordering数组的shared_data,shared_indptr和shared_indices数组由所有cpu内核共享。无需锁定。每个cpu核心运行一个子进程(而不是一直启动和停止子进程)每个cpu核心进程m个作业的1 / s和每个cpu使用A中描述的方法构造一个小的csc矩阵。在cpu完成所有作业之后,它将三个大小的数据广播到其他cpus的三个小csc矩阵阵列中。一旦cpu从所有其他cpu接收到此信息(有s-1信息从其他cpus接收),它将计算其小csc矩阵的三个数组在最终csc矩阵的三个数组中的位置。然后它将其小csc矩阵复制到共享csc矩阵。

      之后,cpu向其他cpu发送信号。一旦cpu收到s-1信号,它就会开始下一个周期。 (可能不需要第二次同步。)

      希望同步将比A中的锁更少浪费时间。并且这种方法从1个cpu核心线性扩展到12个cpu核心。如果有人有这方面的经验,请随时提出建议。

      方法D的缺点是它使用的内存量是内存所需数量的两倍来保存最终的csc矩阵。内存的一半用于共享阵列。另一半被小csc矩阵的所有数组使用......这似乎没问题。在方法B中,每个cpu只有大约1 / s的整个内存可供使用。方法D更好。

      可以将小型matrics的数组保留为缓冲区,因此不必浪费时间来重新创建numpy数组。

      我正在尝试使用B并优化每个作业的每个CPU内核的内存使用量。但是如果方法A可以避免通过更智能的锁设计来杀死性能,我会使用A ......我想我会选择D. 相关问题: Efficiently populate SciPy sparse matrix from subset of dictionary

1 个答案:

答案 0 :(得分:2)

听起来你已经创建了一个csr矩阵,然后直接修改了3个属性数组。而这是通过附加到现有数组来实现的。虽然我操纵了csr的data属性,但我没有直接改变其他属性的经验。似乎很难可靠地做,并且没有稀疏代码反对。

另一种方法是首先构造3个数组,然后构造csr。而不是每行使用np.append,而是附加到列表,并使用np.concatenate加入它们。

如果您使用coo输入样式,则可以按任意顺序追加行。一项工作不必知道前一行中有多少项目。

coo样式输入对于有限元刚度矩阵特别好,其中元素的矩阵重叠。 coo输入可以直接转到csc

我最近讨论过从块构建稀疏矩阵: https://stackoverflow.com/a/37040831/901925

tile operation to create a csr_matrix from one row of another csr_matrix

lil适用于增量工作,因为属性是列表的两个对象数组 - 每行一个列表。因此更新只需要用新列表替换两个空列表 - 指针操作。但我认为lilcsccoo慢到csc(但我还没有测试过它)。