2017-02-16 20 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), либо с флагами или объявив структуры по-разному, оптимизировать код так, чтобы в результате асмовый тоже самое?

  2. Я предполагал, что это будет simple вещь для компилятора для оптимизации, если не существует метода, почему такая оптимизация вообще невозможна? (С учетом структура является постоянной и статической)


/** 
* 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)? Выход будет совсем другим. Попытка оптимизировать выход asm в '-O0', вероятно, является бесполезной. – 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 ООК как:

#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