【问题标题】:Bitwise operator on two large sparse vectors without looping?没有循环的两个大型稀疏向量的按位运算符?
【发布时间】:2019-06-18 19:50:47
【问题描述】:

我有三个大的任意稀疏布尔向量,它们的大小都相同——比如:pool1pool2intersection_of_other_pools。我有兴趣执行按位运算符。如果我能做到,那就太好了:intersection_of_other_pools |= pool1 | pool2,但这似乎不是一个选择——据我所知。

由于所有这些向量的大小都非常大,而 pool1pool2 非常稀疏,我会对在这些向量上执行按位运算的方法感兴趣没有循环。我知道std::vector<bool> 的底层实现只是一个位数组,这让我相信无需循环就可以做到这一点。

我对以速度为名的奇怪的按位hacky解决方案持开放态度。

当然,如果执行此操作的最快方法(或唯一方法)只是循环,那么我也很乐意接受它作为答案。

我已经检查了valarray 作为矢量的潜在替代品,但我无法判断它是循环还是正在执行一些神奇的按位运算。但理想情况下,我不想更改现有的代码库。

【问题讨论】:

  • 我认为无论如何你都无法在没有某种循环的情况下进行操作。
  • 假设向量中有超过 512 位,最新的 MMX 仅支持 256 位操作。在您看来,编译器将如何实现该操作?
  • 迭代 256 位块是否比迭代每个单独的项目更快?如果是这样,我想我会遍历这些块并对这些块执行按位操作。
  • 循环有什么问题?这不是 Python。没有神奇的非循环操作可以同时应用于千兆字节的内存。请注意:在后台,std::vector<bool> 是一个 bytes 数组。计算机没有单独的位。您实际上要解决什么问题,因为没有理由害怕循环。

标签: c++ std


【解决方案1】:

不要对稀疏数组使用std::vector<bool> 或类似名称。

真正的稀疏数组应该能够跳过大段。

将您的数据编码为块标头,以字节为单位说明区域的长度。使用长度中的全 1 表示“长度字段的长度是长度的两倍并遵循”,递归。

所以 0xFF0100 表示后面有一个长度为 512 的块。 (您可以通过不允许 0 或 1-254 做得更好,但这是舍入误差)。

“全 0”块与混合 1 和 0 块的交替块。

不要直接读取区块头;使用 memcpy 进入对齐存储。

一旦你有了这个,你的|& 操作更像是一个缝合而不是按位操作。只有在两者都有非零块的极少数情况下,您才真正进行按位工作。

在完成& 之后,您可能想要检查是否有任何非 0 区域实际上全为 0。

这假设了一个非常稀疏的位域。就像,每 10000 个设置 1 个位是一个典型案例。如果稀疏的意思是“十分之一”,那么只需使用uint64_t 或其他东西的向量。

【讨论】:

    【解决方案2】:

    实现为std::vector<uint64_t>,您的cpu 可能会非常快地在这些上执行按位“或”。它们将是内存对齐的,因此缓存友好。 循环并没有你想象的那么糟糕,因为无论如何在不同的数据结构上都会有一个隐藏的隐式循环。

    如果它非常稀疏(std::set_intersection 进行匹配

    【讨论】:

      猜你喜欢
      • 2010-11-26
      • 2016-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-23
      • 2017-03-26
      • 1970-01-01
      相关资源
      最近更新 更多