以下是一个简单的问题。
假设我们希望展开循环方法,例如:
public int DoSum1(int n)
{
int result = 0;
for(int i = 1;i <= n; i++)
{
result += i;
}
return result;
}
只执行简单添加的方法:
public int DoSum2( )
{
return 1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20;
}
[http://etutorials.org/Programming/Programming+C.Sharp/Part+III+The+CLR+and+the+.NET+Framework/Chapter+18.+Attributes+and+Reflection/18.3+Reflection+Emit/][1]
从逻辑上讲,我们需要一些代码来在IL中创建DoSum2
。在这个IL生成代码中,我们将使用与未优化方法相同的迭代计数执行一个实际循环。
如果生成它所需的代码将使用类似的时间来执行,那么创建一个超级快速的动态方法有什么意义呢?
也许您可以给出一个例子,如果在类似的情况下使用Emit是值得的?
发布于 2014-09-04 15:15:54
如果生成超级快速动态方法所需的代码将使用类似的时间执行,那么创建它的意义是什么?
这并不是专门针对Reflection.Emit
的,而是一般的运行时代码生成,因此我将相应地回答。
首先,我不建议仅仅使用代码生成来执行编译器通常像循环展开一样执行的微优化。让JIT编译器完成它的工作。
第二,您是正确的,因为生成只执行一次的代码通常没有什么意义。发出和编译IL所需的时间不是很长。只有当代码被多次执行时,才会麻烦地生成代码。
现在,在某些情况下,运行时代码生成可以证明是有益的。事实上,这是一种我充分利用的技术。我在一个电子交易环境中工作,在那里需要处理大量的动态数据。这带来了几个问题,最重要的是内存的使用和吞吐量。
我们的交易应用程序需要将大量数据保存在内存中,因此每个记录的足迹都是至关重要的。动态数据结构(如映射/字典)比具有优化字段布局的"POCO“类效率低,根据设计,可能需要装箱某些值。一旦知道数据的形状,我就通过生成客户端存储类来避免这种开销。实际上,内存布局就像我在编译时知道数据的形状一样。
吞吐量也是一个主要问题;(反序列化动态数据通常涉及一些额外的内省和额外的间接层。需要序列化记录吗?好的,首先您需要查询这些字段是什么。然后,对于每个字段,您需要确定其类型,然后为该类型选择一个序列化程序,然后调用序列化程序。如果您的数据结构有可选字段,您可能需要做一些额外的预处理,比如计算出存在映射的大小,以及存在映射中的哪些位对应于哪个字段。如果您需要处理大量数据,那么所有这些开销就会成为一个真正的问题。我通过在服务器端和客户端生成专用(反)序列化程序来避免这种开销。由于序列化器是按需生成的,因此它们可以知道数据的确切形状,并像手工优化的序列化程序一样高效地读取/写入数据。当你有大量的数据更新在非常高的频率,这可能会产生巨大的变化。
现在,请记住,我们是一个边缘的案件。大多数应用程序没有我们所具有的积极的内存和吞吐量需求,因此运行时代码生成是不必要的。只有当你真的需要的时候,你才应该走那条路,而且你已经用尽了所有其他的可能性。虽然它可以帮助提高性能,但是生成的代码很难调试和维护。
https://stackoverflow.com/questions/25667477
复制相似问题