[翻譯]關於Mathematica提速的幾點技巧(一)


本文翻譯自Wolfram Blog的一篇文章http://blog.wolfram.com/2011/12/07/10-tips-for-writing-fast-mathematica-code/,我的英語實在是差,翻譯很爛看官們就忍忍吧.

當人們跟我抱怨說Mathematica不夠快時,我一般會叫他們去仔細瞧瞧那些令人惱怒的代碼.跑得慢的原因通常不是Mathematica的性能問題,而是使用方式不太合適.我想我應該分享一些我在優化Mathematica代碼時使用的技巧.

 1.如果可能話,使用機器精度數並盡量提早使用

如我所見,那些漫不經心的程序員所編寫的緩慢的代碼的一個共同特點是,一下子讓Mathematica考慮到了太多的瑣事而忽略自己的實際需求.不必要的附加計算是導致代碼緩慢的最普遍原因.

大部分的數值計算軟件中沒有這些附加計算,1/3和0.33333333333333被認為是相等的.這些附加計算在你遇到一些復雜難纏的計算時是很有用的.但是在大部分的計算任務中,浮點運算的機器精度已經足夠好,更重要的是,計算速度更快.任何小數位數小於16位的十進制輸入都會自動被Mathematica按機器精度處理,所以,如何你的計算原則是速度優先,那么就盡可能使用機器精度的小數而不是精確形式(例如1/3).這里有一個簡單的例子說明直接使用浮點數進行機器精度的運算會比先用絕對精確形式計算然后轉換為小數的方法快50倍左右.當然,我們這個例子里得到的結果是相同的.

 

這個結果對於符號計算也是成立的.如果你不在乎符號形式的結果和穩定性,那么盡可能早地使用浮點數.例如,在解這個多項式方程之后為系數賦值以避免長達5頁的符號形式結果.

但是如果你在解方程之前就賦值,函數Solve會直接使用速度飛快的數值計算方法.

當你對表中的數據進行處理時,一定要保證所用的數據都是浮點小數形式的.某一個數據形式的不同就會導致整個表中的數據用另一種靈巧有余而效率不足的方式存儲.

 

32000072

2. 學習如何使用Compile…

函數Compile允許你對所編譯的函數的參數的類型(實數,整數,復數…)和結構(數值,,矩陣…)做預先聲明.這樣做損失了Mathematica語言的靈活性,但是另一方面消除了如果參數是符號形式的該怎么辦?”的焦慮.同時Mathematica也可以優化程序生成在其虛擬機上運行的高速字節碼.不是所有的函數都能被編譯,過於簡單的代碼也可能從編譯中得不到好處,但是復雜的低級數值運算代碼可以得到極大的速度提升.

這里有一個例子:

arg = Range[ -50., 50, 0.25];

fn = Function[{x}, Block[{sum = 1.0, inc = 1.0}, Do[inc = inc*x/i; sum = sum + inc, {i, 10000}]; sum]];

Map[fn, arg]; // AbsoluteTiming

{21.5597528, Null}

Compile代替Function使計算速度提高了80.

cfn = Compile[{x}, Block[{sum = 1.0, inc = 1.0}, Do[inc = inc*x/i; sum = sum + inc, {i, 10000}]; sum]];

Map[cfn, arg]; // AbsoluteTiming

{0.2652068, Null}

為了得到更高的性能,我們可以對Compile做進一步的關於並行運算的調整.

cfn2 = Compile[{x}, Block[{sum = 1.0, inc = 1.0}, Do[inc = inc*x/i; sum = sum + inc, {i, 10000}]; sum], RuntimeAttributes → {Listable}, Parallelization → True];

cfn2[arg]; // AbsoluteTiming

{0.1404036, Null}

,在我的雙核電腦上,速度比原始代碼快樂足足150.如果我的CPU有更多核的話速度會更快.

讀者們要意識到很多Mathematica函數比如Table,Plot,NIntegrate會自動進行編譯,所以再使用Compile也不會看到一點效率上的提高.

 

2.5利用Compile生成C代碼.

更進一步,如果你的代碼是可編譯的話,你可以使用CompilationTarget->”C”選項生成C代碼,它會自動調用你的C編譯器生成動態鏈接庫(DLL),並鏈接回Mathematica.這樣做會在編譯階段花費更多的時間,但是動態鏈接庫能直接運行在CPU上而不是Mathematica虛擬機,所以速度會更快.

cfn2C = Compile[{x}, Block[{sum = 1.0, inc = 1.0}, Do[inc = inc*x/i; sum = sum + inc, {i, 10000}]; sum], RuntimeAttributes → {Listable}, Parallelization → True, CompilationTarget → "C"];

cfn2C[arg]; // AbsoluteTiming

{0.0470015, Null}

 

3. 盡可能使用內置函數.

Mathmatica中函數眾多.大部分人不會坐下來一個接着一個地學習這些函數.所以當我看到一些人拼命地實現一些特定功能而沒意識到Mathematica早就用內置函數實現其功能時也就不感到奇怪了.這樣重新造輪子的行為不僅浪費時間,而且我們這些人的工作就是設計做好的算法以應對各種輸入並保證效率,所以絕大部分的內置函數是非常快的.

如果你發現你的代碼已經很不錯了但是好像還欠了點什么,查看一下選項和附加參數;通過它們能獲得很多特定的功能和抽象的應用.

有一個這樣的例子.比如我想把一個100萬個的2階矩陣轉換為100萬個一維列表,最容易想到的方法就是使用MapFlatten的組合.

data = RandomReal[1, {1000000, 2, 2}];

Map[Flatten, data]; // AbsoluteTiming

{0.2652068, Null}

其實整個任務只要Flatten一個函數就可以做到.找到這個方法有點浪費時間,但這是值得的,整個任務的速度比比你的自己造的輪子快了接近4.

Flatten[data, {{1}, {2, 3}}]; // AbsoluteTiming

{0.0780020, Null}

謹記在心---自己造輪子之前先去看看幫助.

(待續)

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM