【问题标题】:Parallel iteration over array with step size greater than 1对步长大于 1 的数组进行并行迭代
【发布时间】:2019-07-29 03:06:40
【问题描述】:

我正在开发一个用于进行信念传播立体视觉的练习程序。这里的相关方面是我有一个相当长的数组表示图像中的每个像素,并且希望在 for 循环的每次迭代中对数组中的每个 second 条目执行操作 -前一半的条目,然后在下一次迭代中的另一半(这来自 Felzenswalb 和 Huttenlocher 在他们 2006 年的论文“早期视觉的有效信念传播”中描述的优化。)因此,您可以将其视为具有运行多次的外部 for 循环,对于该循环的每次迭代,我都会迭代数组中一半的条目。

我想像这样并行化遍历数组的操作,因为我相信这样做是线程安全的,当然可能更快。该操作涉及更新表示相邻像素的数据结构内的值,这些像素本身并不用于外部循环的给定迭代。最初我只是一次性遍历整个数组,这意味着执行此操作相当简单——我需要做的就是将.Parallel 放在Array.iteri 之间。但是,更改为对第二个数组条目进行操作比较棘手。

为了从简单地遍历每个条目进行更改,我从 Array.iteri (fun i p -> ... 到使用 for i in startIndex..2..(ArrayLength - 1) do,其中 startIndex 是 1 或 0,具体取决于我最后使用的那个(通过切换布尔值来控制)。这意味着虽然我不能简单地使用非常好的.Parallel 来让事情并行运行。

我无法找到任何关于如何在 .NET 中实现步长大于 1 的并行 for 循环的具体信息。我能找到的最好的是 a paragraph in an old MSDN document on parallel programming in .NET,但那段只是含糊其辞关于在循环体内转换索引的声明。我不明白那里的意思。

我查看了Parallel.ForParallel.ForEach,以及creating a custom partitioner,但似乎没有一个包含更改步长的选项。

我想到的另一个选择是使用序列表达式,例如

let getOddOrEvenArrayEntries myarray oddOrEven =
    seq {
        let startingIndex =
            if oddOrEven then
                1
            else
                0
        for i in startingIndex..2..(Array.length myarray- 1) do
            yield (i, myarray.[i])
    }

然后使用来自ParallelSeqPSeq.iteri,但我不确定它是否能与.NET Core 2.2 一起正常工作。 (请注意,目前至少,我需要知道数组中给定元素的索引,因为它在处理过程中用作另一个数组的索引)。

如何并行迭代数组的每个第二个元素? IE。使用大于 1 的步长迭代数组?

【问题讨论】:

  • 如果不进行测量,通常几乎不可能知道一种解决方案是否比另一种解决方案更高效。 (当然,除非一个是 O(N),另一个是 O(N^2),但我说的是相同大 O 量级的解决方案)。我怀疑任何人都能够给你一个好的答案,除非他们已经遇到了你的确切情况并进行了基准测试。换句话说,github.com/dotnet/BenchmarkDotNet 可能是您获得答案的方式。
  • @Jarak 因为你只关心 even 个像素,你可以在 Parallel.For 中从 0 迭代到 array.Length/2。另一种选择是使用 PLINQ 的 Where 重载,它接受、索引和过滤掉奇数位置。这样你也可以并行搜索
  • @Jarak 至于更改步长,将索引从0 增加到N step 与从0 迭代到N/step 并将索引乘以相同step使用前
  • @Jarak 确保你 do 对你的代码进行基准测试。访问每一个第二个像素最终会将所有数据加载到 CPU 的缓存中。您仍将只处理一半像素,但不会获得预期的 4x/8x 改进。另一方面,如果您根据索引(奇数/偶数)在循环内执行不同的操作,您将只加载一次数据
  • 我还鼓励测量性能并将并行性能与简单但高效的顺序循环进行比较。有时抽象的开销是如此之大,以至于顺序循环结束得更快。

标签: .net-core f# parallel.foreach parallel.for


【解决方案1】:

您可以尝试PSeq.mapi,它不仅提供序列项作为参数,还提供项的索引。 这是一个小例子

let res = nums
                |> PSeq.mapi(fun index item -> if index % 2 = 0 then item else item + 1)

您也可以查看此sampling snippet。请务必将Seq 替换为PSeq

【讨论】:

  • 谢谢。仅供参考,在查看您链接到的采样 sn-p 时,我偶然发现了 another one,这似乎做同样的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-15
  • 1970-01-01
  • 2015-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多