在 C 语言中实现面向对象式的封装和多态常用的方法是返回一个包含函数指针的结构体的不透明指针。这种模式在 Linux 内核中非常频繁地出现。
使用函数指针而不是函数调用会引入一些开销,但由于缓存的存在,这种开销在大多数情况下可以忽略不计,就像其他问题已经讨论过的那样。
然而,随着 GCC (>4.6) 中新的 -fwhole-program 和 -flto 优化选项的出现,情况发生了变化。
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。
如果我打开-flto -fwhole-program 标志,同时定义了USE_POINTERS,testPointers需要大约25秒的时间,但是如果使用函数调用,需要大约14秒。
这是完全符合预期的行为,因为我理解编译器将在循环中内联和优化函数。然而,我想知道是否有一种方法可以帮助编译器告诉它函数指针是常量,从而允许它优化该情况。
对于使用cmake的人,这是我如何编译的
CMakeLists.txt
set (CMAKE_C_FLAGS "-O3 -fwhole-program -flto")
#set (CMAKE_C_FLAGS "-O3")
add_executable(testPointers
libPointers.c
testPointers.c
)
wrk->do_work
的值并将其存入一个本地函数指针变量中,那么你可以在循环内使用该本地变量。 - Greg Hewgilldo_work
做的工作非常少。如果它实际上做了一些重要的事情,调用速度的差异将更难以测量(因此不太显著)。 - Bo Perssonwrk = libPointers_init(56);
#ifdef USE_POINTERS void (*const f_do_work)(struct worker *, int i) = wrk->do_work; #endiffor (i = 0; i < 1e10; i++) {
#ifdef USE_POINTERS f_do_work(wrk,i); #else do_work(wrk,i); #endif }printf ("%d\n", wrk->datum);
} - Metiu