将 Julia 函数转换为 C 代码¶
在本例中,我们将演示一种技术,它可以让您从 Julia 创建的函数生成 C 代码。
任务描述¶
假设我们想将 Julia 上的代码移植到另一个平台上,而在该平台上编译代码并不方便。例如,在微控制器上。与此同时,我们也希望能在 Engee 内部的模型循环中保留运行相同代码的能力。如何继续?
让我们使用多态机制。你可以在代码中输入数字,然后它就会执行并计算出数字结果。你可以输入矩阵,然后结果将是矩阵。让我们输入符号变量,看看会得到什么。
In [ ]:
Pkg.add(["Symbolics"])
In [ ]:
Pkg.add( "Symbolics" )
从简单函数生成代码¶
让我们考虑从一个返回一个数字的非常简单的函数中生成代码:
In [ ]:
simple_fcn(x1, x2) = 2 .* x1 + sin( x2 )
simple_fcn(1, 2)
Out[0]:
在全局级别声明两个变量x1
和x2
,我们可以用它们进行符号运算。
In [ ]:
using Symbolics
@variables x_1 x_2
Out[0]:
如果我们将符号变量替换为简单函数的参数,我们将不会看到任何异常(除了我们将看到嵌入函数中的方程):
In [ ]:
simple_fcn( x_1, x_2 )
Out[0]:
但我们会得到一个结构良好的对象,并从中生成代码:
In [ ]:
c_model_code = build_function( simple_fcn( x1, x2 ), [x1, x2]; target=Symbolics.CTarget(), fname="SIMPLE_FCN", lhsname=:c, rhsnames=[:x1, :x2] )
println( c_model_code )
我们得到了针对特定 "目标 "优化的代码CTarget
。
这些代码可能需要进行各种修改。例如,翻译成float
类型或更改函数名称(exp
到expf
),以满足您的平台所使用标准的要求。或者添加函数main
。
此外,还可以将代码保存到文件simple_function.c
中,并使用命令gcc -o out simple_function.c -lm
进行编译。
In [ ]:
c_code_standalone = """
#include <stdio.h>
$c_model_code
int main(int argc, char const *argv[]){
double out[1];
double in[] = {1, 2};
SIMPLE_FCN( out, in );
printf( "%f\\n", out[0] );
return 0;
}""";
# Сохраняем в файл доработанный код
open("$(@__DIR__)/simple_function.c", "w") do f
println( f, "$c_code_standalone" )
end
编译得到的代码:
In [ ]:
;gcc -o out simple_function.c -lm
检查生成的二进制文件是否产生了与原始函数相同的值:
In [ ]:
;./out
结果与我们在示例开始时计算的结果一致。
结论¶
我们在 Julia 中生成了一个非常简单函数的代码,并在 Engee 中编译和执行,我们也可以把它放在C Function
块中,以简化半自然测试。
值得注意的是,并不是所有代码都能通过这种方法轻松翻译成 C 代码,通常需要对翻译程序进行重大修改,以使代码能够接受矩阵作为输入。但这种方法可以生成相当大的算法子类,在半自然建模场景中非常有用。