8

我有一个静态 C 库,我可以使用不同的编译时间选项(例如 _BUILD_SMALL、_BUILD_FAST)构建它。它有一个功能

void Foo(void);

我想使用基准测试工具的单个实例来对库的“小型”和“快速”版本进行基准测试。我不想使用 .dll。

如何链接到“小型”和“快速”库并为函数名称设置别名,以便我可以调用小型版本和快速版本。理想情况下,它看起来像:

void benchmark(void)
{
  FAST_Foo();

  SMALL_Foo();
}

更多信息:

该库可以使用不同的优化选项 -Os 与 -O3 构建。此外,算法略有不同(即缓存值与总是查找值)。我想比较不同版本的大小与速度权衡。我希望以最简单的方式在库的两个版本上运行单元测试和基准测试。

4

6 回答 6

6

这只是@Michał Górny 给出的方法的一种变体(我的评论空间用完了)......

您可以创建以下形式的包含文件:

/* Automatically created file - do not edit or ugly dinosaur will eat you */
#ifndef PREFIX
#  define RENAME(f)
#else
#  define RENAME(f) PREFIX ## f
#endif

/* list all the function and variables you want to rename here in one place */
#define func_foo RENAME(func_foo)
#define func_bar RENAME(func_bar)
/* ... many more ... */

#undef RENAME

至少gcc允许您使用选项从命令行指定包含头文件-include rename.h(假设此文件称为rename.h)。因为您使用gcc相似选项(-O3Os),所以我假设您gcc在此答案的其余部分中使用。否则,如果你的 C 编译器是合理的,你应该能够以类似的方式来做。

CFLAGS通过为 C 编译器提供不同的选项(此处通过设置),您可以轻松创建可以同时链接的两个甚至三个版本的库:

CFLAGS += -include rename.h -DPREFIX=fast_ -D_BUILD_FAST -O3 -DBENCHMARKING
CFLAGS += -include rename.h -DPREFIX=small_ -D_BUILD_SMALL -Os -DBENCHMARKING
CFLAGS += -D_BUILD_FAST -O2

如果您的库头文件看起来非常规则,并且如果您声明了库私有函数static,那么很容易通过一些虚拟脚本使用非常简单的正则表达式从这些头文件中提取函数以自动rename.h为您生成文件。如果您正在使用make或类似的东西,这是一个自然的构建目标。所有全局变量也需要使用相同的方法重命名以允许同时使用。

这个解决方案有三个要点:

  1. 丑陋的重命名业务可以隐藏在一个文件中,您不需要编辑实际的源文件——尤其是您不需要弄乱源文件,但可以保持它们的干净和易于阅读。
  2. 如果您遵循一些简单的原则(头文件遵循的编码约定,并且头文件将声明所有全局变量和函数),重命名可以很容易地自动化。
  3. 没有理由需要多次运行您的测试程序来使基准测试变得更加麻烦(如果您像我一样懒惰并且像我一样不喜欢重复性任务,这是相关的 - 我知道很多人不在乎,这是有点偏好)。
于 2012-08-21T04:12:46.870 回答
2

作为一种快速的解决方案,您可以使用宏来破坏函数名称,例如:

#ifdef FAST
#   define FUNC(x) FAST_##x
#else
#   define FUNC(x) SLOW_##x
#endif

void FUNC(Foo)();

现在将建立-DFAST图书馆;FAST_Foo没有它,一个有SLOW_Foo. 请注意,您还需要FUNC()在实现部分使用宏(以及每当您从库内部引用该函数时),并#ifdef FAST在快/慢代码之间切换。

不要在生产代码中使用它。

于 2012-08-15T20:47:27.360 回答
2

如果您尝试将两个静态库链接到同一个可执行文件,则链接行中列出的第二个库将不起作用,因为它提供的所有符号都已被第一个库满足。如果您提供了简单的唯一包装函数 call Foo,它仍然会失败,因为现在有多个定义。这是一个例子:

/* x.c */
extern void Y_Bar ();
extern void Z_Bar ();
int main ()
{
    Y_Bar();
    Z_Bar();
}

这个 main 调用了独特的包装函数,这些函数在liby.a和中提供libz.a

/* y.c in liby.a */
#include <stdio.h>
void Y_Bar () {
    extern void Foo ();
    Foo();
}
void Foo () {
    printf("%s\n", "that Foo");
}

/* z.c in libz.a */
#include <stdio.h>
void Z_Bar () {
    extern void Foo ();
    Foo();
}
void Foo () {
    puts("this foo");
}

尝试链接可执行文件-ly -lz将失败。

对您来说最简单的解决方法是构建两个单独的可执行文件。然后,您的基准驱动程序可以执行这两个可执行文件来比较它们的相对性能。

于 2012-08-15T21:41:53.447 回答
2

一种方法是:为两者保持相同的名称并根据编译时选项集适当地调用。

ifdef SMALL_FOO
void foo() {

/* Small foo code */
}
#endif

ifdef BIG_FOO
void foo() {

/* Big foo code */
}
#endif

在编译SMALL_FOO/BIG_FOO期间使用-d.

于 2012-08-15T20:41:10.793 回答
0

也许你可以使用或者你可以在使用make时接收一个输入参数,比如在你的-D option when call gcc, like -D_FAST_, -D_SMALL_,使用make ,你可以定义,当get参数。CFG=FAST, make CFG=SMALL,makefileFAST, link to FAST library

于 2012-08-16T05:57:12.200 回答
0

您说您可以构建库,更改编译时间选项,那么为什么不编辑代码以更改每个函数的名称。(您将制作两个不同版本的库。)

于 2012-08-15T20:41:29.917 回答