比较阵列数组

时间:2015-10-25 14:10:32

标签: arrays parallel-processing f# simd

所以我有两个数组,a和b大小各异,包含相同长度的子数组,两者的类型与子数组相同(例如float)。

我希望在数组a的子数组中找到b中子数组的所有匹配项。

现在我正在寻找更快或更好的方法(也许是CUDA或SIMD编码)。

目前我有类似(F#)的东西:

let mutable result = 0.0
for a in arrayA do:
 for b in arrayB do:
  if a = b then 
   result <- result + (a |> Array.sum)

我的数组包含大约5百万个元素,数组b包含大约3000个元素。因此我的性能相关问题。

2 个答案:

答案 0 :(得分:2)

通过将大型数组拆分为更小的数组并并行执行相等性检查,可以节省一些时间来比较大型数组。

此块功能直接取自F# Snippets

let chunk chunkSize (arr : _ array) = 
query {
  for idx in 0..(arr.Length - 1) do
  groupBy (idx / chunkSize) into g
  select (g |> Seq.map (fun idx -> arr.[idx]))
}

然后像这样比较数组。我选择将每个数组拆分成4个较小的块:

let fastArrayCompare a1 a2 = async {
let! a =
  Seq.zip (chunk 4 a1) (chunk 4 a2)
  |> Seq.map (fun (a1',a2') -> async {return a1' = a2'}) 
  |> Async.Parallel
return Array.TrueForAll (a,(fun t -> t))}

显然你现在在数组拆分时增加了一些额外的时间,但是这次你应该补充很多非常大的数组比较。

答案 1 :(得分:2)

您使用强力算法来解决问题。假设 A B 分别具有 N M 的大小,则检查相等的每个小数组是< em> K 元素长。在最坏情况下,您的算法需要 O(NMK)时间,并且在最佳情况下, O(NM + ZK),假设匹配数为 Z (可能达到 NM )。

请注意,每个小数组本质上都是一个字符串。你有两组字符串,你想要检测它们之间的所有相等的对。

hash table可以解决此问题。使用 O(M)单元格创建哈希表。在此表中,存储数组 B 的字符串而不重复。添加 B 中的所有字符串后,从 A 中迭代字符串并检查它们是否存在于哈希表中。该解决方案可以实现为随机的,平均时间复杂度为 O((M + N)K),这是输入数据大小的线性。

此外,您可以以非随机方式解决问题。将所有字符串放入单个数组 X 并对其进行排序。在排序过程中,在 B 的所有相等字符串之后放入来自 A 的字符串。请注意,您应该记住 X 的哪些字符串来自哪个数组。您可以使用一些快速comparison sort,也可以使用radix sort。在后一种情况下,排序是在线性时间内完成的,即在 O((M + N)K)中进行。

现在所有常见字符串都连续存储在 X 中。您可以迭代 X ,保持 B 中的字符串集等于当前处理的字符串。如果您看到与前一个字符串不同的字符串,请清除该字符集。如果字符串来自 B ,请将其添加到集合中。如果它来自 A ,请记录它等于 B 中的元素集。这是 X 的单次传递,每个字符串的 O(K)时间,因此需要 O((M + N)K)时间总的来说。

如果字符串的长度 K 不小,则可以向字符串操作添加矢量化。在哈希表方法的情况下,大部分时间将用于计算字符串哈希。如果选择polynomial hash modulo 2 ^ 32,则可以使用SSE2对其进行矢量化。此外,您需要快速字符串比较,这可以通过memcmp函数完成,该函数也可以很容易地进行矢量化。对于排序解决方案,您只需要字符串比较。另外,你可能想要实现一个基数排序,这是不可能矢量化的,我担心。

两种方法的高效并行化并不是很简单。对于第一个算法,您需要一个并发哈希表。实际上,那里甚至还有一些lock-free hash tables。对于第二种方法,您可以并行化第一步(快速排序很容易并行化,基数排序不是)。如果没有太多相等的字符串,第二步也可以并行化:你可以将数组 X 分成几乎相等的部分,只在两个不同的字符串之间分解。