[英]Returning Vs. Pointer
在這兩種情況下,性能會有多大差異?
int func(int a, int b) { return a + b; }
和
void func(int a, int b, int * c) { *c = a + b; }
現在,如果它是一個結構呢?
typedef struct { int a; int b; char c; } my;
my func(int a, int b, char c) { my x; x.a = a; x.b = b; x.c = c; return x; }
和
void func(int a, int b, int c, my * x) { x->a = a; x->b = b; x->c = c; }
我能想到的一件事是寄存器不能用於此目的,對嗎? 除此之外,我不知道這個函數在通過編譯器后會怎樣。
哪個更高效、更快捷?
如果函數可以內聯,往往前2個沒有區別。
否則(由於沒有鏈接時優化而沒有內聯)按值返回int
更有效,因為它只是可以立即使用的寄存器中的值。 此外,調用者不必傳遞盡可能多的參數,也不必查找/騰出空間來指向。 如果調用者確實想要使用輸出值,則必須重新加載它,從而在從准備好輸入到准備好輸出的整個依賴鏈中引入延遲。 (存儲轉發延遲在現代 x86 CPU 上約為 5 個周期,而lea eax, [rdi + rsi]
為 1 個周期延遲,它將為 x86-64 System V 實現該功能。
例外情況可能是在極少數情況下,調用者不打算使用該值,只是希望它在內存中的某個地址。 將該地址傳遞給被調用者(在寄存器中)以便在那里使用它意味着調用者不必將該地址保留在任何可以在函數調用中幸存的地方。
對於結構版本:
寄存器不能用於此目的,對嗎?
不,對於某些調用約定,可以在寄存器中返回小結構。
x86-64 System V 將按 RDX:RAX 寄存器對中的值返回您的my
結構,因為它小於 16 個字節並且都是整數。 (並且可以簡單地復制。)在https://godbolt.org/z/x73cEh上試試 -
# clang11.0 -O3 for x86-64 SysV
func_val:
shl rsi, 32
mov eax, edi
or rax, rsi # (uint64_t)b<<32 | a; the low 64 bits of the struct
# c was already in EDX, the low half of RDX; clang leaves it there.
ret
func_out:
mov dword ptr [rcx], edi
mov dword ptr [rcx + 4], esi # just store the struct members
mov byte ptr [rcx + 8], dl # to memory pointed-to by 4th arg
ret
GCC 不假設char c
像 clang 那樣正確地符號擴展到 EDX( 非官方 ABI 功能)。 GCC 做了一個非常愚蠢的字節存儲/雙字重載,它創建了一個存儲轉發停頓,從內存中而不是從 EDX 的高字節中獲取未初始化的垃圾。 純粹是一個錯過的優化,但可以在https://godbolt.org/z/WGcqKc 中查看。 它還瘋狂地使用 SSE2 將兩個整數合並為 64 位值,然后再執行movq rax, xmm0
或輸出參數的內存。
如果調用者使用這些值,您肯定希望結構版本內聯,因此可以優化這種打包到返回值寄存器中的操作。
函數如何實際返回 C 中的結構變量? 有一個更大結構的 ARM 示例:按值返回將隱藏的指針傳遞給調用者的返回值對象。 從那里開始,如果分配給轉義分析無法證明是私有的東西,則可能需要由調用者復制它。 (例如通過一些指針)。 什么阻止使用函數參數作為隱藏指針?
C 編譯器如何實現返回大型結構的函數? 指出 C 和 C++ 之間的代碼生成可能不同。
我不知道如何解釋在不了解 asm 和您關心的調用約定的情況下可以應用的任何一般經驗法則。 通常通過引用傳遞/返回大型結構,但對於小型結構,它非常“取決於”。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.