Matlab 連結 C/C++ 動態函式庫

| 元月 05, 2006 | 7個迴響| 沒有引用| 6762 reads | Size: L, M, S | Edit: P, C, R |
| Category: Programming. |

    剛剛在網路上看到的,還滿特別的使用方法。

    通常,要利用 MATLAB 矩陣運算的方便,和 C/C++ 的高效能,都會使用 MATLAB 的 C Libary。不過剛剛看到這的這篇,原來還可以這樣玩,大開眼界了。

    原文在此:上篇下篇

    怕文章不見,轉錄在內文。

    UPDATE 2006-12-27: 看來作者也受 天空事件 影響,所以自架主機。修正一下連結,感謝告知! 

    「程式開始跑了,我們去喝個下午茶再回來吧…」
    「記憶體又不夠了,老闆,我想買RAM…」

    只要你是 Matlab 重度使用者,相信你都遇過前面兩個問題。即使 7.0 版已經大幅改善速度,但執行時間永遠都嫌慢。「向量化」這個老問題,總是使得需要的執行記憶體大幅膨脹。偏偏 Matlab 內建的眾多運算函式,再再讓你無法脫離它的魔掌。

    受不了了嗎?解決之道之一就是「連結 C/C++ 動態函式庫」!

    在決定使用 C/C++ 函式庫之前,你應該先用 profile reporter 確定你程式的效能瓶頸,這通常是一個被呼叫非常多次,或需要非常大記憶體的函式。確認效能瓶頸後,才針對函式製作 C/C++ 版本。

    【撰寫C/C++函式】

    製作 C/C++ 版本函數並不困難。絕大多數情況下,需要撰寫的,僅是一個「在 Matlab 與 C/C++  之間轉換變數」的中界函數。

    1. 引入標頭檔 "mex.h"

    在 C/C++ 函式的前端,加入

    #include "mex.h"

    這是包含有 Matlab 矩陣型態定義及協助函式 prototype 的標頭檔。

    2. 撰寫 mexFunction 函數

    mexFunction 函數為程式的進入點,它有規定好之 prototype ,不可隨意更動:

    void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]);

    這個函數包含四個引數,分別代表呼叫該函式時,輸出與輸入之矩陣。我們知道 Matlab 函式允許多個參數輸入與多個參數輸出,例如:

    [M, I] = larger(A, B);

    但 C/C++ 語言僅允許單一參數輸出,因此 mexFunction 必須將函式等號左邊之輸出參數移到右邊,方可順利輸出參數。mexFunction中四個參數分別代表:

    nlhs:呼叫函式時,等號左邊的變數數目,即 plhs 之個數。
    plhs:呼叫函式時,等號左邊之變數本體。
    nrhs:呼叫函式時,等號右邊的變數數目,即 prhs 之個數。
    prhs:呼叫函式時,等號右邊之變數本體。

    在上例中,nlhs=nrhs=2。若呼叫

    M = larger(A, B);

    則 nlhs=1,nrhs=2。

    Matlab 中所有的矩陣型態,在 C/C++ 中都以 mxArray 型態表示。 plhs 及 prhs 之變數型態均為 mxArray **,即為「指向 Matlab 矩陣之指標陣列」,在兩個輸出參數的例子中,plhs[0] 指向 M ,而 plhs[1] 指向 I ,prhs[0] 指向 A ,prhs[1]則指向 B。

    3. 將 Matlab 矩陣轉換為 C/C++ 陣列

    Matlab 提供一系列函式,協助你將 Matlab 矩陣轉換為 C/C++ 之陣列。此處僅介紹必須用到的幾個函式,其他的請參考 Matlab 手冊中 MX-Functions 的部分。

    ˙mxGetPr:取得矩陣之實數部分。

    利用該函數可將矩陣之實數部分取出,函式將回傳一個 double * 指向資料的開頭。無論原始矩陣的維度(dimension)是多少,在 C/C++ 中均以一維陣列表示,故使用時必須自行計算正確之index。函數的 prototype 為

    double *mxGetPr(const mxArray *array_ptr);

    另有類似函數 mxGetPi ,可取得矩陣之虛數部分。

    ˙mxGetM:取得矩陣之第一維大小

    該函數將傳回一整數(int型態),為矩陣之第一維大小。其 prototype 為

    int mxGetM(const mxArray *array_ptr);

    第二維大小則可由 mxGetN 取得。若為多維矩陣,請參考 mxGetNumberOfDimensions 及 mxGetDimensions 函數。

    搭配組合上述函式,我們可以把

    [M, I] = larger(A, B);

    之 A 引入 C/C++ 內:

    double *A;
    int mA, nA;
    A = mxGetPr(prhs[0]);
    mA = mxGetM(prhs[0]);
    nA = mxGetN(prhs[0]);

    如此便可使用 A[i] 來存取輸入矩陣了。

    4. 配置輸出矩陣之記憶體空間

    函數之輸出矩陣,亦必須是 Matlab 矩陣,其大小及內容是由 C/C++ 函式決定,故記憶體空間亦須函式配置。

    ˙mxCreateDoubleMatrix:創造一個二維 Matlab 矩陣

    利用本函數,可以創造一個任意大小、實數或複數型態的二維 Matlab 矩陣。其 prototype 為

    mxArray *mxCreateDoubleMatrix(int m, int n, mxComplexity ComplexFlag);

    我們需給定矩陣的大小(m-by-n),以及矩陣的型態(mxREAL或mxCOMPLEX擇一),函式會創造一個 Matlab 矩陣,配置足夠的空間,並回傳一個 mxArray 指標指向新矩陣。

    若要創造多維矩陣或非實數或虛數型態之矩陣,參考 mxCreateNumericArray 以及 mxCreateNumericMatrix 。

    現在我們可以配置 M 與 I 的空間了:

    double *M, *I;
    plhs[0] = mxCreateDoubleMatrix(mA, nA, mxREAL);
    M = mxGetPr(plhs[0]);
    plhs[1] = mxCreateDoubleMatrix(mA, nA, mxREAL);
    I = mxGetPr(plhs[1]);

    我們將 M 與 I 設定為與 A 相同大小之實數矩陣,並取出其資料指標備用。

    現在我們已經將參數引入 C/C++ 中,亦配置好輸出參數的空間,接下來可依正常 C/C++ 語法撰寫函式的功能部分。

    下一篇文章我們將討論如何編譯撰寫好的 C/C++ 函式庫。

    最後列出完整的 mexFunction 供參考,該函數接受兩個輸入 A 及 B ,輸出 M 的每一個元素,取自 A 與 B 相對位置中較大的數值,以及一個索引 I 表示 M 的元素取自 A 或 B:

    #include "mex.h"
     
    void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
    {
      double *A, *B, *M, *I;
      int m, n, s, i;
     
      A = mxGetPr(prhs[0]);
      B = mxGetPr(prhs[1]);
      m = mxGetM(prhs[0]);
      n = mxGetN(prhs[0]);
     
      plhs[0] = mxCreateDoubleMatrix(m, n, mxREAL);
      M = mxGetPr(plhs[0]);
      if(nlhs>1) {
        plhs[1] = mxCreateDoubleMatrix(m, n, mxREAL);
        I = mxGetPr(plhs[1]);
      }
     
      s = m*n;
      if(nlhs==1) {
        for(i=0; i<s; i++) {
          if(A[i]>=B[i]) M[i] = A[i];
          else M[i] = B[i];
        }
      } else {
        for(i=0; i<s; i++) {
          if(A[i]>=B[i]) {
            M[i] = A[i];
            I[i] = 0;
          } else {
            M[i] = B[i];
            I[i] = 1;
          }
        }
      }
    }

     

     

    我們已經寫好了 C/C++ 函式庫,接下來要把它編譯成 DLL 檔。


    我們仍然以前文的程式為例子,我們將 C/C++ 函式庫存檔為 larger.c ,開啟 Matlab 並將工作目錄切換至 larger.c 存檔的目錄。

    在 Matlab 下編譯 C/C++ 函式庫的指令為 mex ,但在使用 mex 之前,必須先設定編譯器的種類和參數。若你是第一次使用 mex ,系統會自動提示你選擇一個編譯器:

    >> mex
    Select a compiler:
    [1] Lcc C version 2.4 in C:PROGRAM FILESMATLAB704syslcc 
     
    [0] None
     
    Compiler:

    mex 會自動偵測並列出你的電腦中已安裝的合適編譯器, 由於我的電腦並沒有安裝 C/C++ 編譯器,因此 mex 僅列出 Matlab 內建之 Lcc 編譯器。若你有安裝其他編譯器,請選擇你偏愛的編譯器。這個步驟僅需設定一次, Matlab 會記下你選擇的編譯器,以後都會自動使用,若需選擇不同的編譯器,你必須執行:

    >> mex -setup

    指令,重新選擇一個編譯器。

    選擇好編譯器之後,便可以編譯我們的程式,只需要輸入

    >> mex larger.c

    若程式順利結束,現在你應該可以在同一目錄下看見 larger.dll ,這便是編譯好的函式庫。

    接下來我們來測試編譯好的函式:

    >> A=[1 2 3; 4 5 6; 7 8 9];
    >> B=[9 8 7; 6 5 4; 3 2 1];
    >> [M,I]=larger(A, B);
    >> M

    M =

         9     8     7
         6     5     6
         7     8     9

    >> I

    I =

         1     1     1
         1     0     0
         0     0     0

    >>

    你也可以測試一下 M=larger(A,B); 是否能正常工作。接著嘗試一下:

    >> larger(A,B)

    ------------------------------------------------------------------------
           Segmentation violation detected at Sat Dec 10 09:52:22 2005
    ------------------------------------------------------------------------

    (以下刪除)

    出現了一大串的錯誤,這是因為函式庫沒有考慮「沒有指定任何輸出」,即 nlhs=0 時的情形。一般來說,函式在沒有指定輸出的情況下,應傳回一個輸出參數。因此,我們只需要將

    if(nlhs==1)

    修正為

    if(nlhs<=1)

    即可解決這個問題。

    現在你已經會使用 C/C++ 撰寫及編譯 Matlab 函式庫了,開始享受 Matlab 的便利和 C/C++ 的高執行效率吧。

     


    隨機文章:
    » 撰寫快速的 MATLAB 程式碼
    » Del.icio.us、Furl、NewsGator 的 聯播 Linkroll
    » 在文章上加入「Add To Del.icio.us」「Add To Furl」連結
    » LifeType 與 中部的學校
    » LifeType - pLog 的新名字


