gpt4 book ai didi

c - 为什么动态链接的可执行文件明显比 Linux 中的静态链接的慢?

转载 作者:太空宇宙 更新时间:2023-11-04 01:57:15 24 4
gpt4 key购买 nike

用确定井字棋盘结果的玩具程序进行测试,我明白了。是什么造成了如此大的差异?我怀疑使用静态链接的 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_rrandom_r 代替。这些函数使用调用者提供的状态,因此可以(至少在理论上)避免对全局数据的任何内部访问。

关于c - 为什么动态链接的可执行文件明显比 Linux 中的静态链接的慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32544984/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com