我有一些资源敏感的东西要写。我想知道是否将结构中的变量组合在一起实际上会导致内存开销,而不是将这些变量一起传递(例如作为函数参数)。
如果是这样,那么创建一些可以在没有开销的情况下运行惰性值的东西的好方法是什么?例如现在我有一个IEnumerable<Foo>
,其中Foo
是一个有少数成员的结构。我假设它与IEnumerable<Tuple<[members]>>
相同,但是如果创建结构会产生额外的开销,我该如何避免呢?调用[members]
的回调会减少开销吗?
谢谢!
答案 0 :(得分:4)
Tuple
是一个通用类 - reference type。因此,当你做
Tuple.Create(a, b, c, ...)
您正在托管堆上分配和实例化一个类。完成后,您可以传递对此对象的托管引用,该引用本身将很小 - 32位进程上的4个字节或64位进程上的8个字节。这使得调用快速,但实例化可能很慢。它还可以为垃圾收集器增加内存压力 - 如果你正在迭代大量的数据集,可能还会批量。
struct
是值类型。它们总是嵌入在一些更大的上下文中 - 无论是堆栈还是某些类。它们作为副本传递,除非boxed,此时它们被复制到堆中。幸运的是,当使用结构或其他值类型的通用可枚举时,这些类型are not boxed - 这是c#中泛型的基本设计要求之一。因此,对结构进行枚举将导致非常小的内存压力,但可能会在复制内容时产生额外的成本。
至于构造结构的时间,没有理由认为它应该花费比构造一个等效类所需的时间更长的时间。我的观察是一些像Color
这样的构造可能很慢,但那是因为构造函数或工厂方法中有很多代码,而不是因为结构本质上很慢。 (尽管请参阅this article,指出在使用具有只读字段的结构时可能出现的轻微性能问题。)
因此,在决定是否迭代结构或元组时,内存压力和构造时间(可能更高的元组)与调用和内存复制的成本(结构更高)之间存在权衡。我的经验是,对于大量的小对象(比如指针),使用struct
进行迭代的速度更快 - 但是软件中的不同细节可能会产生不同的结果。确定哪个成本较低的唯一方法是使用分析器进行实际测量。
但是,我不确定我会建议那样做。我建议做的是以尽可能最直接的方式编写代码,而不是过多地担心点优化。如果你已经将你的结构Foo
作为许多方法的参数传递 - 继续使用它。如果您有许多期望并使用通用元组的代码,请改用它们。小数据转换的性能成本可能会累积并淹没您使用迭代器进行的任何优化。