错误: 至少有一个需要的隐性或转发依赖函数没找到。_实现一个自己的 Compile...

博主分享了自己实现的Mathematica编译器MathCompile,旨在解决内置编译器的局限性。MathCompile将Mathematica代码转换为C++,利用现代C++特性并依赖外部编译器进行优化,以提高性能。通过实例展示了编译过程和使用方法,包括编译成C++代码和二进制库。博主还提到MathCompile在某些测试中性能优于内置编译器,并提供了性能对比和代码示例。
摘要由CSDN通过智能技术生成

编译(Compile)是 Mathematica 里的提高运算速度的一个重要的工具,但是用过 Compile 的人都知道它并不是很好用,比如基本不能在 Compile 里用函数式的写法。我之前并不在意这个问题,一是对我来说与其用 Compile 不如直接写 C++,二是当时听说 v12.0 会有一个用全新架构的编译器叫 FunctionCompile。直到 v12.0 发布,我发现 FunctionCompile 虽然对于函数式编程的支持提升了很多,但是问题还是不少,比如 bug 太多,输出的错误信息很难看懂,而且编译之后的性能还不如原来的 Compile。

为了彻底解决编译问题,我自己实现了一个编译器

njpipeorgan/MathCompile​github.com
17788e8515bbdb085bf23459b8e68cb4.png

可以从这里下载到最近发布的版本。这个包的使用指南在这里,可编译的函数在这里。这个项目目前还在进行中,如果你有任何建议,欢迎在这里或者 github 上反馈。

MathCompile 简介

要安装 MathCompile,在这个 GitHub 发布页面上找到最新的版本,然后在 Mathematica 中运行相应的 PacletInstall 命令,就完成了。

我们用其中的一个函数 CompileToCode 测试是否安装好了,它的功能是把 Mathematica 函数编译成 C++ 代码。

<<MathCompile` (* 导入包 *)

f = Function[{Typed[x, Integer]}, x + 2]; (* 定义一个函数 *)
CompileToCode[f]   (* 编译成 C++ 代码 *)

其中 Typed[x, Integer] 表示函数的参数是一个名为 x 的整数。编译输出的结果应该是一个字符串,差不多是这样的:

auto main_function(const int64_t& v75) {
    return wl::val(wl::plus(WL_PASS(v75), int64_t(2)));
}

MathCompile 进一步的编译功能需要一个外部的 C++ 编译器,目前支持 GCC,Clang,ICC 和 MSVC。在这里有关于具体支持那些版本和如何设置编译器的介绍。在类 Unix 系统上一般不需要配置,Mathematica 会自动找到 GCC 或者 Clang;而在 Windows 上可以安装 MinGW-w64 然后进行相应配置。

有了 C++ 编译器后就可以用 CompileToBinary 函数了,它的作用是把 Mathematica 函数编译成二进制库然后链接回 Mathematica:

f = Function[{Typed[x, {Integer, 1}]}, Times @@ x]; (* 定义一个函数 *)
compiled = CompileToBinary[f];   (* 编译成二进制 *)

其中 Typed[x, {Integer, 1}] 表示函数的参数 x 是一个整数的列表(数组的维数为 1)。此时编译的结果可以直接当作函数调用,

compiled[{1, 2, 3, 4}]  (* 输出 24 *)

MathCompile 是怎么实现的?

MathCompile 的编译过程是先把 Mathematica 代码翻译成 C++ 然后借助 C++ 编译器完成后续的工作(原生的 Compile 是通过 C,FunctionCompile 则是通过 LLVM)。

生成 C++ 代码的部分是在 Mathematica 中完成的。包括了对一些特殊的结构做转换,比如 Table,If,Module,还包括少量的语义分析。因为我的思路是把尽可能多的工作放在 C++ 的一端完成,所以我在 C++ 中实现了所有支持的函数,而且类型推导之类的工作是由 C++ 编译器完成的。这就形成了一个有趣的现象,虽然这个编译器名义上是用 Mathematica 写的,但是这个包中的 C++ 代码远多于 Mathematica 代码。

这个项目的形成完全得益于主流编译器对 C++14 和 C++17 的支持。比如考虑如下 Mathematica 代码:

...
f = If[x > y, (# + 1)&, (# - 1)&];
f[5]

有了 C++14,这段代码就可以几乎不加改动地翻译成 C++,大大降低了项目的复杂度:

...
auto f = [condition = (x > y)](auto arg) {
    if (condition)
        return ([](auto x) { return x + 1; })(arg);
    else
        return ([](auto x) { return x - 1; })(arg);
};
return f(5);

你可以在这里找到我对 If 的实现。

MathCompile 的性能如何?

在 C++ 编译器能进行充分优化的情况下,MathCompile 在理论上有和原生 C++ 基本等同的性能。我做的一些测试显示出 MathCompile 性能至少持平内置编译器,在少数测试中则快十倍以上。比如在 Mandelbrot 集的计算上,MathCompile 相比内置编译器快了 5 倍左右,具体的代码和结果可以在这里找到。

MathCompile 相比内置编译器的性能优势主要来自去除了对 Wolfram 运行时库的依赖,C++ 编译器能看见更多细节然后做优化,消除了很多函数调用。其次是 MathCompile 生成的 C++ 代码从结构上接近“正常”的代码,方便了 C++ 编译器的优化,而没有像内置编译器一样用 goto 组织循环。

一些编译的例子

  1. 进行牛顿迭代并且指出最近的根 ref/Compile
f=CompileToBinary@
  Function[{Typed[z,Complex],Typed[n,Integer]},
    If[Re[#]>0,1,If[Im[#]>0,2,3]]&@Nest[(2#+1/#^2)/3&,z,n]];

ArrayPlot[Table[f[x+I y,25],{y,-1,1,2./199},{x,-1,1,2./199}]]

2. 用一个复杂的方式 Flatten 一个数组

f=CompileToBinary@
  Function[{Typed[x,{Integer,4}]},Flatten[x,{{1,3},{2,4}}]];

f[ArrayReshape[Range[16],{2,2,2,2}]]

3. 用递归实现阶乘 Code Compilation

f=CompileToBinary@
  Function[{Typed[x,Integer]},
    Module[{fac=Typed[{Integer}->Integer]},
      fac=If[#<=1,1,# fac[#-1]]&;
      fac[x]
    ]
  ];

f[5]

递归函数需要指定类型,Typed[{Integer} -> Integer] 表示函数 fac 的参数是一个整数,并返回一个整数。

4. 计算一组随机数的平均值

f=CompileToBinary@
  Function[{Typed[x,Integer]},
    Mean@RandomVariate[ChiSquareDistribution[x],10000]];

f[30]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值