【问题标题】:Performance implications of using Java BigInteger for a huge bitmask将 Java BigInteger 用于巨大位掩码的性能影响
【发布时间】:2012-09-19 15:52:34
【问题描述】:

我们有一个有趣的挑战。我们必须控制对驻留在“bins”中的数据的访问。可能会有数十万个“垃圾箱”。对每个垃圾箱的访问都是单独控制的,但这些限制可以而且可能会重叠。我们正在考虑为每个 bin 分配位掩码中的位置(1、2、3、4 等)。

然后,当用户登录系统时,我们会查看他的安全属性并确定允许他查看哪些 bin。使用该信息,我们为该用户构建了一个位掩码,其中“设置”位对应于他允许查看的 bin 的标识符。所以如果他能看到 bin 1、3 和 4,他的位掩码就是 1101。

所以当用户搜索数据时,我们可以查看返回行的 bin 索引,看看他的位掩码上是否设置了该位。如果他的位掩码设置了该位,我们让他看到该行。我们计划在 Java 中将位掩码存储为 BigInteger

我的问题是:假设索引号不会比 Integer.MAX_INT 大,BigInteger 位掩码是否会扩展到数十万位位置?在 n 可能很大(例如 874,837)的情况下运行 BigInteger.isBitSet(n) 是否需要永远?创建这样的BigInteger 需要永远吗?

其次:如果您有其他方法,我很乐意听到。

【问题讨论】:

  • 可能是BitSet?
  • 也许有不同的解决方案?您已经在说您的解决方案无法扩展。大量内存位图 +(希望)大量用户 = 坏主意。
  • @Banthar 终于用于BitSet...BitSet 的最大问题(我认为)是几乎没有方法可以与BitSet 相互转换,因此它是没用那么多 - 我上次查看 Java API 时使用了零次。
  • @Augusto,我没有说它不能扩展。我测试了多达 2,000 个位掩码位置,并且能够在 0.2 秒内运行一百万个随机是/否(是位 x 设置)测试。我想我应该为数百万位的位置做一些性能测试。 (对我来说是-1)。我的问题更倾向于使用 BigInteger 来做这件事有多糟糕。它是记忆猪吗?它是否在性能下降的地方达到顶峰?等
  • @owlstead,它在 Java 7 中得到了改进。例如,您现在可以从字节数组创建 BitSet

标签: java biginteger bitmask


【解决方案1】:

如果您不经常更改 BigInteger,它应该会很快。

一个更明显的选择是BitSet,它是为这类事情设计的。对于查找位,我怀疑性能是相似的。对于创建/修改,使用 BitSet 会更有效。

注意:PaulG 评论说区别是“令人印象深刻”并且 BitSet 更快。

【讨论】:

  • 谢谢彼得,这就是我要去的方向。我对 BitSet 做了一些研究,与 BigInteger 相比的性能优势令人印象深刻。
【解决方案2】:

Java 有一个更方便的类,称为BitSet

您不需要检查该位是否在循环中设置:您可以制作一个掩码,按位使用and,并查看结果是否为非空来决定是授予还是拒绝访问:

BitSet resourceAccessMask = ...
BitSet userAllowedAccessMask = ...
BitSet test = (BitSet)resourceAccessMask.clone();
test.and(userAllowedAccessMask);
if (!test.isEmpty()) {
    System.out.println("access granted");
} else {
    System.out.println("access denied");
}

我们在我以前的公司中使用过这个类,它的性能对于我们的目的来说是可以接受的。

【讨论】:

  • 感谢您的示例代码,我希望我能接受两个答案,因为您只落后彼得几分钟。 :) 下一次。
  • @PaulG 当您输入示例代码时会发生这种情况 ;-) 祝您的项目好运!
【解决方案3】:

您可以为此定义自己的 Java 接口,最初使用 Java BitSet 来实现该接口。

如果您遇到性能问题,或者您需要在很久以后使用,您可以始终提供不同的实现(例如,使用缓存或类似改进的实现)而不更改其余代码。仔细考虑您需要的接口,并选择一个long 索引以确保您可以随时检查它是否在稍后的实现中超出范围(或最初简单地返回“无访问权”)任何index > Integer.MAX_VALUE .

使用BigInteger 不是一个好主意,因为该类不是为特定目的而编写的,更改它的唯一方法是创建一个全新的副本。它在内存使用方面很有效;它在内部使用一个由 64 位长组成的数组(目前,这当然可以改变)。

【讨论】:

  • 我不介意被否决,但我讨厌在不知道原因的情况下被否决。请解释一下。
【解决方案4】:

值得考虑的一件事(除了使用 BitSet)是使用不同的粒度。因此,您使用较短的位集,其中每个位“保护”多个实际位。这样,您就不需要在 ram 中为每个用户提供数百万位。

实现这一点的一种简单方法是设置一个较小的位,例如 n/32,然后执行以下操作:

boolean isSet(int n) {
    return guardingBits.isSet(n / 32) && realBits.isSet(n);
}

如果这些位大多为零,这为您提供了避免加载实际位的好机会。您可以修改此方法以匹配预期的位集。如果您希望几乎所有位都已设置,则可以使用此保护位来存储一个,如果它保护的所有位都已设置。所以你只需要检查可能为零的位。

这甚至可能只是开始。根据使用情况和要求,您可能希望使用 B 树或分页版本,在这种版本中,您只在内存中保存了大位字段的一小部分。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-13
    相关资源
    最近更新 更多