Общим способом реализации инкапсуляции и полиморфизма OO-подобных объектов в C является возврат непрозрачных указателей к структуре, содержащей некоторые указатели на функции. Это очень частая модель, например, в ядре Linux.
Использование указателей функций вместо вызовов функций приводит к накладным расходам, которые в основном игнорируются из-за кэширования, как уже обсуждалось в других вопросах.
Однако с новыми параметрами оптимизации -fwole-program и -flto для GCC ( > 4.6) все меняется.
libPointers.c
#include <stdlib.h>
#include "libPointers.h"
void do_work(struct worker *wrk, const int i)
{
wrk->datum += i;
}
struct worker *libPointers_init(const int startDatum)
{
struct worker *wrk = malloc (sizeof (struct worker));
*wrk = (struct worker) {
.do_work = do_work,
.datum = startDatum
};
return wrk;
}
libPointers.h
#ifndef __LIBPOINTERS_H__
#define __LIBPOINTERS_H__
struct worker {
int datum;
void (*do_work)(struct worker *, int i);
};
extern void do_work (struct worker *elab, const int i);
struct worker *libPointers_init(const int startDatum);
#endif //__LIBPOINTERS_H__
testPointers.c
#include <stdio.h>
#include "libPointers.h"
int main (void)
{
unsigned long i;
struct worker *wrk;
wrk = libPointers_init(56);
for (i = 0; i < 1e10; i++) {
#ifdef USE_POINTERS
wrk->do_work(wrk,i);
#else
do_work(wrk,i);
#endif
}
printf ("%d\n", wrk->datum);
}
Компиляция с -O3, но без флагов -flto -fwhole-program, выполнение testPointers занимает около 25 секунд на моей машине, независимо от того, является ли USE_POINTERS #defined или нет.
Если я включаю флаги -flto -fwhole-program, testPointers занимает около 25 с USE_POINTERS #defined, но около 14 секунд, если используется вызов функции.
Это полностью ожидаемое поведение, так как я понимаю, что компилятор будет встроить и оптимизировать функцию в цикле. Интересно, однако, если есть способ помочь компилятору сказать ему, что указатель функции является постоянным и поэтому позволяет ему также оптимизировать этот случай.
Для тех, кто использует cmake, вот как я скомпилировал его
CMakeLists.txt
set (CMAKE_C_FLAGS "-O3 -fwhole-program -flto")
#set (CMAKE_C_FLAGS "-O3")
add_executable(testPointers
libPointers.c
testPointers.c
)