我偶然发现了一个堆栈溢出问题How does differential execution work?,它的回答很长而且很详细。所有这一切都是有道理的……但是当我完成后,我仍然不知道到底差分执行到底是什么。到底是什么

最佳答案

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

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

被调用的过程将FIFO用作内存,因为它们读取的内容与先前执行时写入的内容相同。
因此,如果这次他们的论点与上次发生了变化,他们可以看到这一点,并使用该信息做任何他们想做的事情。

要使其开始,必须有一个初始执行,其中仅发生写入,而没有读取。
对称地,应该有一个最终执行,其中只有阅读发生而没有写作。
因此,存在一个“全局”模式寄存器,其中包含两个位,一个允许读取,一个允许写入,如下所示:

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

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

最终执行在模式10中完成,因此仅发生读取。
在这种模式下,过程调用知道它们正在清理。
如果有任何对象要维护,则从FIFO中读取其标识符,然后将其删除。

但是实际的过程并不总是遵循相同的顺序。
它们包含IF语句(以及其他改变其作用的方式)。
如何处理?

答案是一种特殊的IF语句(及其终止的ENDIF语句)。
运作方式如下。
它写入其测试表达式的 bool(boolean) 值,并读取该测试表达式上次具有的值。
这样,它可以判断测试表达式是否已更改,并采取措施。
需要采取的措施是临时更改模式寄存器。

具体来说,x是从FIFO读取的测试表达式的先验值(如果启用了读取,否则为0),而y是写入FIFO的测试表达式的当前值(如果已启用写入)。
(实际上,如果未启用写入功能,则甚至不会评估测试表达式,并且y为0。)
然后x,y只是掩盖了模式寄存器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。

language-agnostic - 什么是差异执行?-LMLPHP

这样做的好处是,一旦编写了创建所有控件的过程,就可以根据程序的状态进行编写,而无需编写其他任何东西即可保持其更新或事后清理。
您不必写的任何东西都意味着更少的犯错的机会。
(有一种直接的方法可以处理用户输入事件,而无需编写事件处理程序并为其创建名称。下面链接的视频之一对此进行了说明。)

在内存管理方面,您不必组成变量名或数据结构即可保存控件。它一次只能使用足够的存储空间来存储当前可见的控件,而潜在可见的控件可以是无限的。同样,对于以前使用的控件的垃圾回收也没有任何担心-FIFO充当自动垃圾回收器。

在性能方面,当创建,删除或修改控件时,无论如何都要花时间。
当仅更新控件而没有任何更改时,与更改控件相比,执行读取,写入和比较所需的周期是微观的。

相对于响应事件更新显示的系统而言,另一个性能和正确性考虑因素是,这样的系统要求每个事件都被响应,并且两次响应均不响应,否则即使某些事件序列可能是自发的,显示也不正确。取消。在差分执行下,可以根据需要频繁或很少执行更新过程,并且在过程结束时显示总是正确的。

这是一个极其简短的示例,其中有4个按钮,其中按钮2和3以 bool(boolean) 变量为条件。

  • 在第一阶段中,在“显示”模式下, bool(boolean) 值是false,因此仅出现按钮1和4。
  • 然后将 bool(boolean) 值设置为true,并在更新模式下执行通道2,在该模式下,实例化按钮2和3并移动按钮4,其结果与 bool(boolean) 值在第一遍时为true相同。
  • 然后将 bool(boolean) 值设置为false,并在更新模式下执行传递3,从而删除按钮2和3,并使按钮4移回到之前的位置。
  • 最后,第4步在“擦除”模式下完成,导致所有内容消失。

  • (在此示例中,所做的更改以相反的顺序撤消,但这不是必须的。可以按任何顺序进行和不进行更改。)

    注意,在任何时候,由旧的和新的串联在一起的FIFO精确地包含可见按钮的参数以及 bool(boolean) 值。

    这样做的目的是说明如何将单个“绘制”过程也可以不加更改地用于任意自动增量更新和擦除。
    我希望很明显,它适用于任意深度的子过程调用,以及任意嵌套的条件,包括switchwhilefor循环,调用基于指针的函数等。
    如果我必须对此进行解释,那么我会公开说明这些解释过于复杂。

    最后,还有几个粗略的videos posted here

    **从技术上讲,他们必须读取与上次写入的字节数相同的字节。因此,例如,他们可能写了一个字符串,并在前面加上了一个字符计数,这样就可以了。

    添加:我花了很长时间才能确定这将一直有效。
    我终于证明了这一点。
    它基于Sync属性,大致意味着在程序中的任何点,前一遍写入的字节数等于后一遍读取的字节数。
    证明背后的想法是通过对程序长度的归纳来实现的。
    要证明的最困难的情况是程序的一部分由s1和IF(test)s2 ENDIF组成,其中s1和s2是程序的各个子节,每个子节都满足Sync属性。
    在纯文本中做到这一点令人眼花,乱,但是在这里我尝试绘制它:

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

    关于language-agnostic - 什么是差异执行?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4445656/

    10-13 09:52