我手边有一个问题,乍一看它看起来很简单,但是我正在寻找其他解决方案(可能更容易):
表达式:
V0
V1
V2
V3
V4
SumA = V1 + V2
SumB = SumA + V3
SumC = SumB + SumA
SumD = SumC + V0
正如我们在这里看到的,“基础”变量是V0, V1, V2, V3 and V4
(每个变量的值都是从DB查询中返回的)
用户要求软件返回V1
和SumC
的结果。
我知道的解决方案:
找到所有必要的变量:V1,SumC,SumB,SumA,V3,V2
对于性能,我只想处理每个变量的数学,只需一次。
这意味着我需要将“基本表达式”中的表达式排序为“顶部变量”。
此时我只看到“树(数据结构)”类型的解决方案>获取V1,V2和V3 然后在得到SumB之后获得SumA,并且最后得到SumC。
还有其他方法可以解决这个问题吗?
该算法的最终目标是使用更复杂的变量和几个“中间变量”。因此,性能至关重要,我无法进行相同的数学运算超过1次。
答案 0 :(得分:2)
我不确定我完全理解 - 但我认为你指的是common subexpression elimination,[或类似的东西]这是一个非常常见的compiler optimization。
执行此优化的一种常用方法是使用程序中表达式的图形[实际上是DAG],并迭代添加新表达式。 DAG中的“源”都是初始变量[示例中为V0,V1,V2,V3,V4]。如果你已经计算过,你可以“知道”哪个表达式是多余的 - 并且避免重新计算它。
These lecture notes似乎是一个不错的更详细的解释[虽然我承认我没有全部阅读]
答案 1 :(得分:1)
首先,您需要构建一个包含所有表达式的树。对于这种情况,树是最简单的数据结构。
现在让我们假设你有这些公式:
SumA = v1 + v2
SumB = v1 + v2 + v3
SumC = ...
并且用户要求SumB
(因此您知道 如何计算SumC
,但为了让用户满意,您不必)。
在记忆中,看起来像这样:
SumA = Add( v1, v2 )
SumB = Add( Add( v1, v2 ), v3 ) )
下一步是定义比较运算符,它判断两个子树是否相同。运行这些,您会注意到Add( v1, v2 )
出现两次,因此您可以优化:
SumA = Add( v1, v2 )
SumB = Add( SumA, v3 )
这意味着您可以通过最少的计算来实现结果。下一步是向操作员添加缓存:当有人询问他们的值时,他们应该缓存它,以便下一个getValue()
调用可以返回最后的结果。
这意味着评估SumA
或SumB
将填充SumA
的缓存。由于您从未要求SumC
的值,因此它从未计算过,因此无需任何费用。
答案 2 :(得分:0)
只有加快速度的方法是在级别上使用序列化,除非使用自己的硬件,否则无法以编程方式进行。例:
请忽略右上方的注释,这是从我的脚本中窃取的:)
案例A: 100 * 4周期
案例B: 第一个结果需要3个周期,每个周期只需1个(序列化,福特工厂就好)。 - 102个周期
102 vs 400 - 速度约为4 *。
现代CPU可以在某种程度上自动执行此操作,但测量它非常困难。 我听说ICC(intel C编译器)确实优化了它的程序集以尽可能地利用它,也许这部分是为什么他们击败了intel CPU上的所有其他东西:)
答案 3 :(得分:0)
也许你可以简化它并消除中间步骤:
SumA = (V1 + V2)*2
SumC = V3 + SumA