【问题标题】:How do I speed up this BIT_COUNT query for hamming distance?如何加快汉明距离的 BIT_COUNT 查询?
【发布时间】:2016-01-28 15:36:15
【问题描述】:

我有一个 php 脚本,用于检查从安全摄像头拍摄的 2 张静止照片之间的汉明距离。

该表是具有 240 万行的 mySQL,由一个 Key 和 4 个 INT(10) 组成。 INT(10) 已单独、一起以及与 Key 一起被索引,但我没有重要证据表明任何组合都比其他组合更快。如果您建议,我可以再试一次。

汉明权重是通过将图像转换为8x16像素来计算的,每四分之一的位存储在一列中,pHash0,pHash1...等。

我有两种写法。第一种方法是使用嵌套派生表。从理论上讲,每个派生应该比它的前身有更少的数据来检查。查询是准备好的语句,而 ?字段是我正在检查的文件的 pHash[0-3]。

Select
    `Key`,
    Bit_Count(T3.pHash3 ^ ?) + T3.BC2 As BC3
  From
    (Select
      *,
      Bit_Count(T2.pHash2 ^ ?) + T2.BC1 As BC2
    From
      (Select
        *,
        Bit_Count(T1.pHash1 ^ ?) + T1.BC0 As BC1
      From
        (Select
          `Key`,
          pHash0,
          pHash1,
          pHash2,
          pHash3,
          Bit_Count(pHash0 ^ ?) As BC0
        From
          files
        Where
          Not pHash0 Is Null And
          Bit_Count(pHash0 ^ ?) < 4) As T1
      Where
        Bit_Count(T1.pHash1 ^ ?) + T1.BC0 < 4) As T2
    Where
      Bit_Count(T2.pHash2 ^ ?) + T2.BC1 < 4) As T3
  Where
    Bit_Count(T3.pHash3 ^ ?) + T3.BC2 < 4

第二种方法更直接一些。它只是一次性完成所有工作。

Select
    `Key`,
  From
    files
  Where
    Not pHash0 is null AND
    Bit_Count(pHash0 ^ ?) + Bit_Count(pHash1 ^ ?) + Bit_Count(pHash2 ^
    ?) + Bit_Count(pHash3 ^ ?) < 4

第一个查询在大型记录集上更快,而第二个在较小记录集上更快,但在 240 万条记录上每次比较都不会超过 1-1/3 秒。

您是否发现了一种调整此过程以使其更快的方法?可以快速尝试任何建议,例如更改数据类型或索引。

设置为 Win7x64、MySQL/5.6.6 和 InnoDB、nginx/1.99、php-cgi/7.0.0 并启用了 zend。该脚本是从网页调用的,并且已关闭缓冲以提供即时反馈。

编辑:

如果我将 4 个 32 位整数更改为 1 个二进制 (16) 可能会更好,这会将比较从 4 更改为 1,但我还必须将我的 4 个参数转换为 128 位字符,哪个php不会做。如果有一种快速的方法可以将它们结合起来,可能会挤出更多的时间。

编辑 接受的答案将速度提高了约 500%。我们假设的简要概述:pHash "A" 的位数将始终在 pHash "B" +/- 汉明距离之内。

特别感谢@duskwuff 的坚韧和耐心。干杯@duskwuff!

编辑 这是我最近的查询:

Select
  files.`Key`, 
  Bit_Count(? ^ pHash0) + Bit_Count(? ^ pHash1) +
  Bit_Count(? ^ pHash2) + Bit_Count(? ^ pHash3) as BC
  From
    files FORCE INDEX (bitcount)
  Where
    bitCount Between ? And ? 
  AND Bit_Count(? ^ pHash0) + Bit_Count(? ^ pHash1) +
  Bit_Count(? ^ pHash2) + Bit_Count(? ^ pHash3) <= ?
  ORDER BY Bit_Count(? ^ pHash0) + Bit_Count(? ^ pHash1) +
  Bit_Count(? ^ pHash2) + Bit_Count(? ^ pHash3)

