什么是差异执行?

时间:2010-12-15 00:27:32

标签: language-agnostic differential-execution

我偶然发现了一个Stack Overflow问题 How does differential execution work? ,这个问题非常冗长而详细。所有这一切都有意义......但是当我完成时,我仍然不知道差异执行究竟是什么。真的是什么?

2 个答案:

答案 0 :(得分:18)

修改。这是我第N次尝试解释它。

假设您有一个重复执行的简单确定性过程,始终遵循相同的语句执行或过程调用序列。 该过程调用自己按顺序将任何内容写入FIFO,并从FIFO的另一端读取相同数量的字节,如下所示:**

enter image description here

被调用的过程使用FIFO作为内存,因为它们读取的内容与它们在先前执行时所写的内容相同。 因此,如果他们的论点在上次与上次发生时有所不同,他们可以看到,并根据这些信息做任何他们想做的事情。

要开始它,必须有一个初始执行,只有写入发生,没有阅读。 对称地,应该有一个最终执行,其中只有阅读发生,没有写作。 所以有一个"全球"模式寄存器包含两个位,一个用于读取,另一个用于写入,如下所示:

enter image description here

初始执行在 01 模式下完成,因此只完成写入。 过程调用可以看到模式,因此他们知道没有先前的历史记录。 如果他们想要创建对象,可以将识别信息放入FIFO中(无需存储在变量中)。

中间执行在模式 11 中完成,因此读取和写入都会发生,并且过程调用可以检测数据更改。 如果有对象要保持最新, 他们的识别信息从FIFO读取和写入, 因此可以访问它们,并在必要时进行修改。

最终执行在 10 模式下完成,因此只能进行读取。 在该模式下,程序调用知道他们只是在清理。 如果有任何对象被维护,它们的标识符将从FIFO中读取,并且可以删除它们。


但实际程序并不总是遵循相同的顺序。 它们包含IF语句(以及改变其行为的其他方式)。 怎么办呢?

答案是一种特殊的IF语句(及其终止ENDIF语句)。 这是它的工作原理。 它写入其测试表达式的布尔值,并读取测试表达式上次使用的值。 这样,它可以判断测试表达式是否已更改,并采取措施。 它采取的行动是临时改变模式寄存器

enter image description here

具体来说, x 是测试表达式的先前值,从FIFO读取(如果读取已启用,否则为0), y 是当前值写入FIFO的测试表达式(如果写入已启用)。 (实际上,如果未启用写入,则甚至不会评估测试表达式,并且 y 为0.) 然后 x,y 只需MASK模式寄存器 r,w 。 因此,如果测试表达式已将从True更改为False,则正文将以只读模式执行。相反,如果它已从False更改为True,则正文以只写模式执行。 如果结果为 00 ,则跳过IF..ENDIF语句中的代码。 (您可能想要考虑一下这是否涵盖所有情况 - 确实如此。)

这可能并不明显,但这些IF..ENDIF语句可以任意嵌套,并且可以扩展到所有其他类型的条件语句,如ELSE,SWITCH,WHILE,FOR,甚至可以调用基于指针的函数。还有一种情况是,只要遵守模式,程序就可以分成所需的任何程度的子程序,包括递归。

(必须遵循一条规则,称为擦除模式规则,这是在模式 10 中没有计算任何后果,例如跟随应该完成指针或索引数组。从概念上讲,原因是模式 10 只是为了摆脱东西而存在。)

因此,它是一个有趣的控制结构,可以利用它来检测更改,通常是数据更改,并对这些更改采取措施。


它在图形用户界面中的使用是使一些控件或其他对象与程序状态信息保持一致。对于该用途,这三种模式称为SHOW(01),UPDATE(11)和ERASE(10)。 该过程最初在SHOW模式下执行,其中创建控件,并且与它们相关的信息填充FIFO。 然后在UPDATE模式下执行任意数量的执行,其中根据需要修改控件以保持程序状态的最新状态。 最后,在ERASE模式下执行,其中控件从UI中删除,FIFO被清空。

enter image description here

