2017-02-16 85 views
2

当我编写代码时,我尝试通过创建“模块”来保持程序的逻辑上可分割的部分分离。来自诸如Javascript和NodeJS或Python之类的语言来实现这一点非常简单。有了C,我已经找到了一些方法来完成这个模式,我提供了一个下面的例子。我使用静态方法声明与const声明结构来创建“模块”来组织我的代码。我可以让编译器优化结构中的函数调用吗?

我注意到用这种技术调用方法的成本通常只是每个调用一个汇编指令。

而不是

movl -8(%rbp), %edx 
movl -12(%rbp), %eax 
movl %edx, %esi 
movl %eax, %edi 
call my_add_method 

“模块”技术将产生

movl $my_add_method, %ecx 
movl -8(%rbp), %edx 
movl -12(%rbp), %eax 
movl %edx, %esi 
movl %eax, %edi 
call *%rcx 

我想找到被宣布这些模块的方式,但有编译后的输出是相同的,只是调用方法由它的直接名称。

我想知道的是:

  1. 有没有一种方法可以让编译器(GCC),无论是与标志或声明的结构不同,优化代码,这样所产生的ASM是一样?

  2. 我想这将是简单编译器优化的事情,如果没有方法存在,为什么这种优化通常不可能? (考虑到结构都不变和静态)


/** 
* File: main.c 
* Target: x86_64-linux-gnu 
* Compile: gcc main.c -S -o main 
*/ 
#include <stdio.h> 

typedef struct { 
    int (* const add_func)(int, int); 
} MY_MOD_T; 

static int my_add_method(int a, int b) { 
    return a+b; 
} 

const MY_MOD_T Module = { 
    .add_func = my_add_method 
}; 

int main(void) { 
    int a = 5; 
    int b = 6; 

    // substitute these two lines to see the different output like above 
    int result = Module.add_func(a, b); 
    //int result = my_add_method(a, b); 

    printf("%d + %d = %d\n", a, b, result); 
    return 0; 
} 
+0

您能否可靠地检测到性能差异? –

+2

您是否尝试过使用优化标志(-O)?输出将会非常不同。尝试优化'-O0'中的asm输出可能是徒劳无益的。 – Jahaja

+0

不,这不是一个表现问题,只是一种好奇心。我只是认为这对编译器优化来说是一件简单的事情。 – MatUtter

回答

1

在一般情况下,是不可能使通过函数指针的行为函数调用相同,以调用名为功能。

在您的例子,考虑一个头文件module_interface.h

typedef struct { 
    int (* const add_func)(int, int); 
} MY_MOD_T; 

称为module_derived.h不同的头文件:

#include "module_interface.h" 

extern const MY_MOD_T Module; 

module_derived.c派生模块的实现:

#include "module_derived.h" 

static int my_add_method(int a, int b) { 
    return a+b; 
} 

const MY_MOD_T Module = { 
    .add_func = my_add_method 
}; 

int module_add_method(int a, int b) { 
    return my_add_method(a, b); 
} 

然后,你的主程序会l OOK,如:

#include <stdio.h> 
#include "module_derived.h" 

extern int module_add_method(int a, int b); 

int main(void) { 
    int a = 5; 
    int b = 6; 

    // substitute these two lines to see the different output like above 
    int result = Module.add_func(a, b); 
    //int result = module_add_method(a, b); 

    printf("%d + %d = %d\n", a, b, result); 
    return 0; 
} 

如果module_derived实际上是一个共享库,是不是真的有一个优化级别,可以帮助克服函数指针值必须取消引用的事实。在-O3

# Calling named function
movl $6, %esi
movl $5, %edi
call module_add_method
# Calling through module
movl $6, %esi
movl $5, %edi
call *Module(%rip)

正如您所看到的,在通过模块机制时会出现额外的偏移量计算和取消引用。

但是,对于共享库,此模块开销与位置独立代码(PLT和GOT开销)所带来的开销相当。所以,在实践中,除非分析告诉你,否则开销不值得担心。在这种情况下,你必须考虑找到一种内联热门函数调用的方法。

+0

使用''-shared'''可以解释为什么即使在使用-O时我也看到了不同的输出,谢谢! – MatUtter

相关问题