针对这个“64字节”的细节更新一下答案。
如果这个建议是针对2016年以后的.NET Framework(64-bit) 和.NET Core,那么原因是因为RyuJIT 编译器对struct promotion 优化(其他编译器叫做scalar replacement)有64-byte 的struct 尺寸限制。即64-byte 以上的未逃逸struct 不会被拆包并直接计算它的每个field。struct promotion 是.NET 中非常重要的一项优化,因为RyuJIT 的SSA 只针对local variables,struct/class objects 上的冗余操作无法被直接优化掉,比如临时struct object 经常会产生大量memory copy。然而如果一个struct 被promoted 了,那编译器会打开这个struct 并将每一个field 视为local variable 进行优化。
上个月我已经把这个限制放宽到了128-byte Improve struct promotion for 256-bit SIMD fields by fiigii · Pull Request #19663 · dotnet/coreclr
=====================
因为C# 的struct 是value type。
微软设计struct 的一个原因就是要赋予程序员精确控制内存的能力,比如让有能力的开发者把value types 保持在stack 上来避免一些GC 开销。然而由于value type 的特性(比如call-by-value,捕获变量要copy到GC heap 上去,等等),很多时候总是没办法避免copy 整个struct的,比如虽然在函数间传递大尺寸struct 时并不是“直接”copy 整个struct,而是传递一个hidden return buffer 的指针,但你一旦不小心改变了struct 的一部分值,编译器就必须帮你copy 整个struct 来保证call-by-value 的语义。所以用struct 来提升性能时必须是struct 的copy 开销要小于对应class 的GC 开销才行,越大的struct 当然copy 开销也就越大。
性能分析这种事对于一般的开发者要求还是太高了,所以微软直接给出给出一个建议让你记住就行了。我觉得靠谱。(对于不满足于这个level的人,可配合profiling参考 @赵劼 姐夫的答案)
再者为什么是“64 byte”这个数值,我只能猜了,一个很有可能的原因是在目前的CPU 体系结构下很难对64 byte (512 bit)以上的struct 做类似HVA/HFA 的first-class struct 优化,这种优化就是把一个普通struct 当作SIMD type 在SIMD register (xmm, ymm, 或zmm)中传递/拷贝/计算,从而尽量避免内存访问。
因为值类型传递时是要复制一份的,复制太多性能差。
当然,规则不要硬记,用对以后使用大值类型提高性能也很正常,例如我的项目里就有两百多个字段的值类型,然后开一个大数组,反复复用,于是内存里只有一个大对象,不会回收不回移动,对GC毫无压力。
你问大结构传参怎么办,ref了解一下。事实上因为这种模式对于性能优化太常见,C# 7还提供了readonly ref和ref return和ref local更进一步鼓励人们使用这种模式。