Mathematica中的性能调优?
您使用哪种性能调整技巧来更快地制作Mathematica应用程序? MATLAB有一个惊人的分析器,但从我所知道的,Mathematica没有类似的功能。
由于Mathematica是一个符号系统,符号评估器比Matlab更普遍,因此性能调整在这里可能会更棘手,这并不奇怪。 有许多技术,但是它们都可以从一个主要原则来理解。 它是:
尽可能避免完整的Mathematica符号评估过程。
所有的技术似乎都反映了它的一些方面。 这里的主要想法是大多数时候,一个缓慢的Mathematica程序就是这样,因为许多Mathematica函数非常普遍。 这种普遍性是一个很大的优势,因为它使语言能够支持更好,更强大的抽象,但是在程序的许多地方,这种普遍性被忽略使用,可能是一个(巨大的)矫枉过正。
我无法在有限的空间中给出很多说明性的例子,但它们可以在几个地方找到,其中包括一些世界资源研究所的技术报告(Daniel Lichtblau关于Mathematica中有效数据结构的报告),这本非常好的书大卫瓦格纳Mathematica编程,最值得注意的是,许多Mathgroup职位。 我在书中还讨论了其中的一小部分。 我会尽快提供更多参考。
这里有一些最常见的(我只列出了Mathematica语言本身的可用内容,不提及CUDA OpenCL,或者链接到其他语言,这当然也是可能的):
尽可能将尽可能多的工作投入到内核中,尽可能多地处理大量的数据,而不会将它们分解成碎片
1.1。 尽可能使用内置函数。 由于它们在内核中以低级语言(C)实现,它们通常(但并非总是)比用户定义的解决相同问题的速度快得多。 您可以使用的内置功能的更专业版本,您有更多机会加速。
1.2。 使用函数式编程( Map, Apply
和朋友)。 另外,如果可以,在#-&
notation中使用纯函数,它们往往比具有命名参数或基于模式的函数(尤其是对于不在大型列表中映射计算密集函数的函数)更快。
1.3。 使用结构化和矢量化操作( Transpose, Flatten, Partition, Part
和朋友),它们甚至比功能更快。
1.4。 避免使用过程式编程(循环等),因为这种编程风格倾向于将大型结构分解成碎片(数组索引等)。 这推动了内核之外的大部分计算,并使其更慢。
尽可能使用机器精度
2.1。 注意并使用内置数值函数的Listability,将它们应用于大型数据列表,而不是使用Map
或循环。
2.2。 Compile
使用Compile
。 使用Compile
的新功能,例如CompilationTarget->"C"
,并使我们的编译函数并行和Listable。
2.3。 只要有可能,在Compile
使用矢量化操作( UnitStep, Clip, Sign, Abs
等)来实现“矢量化控制流”结构,如If
,以便在Compile
内部避免显式循环(至少作为最内层循环)。 在某些情况下,这可以将您从Mathematica字节码转换为几乎原生的C速度。
2.4。 在使用Compile
,确保编译的函数不会退出到非编译的评估。 请参阅此MathGroup线程中的示例。
请注意,列表在Mathematica中以数组的形式实现
3.1。 预先分配大型列表
3.2。 避免Append, Prepend, AppendTo
和PrependTo
循环,用于构建列表等(因为它们复制整个列表以添加单个元素,这导致列表构建的二次方而非线性复杂度)
3.3。 使用链接列表(结构像{1,{2,{3,{}}}}
),而不是普通列表,用于程序中的列表累积。 典型的习语是a = {new element, a}
。 因为a是一个参考,所以一个赋值是恒定的。
3.4。 请注意,序列模式(BlankSequence,BlankNullSequence)的模式匹配也基于序列是数组。 因此,一个规则{fst_,rest___}:>{f[fst],g[rest]}
将在应用时复制整个列表。 特别是,不要以在其他语言中看起来很自然的方式使用递归。 如果你想在列表上使用递归,首先将你的列表转换为链表。
避免低效模式,构建高效模式
4.1。 基于规则的编程既可以非常快速,也可以很慢,这取决于你如何构建你的结构和规则,但是在实践中,不经意间会让它变慢。 对于强制模式匹配器使许多priory注定匹配尝试的规则,例如通过利用长列表(表达式)对模式匹配器的每次运行进行低利用,规则会很慢。 对元素进行排序就是一个很好的例子: list//.{left___,x_,middle___,y_,right___}/;x>y:>{left,y,middle,x,right}
- 具有立方体复杂度列表(例如这里的解释)。
4.2。 构建高效的模式和相应的结构来存储数据,使得模式匹配器尽可能少地浪费在错误的匹配尝试上。
4.3。 避免在计算密集的条件或测试中使用模式。 模式匹配器在模式大部分是句法性质的时候会给你最快的速度(测试结构,头部等)。 每当使用条件(/;)
或模式测试(?)
,对于每个可能的匹配,评估者都会被模式匹配器调用,这会降低它的速度。
注意大多数Mathematica内置函数的不可变性质
大多数Mathematica内置的函数处理列表会创建一个原始列表的副本并对该副本进行操作。 因此,即使它们仅在少数几个地方修改列表,它们在原始列表的大小上可能具有线性时间(和空间)复杂性。 一个不创建副本的通用内置函数修改原始表达式并且不存在此问题,它是Part
。
5.1。 避免将大多数列表修改内置函数用于大量小的独立列表修改,这些修改不能作为单个步骤来制定(例如, NestWhile[Drop[#,1]&,Range[1000],#<500&]
)
5.2。 使用Part
扩展功能来同时提取和修改大量列表(或更一般的表达式)元素。 这是非常快的,而不仅仅是打包数值数组( Part
修改原始列表)。
5.3。 使用Extract
一次提取不同级别的许多元素,并传递给它一个可能很大的元素位置列表。
使用高效的内置数据结构
以下内部数据结构非常有效,并且可以用于比它从其陈述的主要目的中可能出现的更多情况。 很多这样的例子可以通过搜索Mathgroup档案找到,特别是Carl Woll的贡献。
6.1。 打包阵列
6.2。 稀疏阵列
使用散列表。
从版本10开始,不可变关联数组在Mathematica(关联)中可用,
7.1协会
它们不可变的事实并不妨碍它们有效地插入和删除键值对(与给定键值对存在或不存在的原始关联不同的便宜副本)。 它们代表了Mathematica中的惯用关联数组,并且具有非常好的性能特征。
对于较早的版本,基于内部Mathematica的散列表,以下替代方法非常适用:
7.2。 基于DownValues
或SubValues
哈希表
7.3。 Dispatch
使用元素 - 位置对偶
通常你可以写更快的函数来处理元素的位置而不是元素本身,因为位置是整数(对于平面列表)。 即使与通用内置函数相比,这可以使您的速度提高一个数量级(作为示例, Position
可以想到)。
使用收获 - 母猪
Reap
和Sow
提供了一种有效的方法来收集中间结果,并在计算过程中通常“标记”要收集的部分。 这些命令也适用于函数式编程。
使用缓存,动态编程,懒惰评估
10.1。 Memoization在Mathematica中非常容易实现,并且可以为特定问题节省大量执行时间。
10.2。 在Mathematica中,您可以实现更复杂的memoization版本,您可以在运行时定义函数(闭包),这将在其定义中使用一些预先计算的部分,因此速度会更快。
10.3。 有些问题可以从懒惰评估中受益。 这似乎与记忆效率更相关,但也会影响运行时效率。 Mathematica的符号结构使其易于实现。
一个成功的性能调整过程通常采用这些技术的组合,并且您需要一些练习来确定其中每个方法都是有益的情况。
您可以使用Wolfram Workbench中包含的分析器
请参阅2007年Wolfram Technology Conference上有关高效Mathematica程序的演示文稿。
另一个有用的演示文稿是Wolfram语言中的内存高效编码技巧。
链接地址: http://www.djcxy.com/p/35563.html上一篇: Performance tuning in Mathematica?
下一篇: How to fill in CellFrameLabels in Mathematica Notebook Styles?