【问题标题】:Sending data to workers向工作人员发送数据
【发布时间】:2015-11-12 18:08:43
【问题描述】:

我正在尝试创建一段并行代码来加速处理一个非常大的(几亿行)数组。为了并行化,我将我的数据切成 8 个(我的核心数)块,并尝试向每个工作人员发送 1 块。然而,看看我的 RAM 使用情况,似乎每一块都发送给每个工人,有效地将我的 RAM 使用量乘以 8。一个最小的工作示例:

A = 1:16;
for ii = 1:8
    data{ii} = A(2*ii-1:2*ii);
end

现在,当我使用 parfor 将此数据发送给工作人员时,它似乎发送了完整的单元格,而不仅仅是所需的部分:

output = cell(1,8);
parfor ii = 1:8
    output{ii} = data{ii};
end

我实际上在parfor 循环中使用了一些函数,但这说明了这种情况。 MATLAB 是否实际上将完整的单元格data 发送给每个工作人员,如果是,如何使其仅发送所需的部分?

【问题讨论】:

  • 如果您的数据是sliced variable,它将被“切片”,并且只有这些切片将传输给工作人员;你在真实代码中使用切片变量吗?
  • 我在实际代码中使用了元胞数组,如此处所示。我会研究切片变量函数,谢谢。
  • 也许手动进行切片,为每块提交单独的作业:de.mathworks.com/help/distcomp/submit.html
  • 注意:此处parfor 循环后面的gather 是多余的——gather 用于将distributed 数组转换为常规的MATLAB 数组。
  • 对变量进行切片可能绝对是要走的路。在没有看到您的代码的情况下,我们无法告诉您切片是否正确。

标签: matlab parallel-processing parfor spmd


【解决方案1】:

根据我的个人经验,我发现使用parfeval 在内存使用方面比parfor 更好。此外,您的问题似乎更容易破解,因此您可以使用 parfeval 向 MATLAB 工作者提交更多较小的作业。

假设您有workerCnt MATLAB 工作人员,您将处理jobCnt 工作。让data 是一个大小为jobCnt x 1 的元胞数组,它的每个元素对应于函数getOutput 的数据输入,该函数对数据进行分析。然后将结果存储在大小为jobCnt x 1 的单元格数组output 中。

在下面的代码中,作业在第一个for 循环中分配,结果在第二个while 循环中检索。布尔变量doneJobs 表示完成了哪个工作。

poolObj = parpool(workerCnt);
jobCnt = length(data); % number of jobs
output = cell(jobCnt,1);
for jobNo = 1:jobCnt
    future(jobNo) = parfeval(poolObj,@getOutput,...
        nargout('getOutput'),data{jobNo});
end
doneJobs = false(jobCnt,1);
while ~all(doneJobs)
    [idx,result] = fetchnext(future);
    output{idx} = result;
    doneJobs(idx) = true;
end

此外,如果您想节省更多内存,可以进一步采用这种方法。您可以做的是,在获取完成作业的结果后,您可以删除future 的相应成员。原因是这个对象存储了getOutput 函数的所有输入和输出数据,这可能会很大。但是你需要小心,因为删除future 的成员会导致索引移位。

以下是我为这个porpuse写的代码。

poolObj = parpool(workerCnt);
jobCnt = length(data); % number of jobs
output = cell(jobCnt,1);
for jobNo = 1:jobCnt
    future(jobNo) = parfeval(poolObj,@getOutput,...
        nargout('getOutput'),data{jobNo});
end
doneJobs = false(jobCnt,1);
while ~all(doneJobs)
    [idx,result] = fetchnext(future);
    furure(idx) = []; % remove the done future object
    oldIdx = 0;
    % find the index offset and correct index accordingly
    while oldIdx ~= idx
        doneJobsInIdxRange = sum(doneJobs((oldIdx + 1):idx));
        oldIdx = idx
        idx = idx + doneJobsInIdxRange;
    end
    output{idx} = result;
    doneJobs(idx) = true;
end

【讨论】:

    【解决方案2】:

    @m.s 的评论是正确的 - 当 parfor slices 一个数组时,每个工作人员只发送它正在处理的循环迭代所需的切片。但是,您可能会看到 RAM 使用量的增加超出了您最初的预期,因为不幸的是,当数据通过 parfor 通信机制从客户端传递给工作人员时,需要数据副本。

    如果您只需要工作人员的数据,那么最好的解决方案是尽可能只在工作人员上创建/加载/访问它。听起来您追求的是数据并行而不是任务并行,spmd 确实更适合(正如@Kostas 所建议的那样)。

    【讨论】:

      【解决方案3】:

      我建议使用 MATLAB 的spmd 命令。

      您几乎可以像编写非并行实现那样编写代码,还可以通过labindex“system”变量访问当前工作线程。

      看看这里:

      http://www.mathworks.com/help/distcomp/spmd.html

      还有关于 spmdparfor 的 SO 问题:

      SPMD vs. Parfor

      【讨论】:

      • 虽然使用 SPMD 可能有助于代码,但它会绕过问题而不是回答问题。我想知道如何正确地将我的数据发送给工作人员而不会产生可笑的开销,所以我也可以在其他代码片段中使用它。
      • 使用 spmd 或 parfor 在 RAM 使用方面并不重要,但使用 spmd 的代码比 parfor 实现的时间长约 1.2 倍。
      • 如果您使用与提供的示例相同的逻辑,那么您的代码有问题。由于几乎需要对您的代码进行调试才能看到实际错误,因此我建议了一种快速的替代方法来解决您的问题。如果您特别需要 parfor,那么您应该发布整个代码。
      • 我没有收到错误,我只是怀疑数据会发送给所有工作人员并因此成倍增加。因此,问题不是调试我的代码,而是了解 matlab 如何将数据分配给工作人员的工作原理。
      猜你喜欢
      • 2011-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-27
      • 1970-01-01
      • 2017-11-06
      • 1970-01-01
      • 2020-08-28
      相关资源
      最近更新 更多