我有一个简单的程序,它从文件系统中读取大量内容,过滤结果并打印它们。这个简单的程序实现了特定于域的语言,使选择更容易。这个DSL“编译”成一个看起来像这样的执行计划(输入是C:\Windows\System32\* OR -md5"ABCDEFG" OR -tf
):
Index Success Failure Description
0 S 1 File Matches C:\Windows\System32\*
1 S 2 File MD5 Matches ABCDEFG
2 S F File is file. (Not directory)
过滤器应用于给定文件,如果成功,则索引指针跳转到成功字段中指示的索引,如果失败,则索引指针跳转到失败字段中指示的数字。 “S”表示文件通过过滤器,F表示该文件应该被拒绝。
当然,基于简单文件属性(!FILE_ATTRIBUTE_DIRECTORY)检查的过滤器比基于文件MD5的检查快得多,这需要打开并执行文件的实际散列。每个过滤器“操作码”都有一个与之关联的时间类; MD5获得一个高时序数,ISFILE获得一个低时序数。
我想重新排序此执行计划,以便尽可能少地执行需要很长时间的操作码。对于上述计划,这意味着它必须是:
Index Success Failure Description
0 S 1 File is file. (Not directory)
1 S 2 File Matches C:\Windows\System32\*
2 S F File MD5 Matches ABCDEFG
根据“龙书”,选择三个地址代码的最佳执行顺序是NP-Complete问题(至少根据该文本第二版的第511页),但在这种情况下,他们正在谈论关于寄存器分配和机器的其他问题。就我而言,实际的“中间代码”要简单得多。我想知道一个允许我将源IL重新排序为最佳执行计划的方案。
这是另一个例子:
{C:\ Windows \ Inf * AND -tp}或{-tf AND NOT C:\ Windows \ System32 \ Drivers *}
解析:
Index Success Failure Description
0 1 2 File Matches C:\Windows\Inf\*
1 S 2 File is a Portable Executable
2 3 F File is file. (Not directory)
3 F S File Matches C:\Windows\System32\Drivers\*
最佳:
Index Success Failure Description
0 1 2 File is file. (Not directory)
1 2 S File Matches C:\Windows\System32\Drivers\*
2 3 F File Matches C:\Windows\Inf\*
3 S F File is a Portable Executable
答案 0 :(得分:3)
听起来在编译到您的操作码之前选择最佳订单可能更容易。如果您有一个解析树,并且它尽可能“平坦”,那么您可以为每个节点分配一个分数,然后按照最低总分数对每个节点的子项进行排序。
例如:
{ C:\Windows\Inf* AND -tp } OR { -tf AND NOT C:\Windows\System32\Drivers* }
1 2 3 4
OR
/ \
AND AND
/ \ / \
1 2 3 4
您可以按最低分数对AND节点(1,2)和(3,4)进行排序,然后将该分数分配给每个节点。然后按他们的子项的最低分数对OR节点的子项进行排序。
由于AND和OR是可交换的,因此这种排序操作不会改变整体表达的含义。
答案 1 :(得分:1)
@Greg Hewgill是对的,这在AST上比在中级代码上更容易执行。当您想要处理中间代码时,第一个目标是将其转换为依赖树(看起来像AST /耸肩)。
从叶子开始 - 如果你对NOT使用否定谓词,这可能是最简单的。
Index Success Failure Description
0 1 2 File Matches C:\Windows\Inf\*
1 S 2 File is a Portable Executable
2 3 F File is file. (Not directory)
3 F S File Matches C:\Windows\System32\Drivers\*
Extract Leaf(将子节点作为S,F或提取的节点的任何内容;在需要时插入NOT;将所有对Leaf的引用替换为对叶子节点的引用)
Index Success Failure Description
0 1 2 File Matches C:\Windows\Inf\*
1 S 2 File is a Portable Executable
2 L1 F File is file. (Not directory)
L1=NOT(cost(child))
|
Pred(cost(PATH))
提取节点(如果成功指向提取的节点使用连接加入;失败使用析取;替换所有对节点的引用,并引用包含节点的树的结果根。)
Index Success Failure Description
0 1 L3 File Matches C:\Windows\Inf\*
1 S L3 File is a Portable Executable
L3=AND L1 L2 (cost(Min(L1,L2) + Selectivity(Min(L1,L2)) * Max(L1,L2)))
/ \
L1=NOT(cost(child)) L2=IS(cost(child))
| |
3=Pred(cost(PATH)) 2=Pred(cost(ISFILE))
提取节点
Index Success Failure Description
0 L5 L3 File Matches C:\Windows\Inf\*
L5=OR L3 L4 (cost(Min(L3,L4) + (1.0 - Selectivity(Min(L3,L4))) * Max(L3,L4)))
/ \
| L4=IS(cost(child))
| |
| 1=Pred(cost(PORT_EXE))
|
L3=AND L1 L2 (cost(Min(L1,L2) + Selectivity(Min(L1,L2)) * Max(L1,L2)))
/ \
L1=NOT(cost(child)) L2=IS(cost(child))
| |
3=Pred(cost(PATH)) 2=Pred(cost(ISFILE))
提取节点(在成功和失败都引用节点的情况下,您必须通过节点定义的子树的根上的模式匹配将节点注入树中)
如果root为OR,则在必要时反转谓词以确保引用为Success,并将其作为与未被Failure引用的子项的连接注入。
如果root是AND,则在必要时反转谓词以确保引用为Failure并注入与Success引用的子根分离。
导致:
L5=OR L3 L4 (cost(Min(L3,L4) + (1.0 - Selectivity(Min(L3,L4))) * Max(L3,L4)))
/ \
| L4=AND(cost(as for L3))
| / \
| L6=IS(cost(child)) L7=IS(cost(child))
| | |
| 1=Pred(cost(PORT_EXE)) 0=Pred(cost(PATH))
|
L3=AND L1 L2 (cost(Min(L1,L2) + Selectivity(Min(L1,L2)) * Max(L1,L2)))
/ \
L1=NOT(cost(child)) L2=IS(cost(child))
| |
3=Pred(cost(PATH)) 2=Pred(cost(ISFILE))