Leave A Comment:













   


7 迴響 of "Matlab 連結 C/C++ 動態函式庫"

asglayRe:Matlab 連結 C/C++ 動態函式庫 By asglay @ 2006 九月 02, 21:58 :

這篇文章很棒!!!

給你最高的推薦~

蔡少Re:Matlab 連結 C/C++ 動態函式庫 By 蔡少 @ 2006 十一月 29, 13:35 :

這篇文章真的超棒的!!
剛好有需要,感恩阿~~

蘋果王子Re:Matlab 連結 C/C++ 動態函式庫 By 蘋果王子 @ 2006 十二月 26, 18:22 :

你好,

我是文章原作者,謝謝你引用我的文章,因為部落格網址已經改變,想麻煩您修改上面的引用網址。

新的 blog 在 http://appleprince.tw/

兩篇文章的,永久連結為:
link 1
link 2

謝謝唷。

PS. 先前選擇 web hosting 時也來你這邊看過文章哩,受益良多。

蘋果王子Re:Matlab 連結 C/C++ 動態函式庫 By 蘋果王子 @ 2006 十二月 26, 18:23 :

另外我文章的內容有做過修正,主要是順順語法,可以參考看看。 :)

ajer001Re:Matlab 連結 C/C++ 動態函式庫 By ajer001 @ 2006 十二月 27, 09:50 :

Hi
感謝告知,已經修正連結了!

蘋果王子Re:Matlab 連結 C/C++ 動態函式庫 By 蘋果王子 @ 2006 十二月 27, 12:16 :

你猜對啦,我是被 天空事件 嚇到了。想想還是有自己的網址跟主機來的好,不用受制於人,想加什麼外掛都可以。我之前留下的網址太長,似乎破壞了版面了,可以修嗎?不能修的話你就把它刪了吧。

ajer001Re:Matlab 連結 C/C++ 動態函式庫 By ajer001 @ 2006 十二月 27, 16:45 :

修好了。
有自己的主機真的比較爽,什麼都可以自己惡搞!
期待你的其他分享:-)