Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/61.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
&引用;内联“;C语言中运行时的(类)函数_C_Lisp_Metaprogramming_Inline_Jit - Fatal编程技术网

&引用;内联“;C语言中运行时的(类)函数

&引用;内联“;C语言中运行时的(类)函数,c,lisp,metaprogramming,inline,jit,C,Lisp,Metaprogramming,Inline,Jit,我在考虑一个典型的问题,这个问题非常JIT,但是用原始C很难解决。这个场景是设置一系列函数指针,这些指针将在运行时“组合”(如数学函数组合)一次,然后多次调用 显然,这样做会涉及许多虚拟调用,这是非常昂贵的,如果有足够多的嵌套函数来完全填充CPU分支预测表,那么性能将显著下降 在像Lisp这样的语言中,我可能会处理代码并用函数的实际内容替换“虚拟”调用,然后调用compile,以获得一个优化版本,但这在C中似乎非常粗糙且容易出错,使用C是这个问题的一个要求;-) 那么,你知道在C语言中是否有一种

我在考虑一个典型的问题,这个问题非常JIT,但是用原始C很难解决。这个场景是设置一系列函数指针,这些指针将在运行时“组合”(如数学函数组合)一次,然后多次调用

显然,这样做会涉及许多虚拟调用,这是非常昂贵的,如果有足够多的嵌套函数来完全填充CPU分支预测表,那么性能将显著下降

在像Lisp这样的语言中,我可能会处理代码并用函数的实际内容替换“虚拟”调用,然后调用
compile
,以获得一个优化版本,但这在C中似乎非常粗糙且容易出错,使用C是这个问题的一个要求;-)

那么,你知道在C语言中是否有一种标准的、可移植的、安全的方法来实现这一点吗


干杯

您可能想看看LLVM。他们有一个库,允许从C中JIT代码(以及更多的东西),它支持许多平台,并且是一个开源项目:

我有两个建议来消除虚拟函数调用,如果性能需要的话。为了便于说明,假设有一个函数将函数指针作为参数:

void my_algorithm(int (*func)(...), ...)
{
    /* ... */
}
并且还假设您预先知道函数指针可以采用的所有可能值。例如:

my_algorithm(func_1, ...);
//...
my_algorithm(func_2, ...);
首先将原始my_算法()转换为宏:

#define MY_ALGORITHM(func, ...)       \
{                                     \
    /* ... */                         \
}
然后将我的_算法()重写为:

这当然会使编译的对象文件的大小增加一倍。而且,从表面上看,它只删除了一个间接层次。但是如果func_1和/或func_2是内联的,则可以获得相当大的速度

您甚至可以“传递”宏,如:

#define HYPOT_Y(x) hypot(x, y)
MY_ALGORITHM(HYPOT_Y, ...);     //assumes y is known
第二个建议是使用X宏()的一个变体。将原始my_algorithm()的主体放在单独的文件my_algorithm.h中,而不是定义。然后将我的_算法()重写为:

如果代码超过几十行,我可能会使用X宏。其优点包括(无双关语):

  • 没有难看的反斜杠
  • 更容易调试(例如回溯和单步执行)

这都是标准的C语言,但比较老派。

如果您只支持x86,您可以尝试嵌入TCC:


如果执行此操作,请使用switch语句并传入整数/枚举,而不是使用一堆If/else If/etc。开关速度要快得多。您可以切换常量而不是函数指针。如果不需要函数地址,则根本不获取函数地址,它会强制编译器提供函数的非内联副本,以提供指针。此外,内联(对于大多数C编译器)不能与extern一起工作,因为函数必须包含在同一个编译单元中。不幸的是,情况并非如此,要使用的函数只有在运行时才知道,它们可以以多种不同的方式组合。。。而且仍然有分支^ ^如果事先不知道所使用的函数,就永远不能在编译阶段进行内联。你需要准时制。是的,同意。我的建议只有在“您事先知道函数指针可以接受的所有可能值”的情况下才有效。关于初始分支,如果算法类似于数值积分,其成本可以忽略不计。是的,这是我在考虑的一个选项,但我在考虑更简单的问题,我不认为我需要整个JIT,只是将一些函数粘在一起,就好像它们只是一个大函数一样。无论如何,谢谢:-)遗憾的是,我需要代码能够移植到各种平台:-(不过,感谢链接,看起来可能很有趣!LLVM是您的平台。毕竟,它没有那么糟糕,而且通过普通的C API很容易使用。另一种选择是在C中实现线程化代码Forth intepreter(使用计算的goto GCC扩展),并为其生成代码。应该比下降JIT慢2-6倍,但简单且绝对可移植。它足以将函数粘合在一起。
#define HYPOT_Y(x) hypot(x, y)
MY_ALGORITHM(HYPOT_Y, ...);     //assumes y is known
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()");
}