![](/img/trans.png)
[英]why executable is smaller than the library which is statically linked with application project?
[英]Why is a dynamically linked executable noticeably slower than the statically linked one in Linux?
用玩具程序測試確定井字棋盤的結果,我得到了這個。 是什么讓這個差別很大? 我懷疑使用靜態鏈接的libc對rand
的調用更快,但仍然對結果感到驚訝。
~$ gcc a.c -std=c11 -O3
~$ time ./a.out
32614644
real 0m9.396s
user 0m9.388s
sys 0m0.004s
~$ gcc a.c -std=c11 -O3 -static
~$ time ./a.out
32614644
real 0m6.891s
user 0m6.884s
sys 0m0.000s
#include <stdio.h>
#include <stdlib.h>
#define SIZE 3
#define SIZE_2 (SIZE * SIZE)
static int determineResult(int board[static SIZE_2]) {
for (int i = 0; i < SIZE_2; i += SIZE) {
if (!board[i]) {
continue;
}
for (int j = i + 1; j < i + SIZE; ++j) {
if (board[i] != board[j]) {
goto next;
}
}
return board[i];
next:;
}
for (int i = 0; i < SIZE; ++i) {
if (!board[i]) {
continue;
}
for (int j = i + SIZE; j < i + SIZE_2; j += SIZE) {
if (board[i] != board[j]) {
goto next2;
}
}
return board[i];
next2:;
}
for (int i = SIZE + 1; i < SIZE_2; i += SIZE + 1) {
if (board[i] != *board) {
goto next3;
}
}
return *board;
next3:
for (int i = SIZE * 2 - 2; i <= SIZE_2 - SIZE; i += SIZE - 1) {
if (board[i] != board[SIZE - 1]) {
return 0;
}
}
return board[SIZE - 1];
}
#define N 50000000
int main(void) {
srand(0);
size_t n = 0;
for (int i = 0; i < N; ++i) {
int board[SIZE_2];
for (int i = 0; i < SIZE_2; ++i) {
board[i] = rand() % 3;
}
n += determineResult(board);
}
printf("%zu\n", n);
return EXIT_SUCCESS;
}
我不能確定這是不知道您的系統正在使用的特定ABI(這取決於操作系統和CPU架構)的原因,但以下是最可能的解釋。
在大多數實現中,共享庫中的代碼(包括共享libc.so
)必須是與位置無關的代碼 。 這意味着它可以在任何地址加載(而不是由鏈接器分配固定的運行時地址),因此不能在機器代碼中使用硬編碼的絕對數據地址。 相反,它必須通過指令指針相對尋址或全局偏移表 (GOT)訪問全局數據,其地址保存在寄存器中或相對於指令指針計算。 這些尋址模式主要在精心設計的現代指令集架構(如x86_64,AArch64,RISC-V等)上高效。在大多數其他架構(包括32位x86)上,它們的效率非常低。 例如,以下功能:
int x;
int get_x()
{
return x;
}
會在x86上冒充如下內容:
get_x:
push %ebp
mov %esp, %ebp
push %ebx
sub $4, %esp
call __x86.get_pc_thunk_bx
add $_GLOBAL_OFFSET_TABLE_, %ebx
mov x@GOT(%ebx), %eax
mov (%eax),%eax
add $4, %esp
pop %ebx
pop %ebp
ret
而你會期望(對於非位置無關的代碼)看到:
get_x:
mov x, %eax
ret
由於隨機數生成器具有內部(全局)狀態,因此他們無法為與位置無關的代碼執行這種昂貴的舞蹈。 由於他們所做的實際計算可能非常短且快,因此PIC開銷可能是其運行時間的重要部分。
確認這一理論的一種方法是嘗試使用rand_r
或random_r
。 這些函數使用調用者提供的狀態,因此(至少在理論上)可以避免對全局數據的任何內部訪問。
這里的問題是你正在比較總的執行時間,在這樣的小例子中,它將非常優於靜態鏈接示例,因為沒有要執行的查找。
靜態鏈接和動態鏈接之間的最大區別在於,動態鏈接具有在運行時鏈接在一起的多個模塊/對象,並且靜態編譯的二進制文件包含二進制文件中包含的所有內容。 當然有些細節可能會有所不同,但大致就是這樣。
所以......考慮到上述因素,啟動可執行文件,加載幾個不同的文件,執行函數並返回可能比加載文件和執行函數需要更多的時間。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.