写下答案后,我可能应该将问题标记为“范围太广” –我们可以讨论各种策略已有很长时间了,最后必须对您的数据进行基准测试。
每个标签可以有效地由整数表示。每个实体都有一组标签。选择正确的集合实现很重要– B树和排序数组都是可能的。有了这个设置,我们将仅执行成员资格测试。由于这两种结构都是在O(log t)(每个实体带有t个标签)中执行此操作的,因此我更喜欢数组,因为它们的表示形式更密集。
现在,我们可以在O(n·log t·p)操作中筛选所有实体,其中p是谓词决策树中的平均路径长度。可以对决策树进行排序,以便可以快速做出决策。如果没有统计数据,则只能排除常见的子表达式。
搜索实体的顺序并不是很重要。在另一方面,它可能是有利的排序,它使得在指数中的实体0
来i
都具备一定的标签,而其余的则没有。这会在搜索此特定标签时减少n(在决策树中,这应该是第一个测试)。可以将其扩展到多个级别,但这会使事情变得复杂,并且占用O(2 k)内存为k水平。对于多个级别,应首先确定增益最高的标签,其中增益是不必查找的实体数乘以丢弃它们的概率。当50:50的机会或50%的实体具有此特定标签时,增益变为最大。即使访问模式未知,这也可以使您进行优化。
您还可以创建用于按使用的每个标签索引实体的集合-一组包含所有实体的T1
,下一组包含的所有T2
。一个明显的(时间和空间)优化是在集合包含所有元素的一半以上时停止,并保存那些没有此标签的元素–这样,为所有标签建立索引所占用的½ · n · t
空间会少于空间(总共t个标签)。请注意,保存互补集会使其他优化更加困难。同样,我将对数组进行排序(排序)。
如果您还通过整数范围表示实体,则可以通过仅存储连续范围的开始和结束成员来压缩用于索引集的空间。在实现方面,这可能以高位完成,以指示条目是范围限制条目还是常规条目。
如果现在有了索引集(以及标签的统计信息),我们可以优化谓词,以便首先测试不太可能的属性(快速失败策略)。这意味着,如果T1
通用且T2
罕见,则谓词T1 & T2
应通过遍历所有T2
索引集条目并测试每个元素来评估T1
。
如果我们使用排序数组来实现索引集,那么许多评估步骤都可以实现为合并操作。T1 & T2
表示我们采用T1
和T2
列表,为目标数组分配较大输入的大小,并执行以下算法,直到两个输入都为空:如果为T1[0] < T2[0]
,则T1++
(丢弃头部)。如果那样的T1[0] > T2[0]
话T2++
。如果两个磁头相等,那么,这个数字复制到目标阵列,并递增所有三个指针(T1
,T2
,目标)。如果谓词为T1 | T2
,则不会丢弃任何元素,但会复制较小的元素。形式的谓语T1 & ¬T2
也可以使用并购战略实现的,但¬T1
还是T1 | ¬T2
不行。
在对谓词决策树进行排序时应考虑这一点:补码应该在的RHS上发生&
,或者在确定最终计数而不必查看实际元素时出现在末尾。
在不使用索引集的情况下,每个线程都可以过滤掉实体的一部分,并返回与谓词匹配的元素计数,可以将这些计数相加。使用索引集时,每个线程将在决策树中分配一个节点。它接受与有序集相对应的两个输入流,并返回合并的流。请注意,决策树中的每个节点都有一个对应的集合,该集合表示满足该子表达式的所有实体,并且由于集合的顺序,因此无需一次了解整个集合即可合并它们。
可以在一定程度上组合不同的策略,例如合并索引集或通过实体列表进行过滤。过滤具有非常可预测的性能。如果查询是非常特定的,从而使索引集的使用大大减少了搜索空间,则合并操作可能会更好。重要的是要注意,合并许多大型输入集会导致性能比暴力搜索差得多。一种非常优化的算法将根据输入大小,查询结构和统计指标选择合适的策略。
顺便说一句,如果预计将来会运行类似的查询,即使它们不会加快初始运行速度,也可以缓存部分结果。
T1
对同一个对象的参考E1
,E2
等?