如果我有以下函数,它被认为是纯粹的,因为它没有副作用,并且在给定相同输入x的情况下总是会产生相同的结果。
public static int AddOne(int x) { return x + 1; }
据我了解,如果运行时了解功能纯度,它可以优化执行,从而不必重新计算返回值。
有没有办法在 C# 中实现这种运行时优化?而且我认为这种优化有一个名称。它叫什么?
编辑:显然,我的示例函数不会从这种优化中获得很多好处。给出这个例子是为了表达我心中的纯洁类型,而不是现实世界的例子。
如果我有以下函数,它被认为是纯粹的,因为它没有副作用,并且在给定相同输入x的情况下总是会产生相同的结果。
public static int AddOne(int x) { return x + 1; }
据我了解,如果运行时了解功能纯度,它可以优化执行,从而不必重新计算返回值。
有没有办法在 C# 中实现这种运行时优化?而且我认为这种优化有一个名称。它叫什么?
编辑:显然,我的示例函数不会从这种优化中获得很多好处。给出这个例子是为了表达我心中的纯洁类型,而不是现实世界的例子。
正如其他人所指出的,如果您想节省重新计算已计算结果的成本,那么您可以记忆该函数。这以增加的内存使用量换取了速度的提高——如果您怀疑如果缓存无限制地增长,您可能会耗尽内存,请记得偶尔清除缓存。
然而,除了记忆它们的结果之外,还可以对纯函数执行其他优化。例如,没有副作用的纯函数通常可以安全地调用其他线程。使用大量纯函数的算法通常可以并行化以利用多个内核。
随着大规模多核机器变得越来越便宜和越来越普遍,这一领域将变得越来越重要。我们对 C# 语言有一个长期的研究目标,以找出某种方法来利用语言、编译器和运行时中的纯函数(以及不纯但“孤立”的函数)的强大功能。但这样做涉及许多难题,工业界或学术界对最佳方法几乎没有共识。高层人士正在考虑它,但不要指望很快就会有任何重大成果。
如果计算成本很高,您可以将结果缓存在字典中吗?
static Dictionary<int, int> cache = new Dictionary<int, int>();
public static int AddOne(int x)
{
int result;
if(!cache.TryGetValue(x, out result))
{
result = x + 1;
cache[x] = result;
}
return result;
}
当然,在这种情况下,字典查找比添加更昂贵:)
Wes Dyer 在这里解释了另一种更酷的功能记忆方法:http: //blogs.msdn.com/wesdyer/archive/2007/01/26/function-memoization.aspx - 如果你做了很多这种缓存,那么他的 Memoize 功能可能会为您节省大量代码......
我认为您正在寻找功能性记忆
您所追求的技术是memoization:缓存执行结果,将传入函数的参数键入到数组或字典中。运行时并不倾向于自动应用它,尽管在某些情况下它们会这样做。C# 和 .NET 都不会自动应用 memoization。您可以自己实现 memoization - 这很容易 - 但这样做通常仅对较慢的纯函数有用,在这些函数中您倾向于重复计算并且您有足够的内存。
这可能会被编译器内联(又名内联扩展)......
只需确保使用“优化代码”标志集编译代码(在 VS 中:项目属性/构建选项卡/优化代码)
您可以做的另一件事是缓存结果(又名memoization)。但是,由于您的查找逻辑,初始性能会受到巨大影响,因此这仅对慢速函数有意义(即不是 int 加法)。
还有内存影响,但这可以通过巧妙地使用弱引用来管理。
据我了解,如果运行时了解功能纯度,它可以优化执行,从而不必重新计算返回值。
在您的示例中,除非 x 在编译时已知,否则运行时将必须计算结果。在这种情况下,您的代码将通过使用常量折叠进一步优化
编译器可以通过内联(在调用点用该函数的主体替换函数调用)和常量传播(用该表达式的结果替换没有自由变量的表达式)的组合来优化此函数。例如,在这段代码中:
AddOne(5);
AddOne 可以内联:
5 + 1;
常量传播可以简化表达式:
6;
(死代码消除可以进一步简化这个表达式,但这只是一个例子)。
知道AddOne()
没有副作用也可能使编译器能够执行公共子表达式消除,以便:
AddOne(3) + AddOne(3)
可以转化为:
int x = AddOne(3);
x + x;
或通过强度降低,甚至:
2*AddOne(3);
无法命令 c# JIT 编译器执行这些优化;它自行决定优化。但它非常聪明,您应该可以放心地依靠它来执行这些类型的转换,而无需您的干预。
编译器怎么能做到这一点?它如何知道在运行时将传入 x 的哪些值?
和回复:其他提到内联的答案......我的理解是,内联(作为一种优化)对于只使用一次(或只使用几次......)的小函数是有保证的,而不是因为它们没有副作用。 ..
另一种选择是使用 fody 插件https://github.com/Dresel/MethodCache 您可以装饰应该缓存的方法。使用它时,您当然应该考虑其他答案中提到的所有评论。