您混淆了两个不同的问题。您可以按值传递 任何 对象(只要它是可复制的)。
是否将其传递到寄存器中或堆栈上取决于实现,特别是所使用的调用约定。
在某些调用约定下,大于 8 字节(通用寄存器大小)的参数将在堆栈上传递。在其他调用约定下,它们可能会被简单地拆分为多个寄存器。
在某些情况下,对象可能从不传入寄存器,不管它们的大小。
同样,SIMD 值 (SSE/AVX) 在某些调用约定中可能会在寄存器中传递,但在其他调用约定中将始终放在堆栈中。标量浮点值也是如此。
但是您的问题无法真正得到有意义的回答。复制对象的速度受对象大小的影响,是的。如果对象是 POD 类型,并且适合寄存器,则 可以 使用简单的 mov 指令进行复制。编译器是否会做这取决于编译器。
显然,对象越大,占用的缓存空间就越多,这意味着您将获得更多的缓存未命中。
但这一切都太模糊了,几乎没有用处。我们不知道您的对象是什么样子,也不知道您的代码对它做了什么。如果您有一个特定的类型,那么编写一个基准来看看它是如何被编译器处理的。
回应您的修改
我很感兴趣,在了解架构、类型大小、缓存大小等的参数传递方面是否有任何一般性建议。例如:“当类型小于 N 字节时,最好按值传递类型。
首先,相信你的编译器。它会在许多情况下积极优化副本,因此即使您确实按值传递一个大对象,也不太可能成为可衡量的问题。
其次,您正在研究一种微优化,无论哪种方式都不太可能产生明显的差异。对于小对象,按值传递避免了指针间接,因此它可能会稍微快一些。在某些时候,这会被复制的成本所淹没(假设对象被复制,见上文)。对于 非常 大的对象(为了论证,假设 500 字节或以上,大到对象 通常 达不到它),您绝对应该通过引用传递.
但是对于 8、16、24、40 字节的对象呢?谁知道?谁在乎?在实际代码中不太可能产生可衡量的差异。
这让我想到了两条经验法则:
- 做看起来很自然的事情:如果通过副本使您的代码更简单或更清晰,那就这样做。
- 如果性能很重要,那么 (1) 确保您所查看的内容实际上对您的性能有任何明显的影响。测量它。如果它影响性能,那么它可以被测量。如果无法衡量,那么从定义上讲,性能差异就不会很明显。
所以,简而言之:
- 对于原始类型,按值传递。
- 对于非常大的类型,通过引用传递。
- 除此之外,别再担心了,把时间花在富有成效的事情上。