运行时动态函数解析

Iff*_*ffi 3 c++ templates jit metaprogramming

我的项目在运行时需要加载很多模块,每个模块都包含很多函数,形式类似于下面的伪代码:

void someFunction(Context &ctx) {
    bool result;
    result = ctx.call("someFunction2")(ctx.arg["arg1"], ctx.arg["arg2"])
             && ctx.call("someFunction3")(ctx.arg["arg1"], ctx.arg["arg3"]);
    ctx.result(result);
} 
Run Code Online (Sandbox Code Playgroud)

其中ctx.arg["arg1"]ctx.arg["arg2"]ctx.arg["arg3"]是传递到的参数someFunction在运行时。someFunction2并且someFunction3无法在编译时静态解析,但会在运行时在加载所有模块时知道(它们是否已在其他模块中定义)。

现在,一个简单的实现将使用散列映射来存储所有这些函数的函数句柄,但散列会很慢,因为通常需要搜索 10k 个函数,并且每个函数将在其他函数中被多次调用(例如:枚举参数以找到将产生所需结果的正确组合)。

因此,我正在寻找某种解决方案,它会在加载所有模块时对这些“ctx.call”执行一次替换,而不是每次都执行“散列和探测”。目前的主要问题是“替换”操作。我提出了一些想法,但它们并不完美:


第一个解决方案:创建一个内部函数inner_func(func_handle1, func_handle2, arg1, arg2, arg3),并用于std::bind创建一个外部包装器outer_wrapper()

问题:不是用户友好的,必须明确告诉上下文要查找哪些函数和参数。


第二种解决方案:使用元编程+ constexpr + 宏来自动统计函数和参数名称的引用,然后创建一个引用表,然后让上下文在运行时填充每个表。

问题:我无法解决,需要一些帮助。我已经阅读了来自 facebook、mpl 和 hana 的 Fatal 库的文档,但似乎没有一种干净的方法可以做到这一点。


第三种解决方案:使用 JIT 编译器

问题:c++ JIT 编译器的选择是有限的。NativeJIT 不够强大,easy::JIT 似乎不能定制,也不容易分发。asmjit 不可用。


PS:问题上下文是“自动规划器”,这些函数是用来构造谓词的。Context ctx只是一个例子,如果需要,您可以使用其他合适的语法,只要它们易于用于表示以下 lisp 表达式:

(and (at ?p ?c1)
(aircraft ?a)
(at ?a ?c3)
(different ?c1 ?c3))
Run Code Online (Sandbox Code Playgroud)

PPS:更具体地说,我正在考虑这样的事情:

用户将定义一个如下所示的函数:

void module_init() {
    FUNC ("someFunction")("p", "a", "c1", "c3") (
        bool result;
        result = CALL("at")("p", "c1") 
                 && CALL("aircraft")("a")
                 && CALL("at")("a", "c3")
                 && CALL("different")("c1", "c3")

        /// Users should also be able to access arguments as a "Variable" 
        /// class using ARG["p"]
        return result;
    )
}
Run Code Online (Sandbox Code Playgroud)

然后通过某种方式,FUNC()将转换为类似于以下内容的函子:

struct func_someFunction {
    some_vector<std::function<bool()>> functions;
    some_vector<Variable*> args;
    some_vector<std::string> func_name;
    some_vector<std::string> arg_name;

    bool operator()() {
       /// above representation of Func(), but function and args are pointers in "functions" and "args"
    }
}
Run Code Online (Sandbox Code Playgroud)

然后当所有模块加载完毕后,系统将读取func_namearg_name,并分别填充相应的函数指针和变量指针到functionsargs

状态:首先使用hashmap,完成后我会发布更新。

状态:自己想出了一个解决方案,也测试了哈希实现,发布在下面。

任何想法将不胜感激。谢谢!

Pau*_*ans 5

现在,一个简单的实现将使用散列映射来存储所有这些函数的函数句柄,但散列会很慢,因为通常有 10k 个函数要搜索 [...]

哈希表的查找成本为O(1)。您是否尝试过这种广泛使用的解决方案并进行了性能分析?您是否尝试过使用不同的散列算法来减少散列时间和冲突?

  • 这是一个很好的答案,但有一些重要的警告。首先,O(1) 查找只能通过良好的(统一)哈希策略以及适当的哈希表大小和负载因子来实现。其次,对 *strings* 的 O(1) 查找仍然需要迭代字符串来计算哈希值。幸运的是,这两个问题都在解释器的上下文中建立了解决方案,因为一旦加载模块,表基本上将被“固定”,因此可以优化大小。并且可以通过字符串驻留大大加快字符串查找速度,这使得哈希计算成为指针查找。 (2认同)