这样做的好处是,一旦你编写了创建所有控件的程序,作为程序状态的函数,你就不会这样做。必须写任何其他内容以保持更新或清理后。 你不必写的东西意味着犯错的机会就越少。 (有一种直接的方式来处理用户输入事件,而无需编写事件处理程序并为它们创建名称。这在以下链接的视频中进行了解释。)

在内存管理方面,您不必编写变量名称或数据结构来保存控件。对于当前可见的控件,它一次只能使用足够的存储空间,而可能的可见控件可以是无限制的。此外,从未担心以前使用过的控件的垃圾收集 - FIFO充当自动垃圾收集器。

就性能而言,在创建,删除或修改控件时,无论如何都需要花费时间。 当它只是更新控件而且没有变化时,与改变控件相比,进行读取,写入和比较所需的周期是微观的。

相对于更新显示以响应事件的系统,另一个性能和正确性考虑因素是这样的系统要求每个事件都被响应,而不是两次,否则显示将是不正确的,即使某些事件序列可能自我取消。在差异执行下,更新传递可以经常或根本不需要执行,并且在传递结束时显示始终是正确的。


这是一个极其简短的例子,其中有4个按钮,其中按钮2和3以布尔变量为条件。

  1. 在第一遍中,在Show模式下,布尔值为false,因此只显示按钮1和4。
  2. 然后布尔值设置为true,并且在更新模式下执行传递2,其中实例化按钮2和3并移动按钮4,给出与第一次传递时布尔值为真的相同结果。 / LI>
  3. 然后布尔值设置为false,并且在更新模式下执行传递3,导致按钮2和3被移除,按钮4移回到之前的位置。
  4. 最后传递4在擦除模式下完成,导致一切消失。
  5. (在这个例子中,更改按照相反的顺序撤消,但这不是必需的。可以按任何顺序进行更改。)

    请注意,在任何时候,由旧和新连接在一起组成的FIFO包含可见按钮的参数和布尔值。

    这一点的目的是展示单个"油漆"程序也可以不加改变地用于任意自动增量更新和擦除。 我希望很明显它适用于任意深度的子过程调用,以及条件的任意嵌套,包括switchwhilefor循环,调用基于指针的函数等。 如果我必须解释这一点,那么我就会因为过于复杂的解释而接受抨击。

    enter image description here

    最后,有一些粗略但短videos posted here

    **从技术上讲,他们必须读取上次写入的相同字节数。因此,例如,他们可能已经写了一个以字符数开头的字符串,并且没问题。


    补充:我花了很长时间才确定这总能奏效。 我终于证明了这一点。 它基于 Sync 属性,大致意味着在程序中的任何一点,在先前传递上写入的字节数等于在后续传递中读取的数字。 证明背后的想法是通过程序长度的归纳来实现。 要证明的最棘手的案例是程序部分包含 s1 ,然后是 IF(测试)s2 ENDIF ,其中 s1 s2 是程序的子部分,每个部分都满足 Sync 属性。 在纯文字中做这件事就是眼睛上釉,但在这里我试图绘制它: enter image description here

    它定义了 Sync 属性,并显示了代码中每个点写入和读取的字节数,并显示它们是相等的。 关键点是:1)当前通道上读取的测试表达式(0或1)的值必须等于先前通道上写入的值,以及2) Sync(s2)的条件很满意。 这满足组合程序的 Sync 属性。

答案 1 :(得分:9)

我阅读了所有可以找到的内容,并观看了视频,并将对第一原则描述进行了拍摄。

<强>概述

这是一种基于DSL的设计模式,用于以干净,高效的方式实现用户界面和其他面向状态的子系统。它着重于改变GUI配置以匹配当前程序状态的问题,其中该状态包括GUI小部件本身的条件,例如,用户选择选项卡,单选按钮和菜单项,小部件以任意复杂的方式显示/消失。

<强>描述