前 4 个“?”代表被检查文件的 4 个 32 位哈希值,接下来的 2 个“?”表示该文件的预先计算的位数 +/- 所需的汉明距离,最后一个“?”表示汉明距离。 ORDER BY 子句仅用于将最接近的匹配项带到顶部,其中 LIMIT 1 子句将返回最佳匹配项。 bitcount 字段上有一个 B-TREE 索引。

240 万个文件的位数分布呈钟形曲线,极端为 3 或 4,中间为 70,000。如果给定一个位数为 64 的文件(这是最坏的情况),则查找汉明距离为 3 内的文件意味着比较 20% 的文件(在我的情况下为 490,000),而查找汉明距离为 0 的文件将比较只有 2.8% 的记录(当然是 70,000 条)。

【问题讨论】:

  • 为什么不是简单的BIT_COUNT(pHash0 &amp; ?) + BIT_COUNT(pHash1 &amp; ?) + BIT_COUNT(pHash2 &amp; ?) + BIT_COUNT(pHash3 &amp; ?)
  • @RickJames 是的,就像我的第二个“更直接”示例中的那样(XOR 而不是 AND)
  • 糟糕,我应该说^,而不是&amp;

标签: php mysql performance


【解决方案1】:

观察到BIT_COUNT(a ^ b) 在下面BIT_COUNT(a)BIT_COUNT(b) 之间的差异决定。 (也就是说,它总是至少等于差值,并且可能更大。)如果您预先计算每行的总位数,您可以使用它来排除总位数太远的行你的目标。更好的是,您可以在该列上创建一个索引,并且该索引将被使用。

我的想法是这样的:

ALTER TABLE files ADD COLUMN totalbits INTEGER;
CREATE INDEX totalbits_index ON files (totalbits);

UPDATE files SET totalbits = BIT_COUNT(pHash1) + BIT_COUNT(pHash2)
                           + BIT_COUNT(pHash3) + BIT_COUNT(pHash4);

SELECT `Key` FROM files WHERE (totalbits BETWEEN … AND …) AND …

注意,有了这个,就没有必要将散列分成四个块。将它们组合回一列会使事情变得更容易。

【讨论】:

  • 感谢您的回复!这不适用于 pHashing……实际上,您必须将一个 pHash 中的所有 128 位与另一个 pHash 中的所有 128 位进行异或,然后计算不同的位以获得汉明距离。我之所以使用 32 位整数,是因为这段代码最初是在 32 位机器上编写的,当时 php 对 64 位列有问题。
  • @alfadog67 是什么让您认为它不起作用?请记住,XOR 本质上是一种差分运算:对于两个值的 XOR 设置少于 4 位,这两个值的“起始”位计数需要在 4 以内。
  • 例如 bitcount(11110000) + bitcount(00001111) = 8,bitcount(11110000 ^ 00001111) 也 = 8,这在您的查询中工作正常。但是,bitcount(11110000) + bitcount(00010111) = 8,并且 bitcount(11110000 ^ 00010111) = 6。这种差异会消除汉明权重。我不想计算设置字节数,而是计算不同字节数。这条消息的数学计算花了我 4 次尝试 ;-)
  • @alfadog67 这两个值应该代表什么?如果它们应该被存储与目标哈希,你应该考虑它们的区别,而不是将它们加在一起......
  • 你是对的。不是区别,或者将它们加在一起,而是对它们进行异或运算。您的解决方案将它们显示为已添加,这会将 128 位减少到不超过 35 位。本质上是 ((2^32)*4) 而不是 (2^(32*4))
猜你喜欢
  • 2014-01-28
  • 2016-12-16
  • 2017-09-10
  • 2012-09-26
  • 2014-09-25
  • 2015-03-21
  • 2012-03-10
  • 1970-01-01
相关资源
最近更新 更多