2.5.14 Mathematica 表达式的编译在定义 f[x_] := x Sin[x] 函数时,Mathematica 将表达式 x Sin[x] 按能对所有的 x 求值的方式保存. 然后当你给出了 x 的一个值时,Mathematica 就将这个值代入表达式 x Sin[x] 中计算出结果. 不管 x 的指定值是一个数, 一个列表、一个代数元素,或者是其它形式的表达式,用来进行计算的 Mathematica 内部代码使其工作过程一样有效. 要考虑到所有这些可能性时计算过程就会变慢. 但如果 Mathematica 能将 x 看作一个机器数时,这将省掉许多步骤,从而使 x Sin[x] 的计算相当快. 使用 Compile 可以在 Mathematica 中产生编译后的函数,这个函数在 计算表达式时假设所有出现的参数都是数 (或逻辑变量). Compile[ , , ... , expr] 使用表达式 expr 并产生一个 "编译过的函数",当给出了变量 , , ..., 的值后,所编译的函数就计算表达式的值. Compile一般产生一个 CompileLedFunction 目标, 该目标包含一系列计算所编译函数的一系列 简单指令. 这些指令与典型计算机中的机器代码很接近,所以执行得很快. 产生编译后的函数 定义 f 是计算 x Sin[x] 的纯函数
Out[1]= |  |
产生计算 x Sin[x] 的编译函数
Out[2]= |  |
f 和 fc 的结果相同,但当变量是数时,fc 运行很快
Out[3]= |  |
在计算数量逻辑表达式许多次时 Compile 是非常有效的. 花一点时间调用 Compile 后, 所得到的编译函数比任何普通的 Mathematica 函数执行得更快.对形如 xSin[x] 这样的简单表达式,普通函数和编译函数的执行速度几乎没有 什么差别. 但当所涉及表达式的数量增加时,编译函数就显示出了其优越性. 对大型的表达式编译可以提高速度 20 倍. 当表达式含有大量简单的算术运算和函数时, 编译就有很大的差异. 对 BesselK 和 Eigenvalues 算复杂函数,大部分时间花在执行 Mathematica 的内部程序上,此时编译没有效果. 这里产生一个计算 10 阶 Legendre 多项式值的编译函数. Evaluate 令 Mathematica 在编译之前构造多项式
Out[4]= |  |
当自变量为 0.4. 时计算 Legendre 多项式的值
Out[5]= |  |
这里使用了内部代码
Out[6]= |  |
即使用编译可以加快数值函数的计算速度,但应该尽可能地使用 Mathematica 的内部函数. 通常内部函数比用户能产生的任何编译过的程序执行 得更快,另外它们使用许多算法,容易控制且计算精度高. 应该知道 Mathematica 的内部函数经常使用 Compile. 例如 NIntegrate 自动对要积分的表达式使用 Compile,Plot 和 Plot3D 对要绘图 的表达式也自动使用 Compile. 使用 Compile 的内部函数一般都有可选项 Compile. SettingCompiled->False 让函数不使用 Compile. 指定编译类型 编译时要假设在所给表达式计算过程中出现的对象的类型.默认值是假设 表达式的所有变量是近似实数. Compile 使用整数, 复数 (True 或 False) 及阵列. 可以通过与某一类型的值匹配的方法来指定某一个变量. 例如,用模式 Integer 指定整型数,用 True|False 指定逻辑型变量. 在 i 和 j 都是整型的假设下编译表达式 5i+j
Out[7]= |  |
这里给出整数结果
Out[8]= |  |
这里编译一个元素是整数的矩阵运算
Out[9]= |  |
按照编译后的方式进行一系列运算,其结果是一个整数
Out[10]= |  |
Compile 处理的类型基本上与机器代码水平上计算机处理的类型相对应. 例如,Compile 能处理机器精度内的近似实数,但不能处理任意精度的实数. 另外,如果指定某变量是整数,Compile 产生的代码仅能处理机器长度之内的整数, 该长度通常在 之间. 当编译的表达式仅涉及标准算术运算和逻辑运算时,Compile 能简单地从 输入变量的类型推断每一步产生的对象的类型. 但如果调用其它函数时,Compile 一般就无法推断其返回值的类型. 当用户不指定时,Compile 假定其它函数产生一个 近似实数值.当然可以直接给出一个模式列去确定与一个模式匹配的表达式的类型. 定义一个函数,当自变量是整数时其值也为整数 假设 com[_] 总是整数, 编译 x^com[i]
Out[12]= |  |
计算所编译函数的值
Out[13]= |  |
Compile 的思路是对自变量的某一类型产生一个优化后的函数. 但 Compile 的构造使得 它所产生的函数对任何类型的自变量都能使用. 当无法优化时,就通过计算一个标准的 Mathematica 表达式来得到函数的值. 求变量平方根的编译函数
Out[14]= |  |
给出实数自变量时,就使用优化代码
Out[15]= |  |
所编译的代码不能用,Mathematica 给出一个警告,然后计算原来的符号表达式
Out[16]= |  |
由Compile 产生的编译代码不仅要对所使用的自变量类型进行假设,而且也要 对执行过程中产生目标的类型进行假设. 有时这些类型依赖于所给自变量的实 际值. 例如,Sqrt[x] 当 x 为非负数时的值为实数,而当 x <0 时为复数. Compile 总是对一个函数的返回值类型进行确定的假设. 当这假设在 Compile 产生 的代码执行过程中不成立时,Mathematica 就放弃编译过的代码,并计算普通的 Mathematica 表达式去得到结果. 编译的代码不能得到复数结果,所以 Mathematica 必须直接计算原来的符号表达式
Out[17]= |  |
Compile 的一个重要特点是它不但可以处理数学表达式,还可以处理各种 简单的 Mathematica 程序.例如,Compile 可以处理条件和控制流结构. 在 所有情况下,Compile[vars, expr] 保持其变量不计算,这意味着可以通过对表达式的编译给出一个程序. 这里产生了一个牛顿法求平方根的 Mathematica 程序的编译形式
Out[18]= |  |
执行编译的代码
Out[19]= |  |
|