该模式假定:

  1. 全局集合 C需要定期更新的对象
  2. 这些对象的类型系列,其中实例具有参数。
  3. C上的一组操作
    • 添加A - 将新对象A放入带有参数P
    • 的C中
    • 修改A P - 将C中对象A的参数更改为P。
    • 删除A - 从C中删除对象A.
  4. C的更新包含一系列此类操作,以将C转换为给定的目标集合,例如C&#39;。
  5. 鉴于当前集合C和目标C&#39;,目标是以最低成本查找更新。每项操作都有单位成本。
  6. 可能的集合集以域特定语言(DSL)描述,该语言具有以下命令:
    • 创建H - 使用可选提示H实例化一些对象A,并将其添加到全局状态。 (注意这里没有参数。)
    • 如果B则T Else F - 根据布尔函数B有条件地执行命令序列T或F,这可能取决于正在运行的程序中的任何内容。
  7. 在所有例子中,

    • 全局状态是GUI屏幕或窗口。
    • 对象是UI小部件。类型是按钮,下拉框,文本字段,......
    • 参数控制窗口小部件的外观和行为。
    • 每次更新都包括在GUI中添加,删除和修改(例如重新定位)任意数量的小部件。
    • 创建命令正在制作小部件:按钮,下拉框,......
    • 布尔函数取决于底层程序状态,包括GUI控件本身的条件。因此,更改控件可能会影响屏幕。

    缺少链接

    发明人从未明确说明它,但一个关键的想法是,每当我们期望布尔函数值B的任何组合发生变化时,我们就在代表所有可能的目标集合(屏幕)的程序上运行DSL解释器。解释器通过发出一系列添加,删除和修改操作来处理使集合(屏幕)与新B值一致的脏工作。

    最后隐藏的假设是:DSL解释器包含一些算法,可以根据目前在当前运行期间执行的Creates的历史记录为Add和Modify操作提供参数。在GUI上下文中,这是布局算法,Create提示是布局提示。

    打孔线

    该技术的强大之处在于复杂性被封装在DSL解释器中。一个愚蠢的解释器将首先删除集合(屏幕)中的所有对象(小部件),然后在逐步执行DSL程序时为每个Create命令添加一个新的命令。永远不会发生修改。

    差异执行只是解释器的一种更智能的策略。它相当于保留了解释器上次执行的序列化记录。这是有道理的,因为录制会捕获屏幕上当前的内容。在当前运行期间,解释器查阅记录以决定如何通过具有最低成本的操作来实现目标集合(窗口小部件配置)。这可以归结为永远不会删除一个对象(小部件),只是稍后再次添加它,成本为2. DE将始终改为修改,其成本为1.如果我们碰巧在某些情况下运行解释器,其中B值如果没有更改,DE算法将根本不生成任何操作:记录的流已经代表了目标。

    当解释器执行命令时,它还会为下次运行设置录音。

    类似算法

    该算法与minimum edit distance(MED)具有相同的风格。然而,DE比MED更简单,因为没有重复的字符&#34;在MED中的DE序列化执行字符串中。这意味着我们可以通过简单的在线贪婪算法而不是动态编程找到最佳解决方案。这就是发明人的算法所做的事情。

    <强>优势

    我的看法是,对于实现具有许多复杂形式的系统来说,这是一个很好的模式,您需要使用自己的布局算法和/或&#34; if else&#34;来完全控制小部件的放置。什么是可见的逻辑是深深嵌套的。如果有&#34;如果elses&#34; N深入形式逻辑,然后有K * 2 ^ N不同的布局才能正确。传统的表单设计系统(至少是我使用的系统)根本不能很好地支持更大的K,N值。您倾向于最终使用大量类似的布局和特殊逻辑来选择难以维护且难以维护的布局和特殊逻辑。这种DSL模式似乎是一种避免这一切的方法。在具有足以抵消DSL解释器成本的形式的系统中,在初始实现期间甚至会更便宜。关注点分离也是一种力量。 DSL程序抽象表单的内容,而解释器是布局策略,作用于DSL的提示。正确地获得DSL和布局提示设计似乎是一个重要且很酷的问题。

    <强>可疑...

    我不确定避免删除/添加对有利于修改是值得在现代系统中的所有麻烦。发明者似乎对这种优化感到最自豪,但更重要的想法是使用条件表示形式的简洁DSL,并在DSL解释器中隔离布局复杂性。

    <强>小结

    到目前为止,发明家一直专注于解释器如何做出决定的深层细节。这是令人困惑的,因为它针对树木,而森林则更受关注。这是对森林的描述。