如果生成它所需的代码将使用相似的时间来执行,那么创建一个超快速的动态方法有什么意义?
这并不是真正特定于Reflection.Emit
,而是一般的运行时代码生成,所以我会相应地回答。
首先,我不建议仅使用代码生成来执行编译器通常执行的微优化,例如循环展开。让 JIT 编译器完成它的工作。
其次,您是对的,生成只会执行一次的代码通常没有什么意义。发出和 JIT 编译 IL 所需的时间并非微不足道。如果代码会被执行多次,您应该只费心生成代码。
现在,肯定有运行时代码生成可以证明是有益的情况。事实上,这是我大量使用的一种技术。我在需要处理大量动态数据的电子交易环境中工作。这引入了几个问题,最重要的是内存使用和吞吐量。
我们的交易应用程序需要在内存中保存大量数据,因此每条记录的足迹至关重要。地图/字典等动态数据结构的效率低于具有优化字段布局的“POCO”类,并且根据设计,可能需要对某些值进行装箱。一旦知道数据的形状,我就会通过生成客户端存储类来避免这种开销。实际上,内存布局就像我在编译时知道数据的形状一样。
吞吐量也是一个主要问题。(反)序列化动态数据通常涉及一些额外的内省和额外的间接层。需要序列化记录吗?好的,首先您需要查询字段是什么。然后,对于每个字段,您需要确定其类型,然后为该类型选择一个序列化程序,然后调用该序列化程序。如果您的数据结构具有可选字段,您可能需要进行一些额外的预处理,例如确定存在图的大小,以及存在图中的哪些位对应于哪些字段。如果你需要处理一吨数据,所有这些开销都成为一个真正的问题。我通过在服务器端和客户端生成专门的(反)序列化程序来避免这种开销。由于序列化器是按需生成的,因此它们可以知道数据的确切形状,并像手动优化的序列化器一样高效地读取/写入该数据。当您以非常高的频率更新大量数据时,这可能会产生巨大的影响。
现在,请记住,我们是一个边缘案例。大多数应用程序没有我们的严格的内存和吞吐量要求,因此不需要生成运行时代码。如果你真的需要它,你应该只走那条路,并且你已经用尽了所有其他可能性。尽管它可以帮助提高性能,但生成的代码可能很难调试和维护。