«Inlining» (вид) функции во время выполнения в C - PullRequest
5 голосов
/ 05 мая 2010

Я думал о типичной проблеме, которая очень JIT-способна, но трудна для подхода с необработанным C. Сценарий устанавливает серию указателей на функции, которые будут «составлены» (как в составлении математической функции) один раз во время выполнения, а затем вызывал много-много раз.

Это очевидный способ включает в себя множество виртуальных вызовов, которые стоят дорого, и если имеется достаточное количество вложенных функций для полного заполнения таблицы прогноза ветвления ЦП, то производительность значительно падает.

На таком языке, как Лисп, я мог бы, вероятно, обработать код и заменить «виртуальный» вызов фактическим содержимым функций, а затем вызвать compile, чтобы получить оптимизированную версию, но это кажется очень хакерским и подверженным ошибкам. сделать в C, и использование C является требованием для этой проблемы; -)

Итак, знаете ли вы, есть ли стандартный, переносимый и безопасный способ добиться этого в C?

Приветствия

Ответы [ 3 ]

6 голосов
/ 05 мая 2010

Возможно, вы захотите взглянуть на LLVM. У них есть библиотека, которая позволяет JITing-код (и многое другое) из C, она поддерживает множество платформ и это проект с открытым исходным кодом: http://llvm.org/

1 голос
/ 06 мая 2010

У меня есть два предложения по устранению вызовов виртуальных функций, , если необходимо для производительности. Для иллюстрации предположим, что у вас есть функция, принимающая указатель на функцию в качестве аргумента:

void my_algorithm(int (*func)(...), ...)
{
    /* ... */
}

А также предположим, что вы заранее знаете все возможные значения, которые может принимать указатель функции. Например:

my_algorithm(func_1, ...);
//...
my_algorithm(func_2, ...);

Сначала преобразуйте ваш оригинальный my_algorithm () в макрос:

#define MY_ALGORITHM(func, ...)       \
{                                     \
    /* ... */                         \
}

Затем перепишите my_algorithm () как:

extern int func_1(...);
extern int func_2(...);

void my_algorithm(int (*func)(...), ...)
{
    if (func == func_1)
        MY_ALGORITHM(func_1, ...)
    else if (func == func_2)
        MY_ALGORITHM(func_2, ...)
    else
        assert(0 && "Unexpected function arg to my_algorithm()");
}

Это, конечно, удвоит размер скомпилированного объектного файла. И, на поверхности, он удаляет только один уровень косвенности. Но если func_1 и / или func_2 встроены, вы можете значительно ускориться.

И вы можете даже «пропустить» макросы, как в:

#define HYPOT_Y(x) hypot(x, y)
MY_ALGORITHM(HYPOT_Y, ...);     //assumes y is known

Второе предложение - это вариант с использованием X Macros (http://en.wikipedia.org/wiki/C_preprocessor#X-Macros). Вместо #define поместите тело исходного my_algorithm () в отдельный файл my_algorithm.h. Затем перепишите my_algorithm () как:

void my_algorithm(int (*func)(...), ...)
{
    if (func == func_1)
        #define func func_1
        #include "my_algorithm.h"
        #undef func
    else if (func == func_2)
        #define func func_2
        #include "my_algorithm.h"
        #undef func
    else
        assert(0 && "Unexpected function arg to my_algorithm()");
}

Я бы, вероятно, использовал бы макросы X, если код содержит более пары десятков строк. Его преимущества включают (без каламбура):

  • Без уродливых обратных слешей
  • Упрощенная отладка (например, обратные трассировки и пошаговое выполнение).

Это все стандартное C. Но скорее старая школа.

0 голосов
/ 06 мая 2010

Если у вас все в порядке с поддержкой только x86, вы можете попробовать встроить TCC:

http://bellard.org/tcc/

...