EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
前言:众所周知对于循环这种运算,matlab的运算速度是不快的 想起来个冷笑话,黑客帝国里的主角呢奥之所以能快过子弹,因为Matrix是matlab写成的hhhh 因此将循环部分用C语言写,然后编译成matlab可以调用的mex文件,无疑可以加快循环运算速度,从而加快整个代码速度 之前因为合并数据需要不会数据库,强行在matlab里利用类似excel的vlookup功能,无奈太慢,只能想办法最后知道了这种方法,最近偶然在桥哥的知识星球里又提到了,也顺便复习和记录一下。 ps:今天在闲鱼上买了小车车,高兴! 1.环境配置我的是matlab2019a,win10-64bit,c语言编译器我选的是TDM-GCC(gcc/g++),安装起来很简单,推荐matlab2015及以后的选择这种,详细安装过程可以见文末链接1中的For Matlab 2015部分
, a4 i6 R) p; v8 f: @$ ?
2.如何编写可以被编译成matlab可执行文件的c语言代码我是在matlab中文论坛(地址见reference第2条)里找到如何编写的,不过他写的有点乱,我来消化后写一下自己的理解 mex接口函数跟一般的C语言文件主要是两点区别, 第一是必须引入头文件mex.h #include "mex.h"第二是多了一个叫mexFunction的函数 void mexFunction(int nlhs,mxArray *plhs[], int nrhs,const mxArray *prhs[])举个栗子:
' ^1 F+ D% Q' N$ h
% S0 f9 ?' w8 G T2 l% @# h/ D#include "mex.h" // 使用MEX文件必须包含的头文件
+ n7 g" X/ U6 C# w& K" `+ s7 R* W- q- u( o
7 o5 E" |9 o2 h$ b% ~0 [# o5 ~5 ~1 V) K3 ]% D+ |
// 执行具体工作的C函数' q8 L# n2 ^0 N0 V- _2 [
' q& R, A! h* t8 D$ |
- / w6 V( Q- Z+ ?
: {" A) I9 z" Y
double add(double x, double y)4 X+ W E; o( ]- n5 k+ Q i& m( M
8 N: g/ D9 C! d3 k' p( P
Z3 C9 E# I% e! u; ~* { H1 Z0 @2 S I l2 d4 X) j+ z
{3 B+ P! O+ e% n& X4 R% l
% O8 `4 e, W5 P( l* @! q! c
+ a" l% \/ Z9 P# F* A! l1 e! z6 w7 C2 b5 m
return x + y;% N' C! a- m2 V, t# N( j8 G
$ {$ C( {0 N7 d
* i2 b: u9 r9 s" {' @1 ?0 d# d" ]6 p8 y) V7 k
}1 Z2 M: M l6 m" B5 p
- z- o6 Z' e" e7 O5 E3 B9 C
- * Y7 o% |' ^: E8 \
% j+ _* |) E6 m$ }2 V2 c$ t7 ]7 N4 }
// MEX文件接口函数& M1 ]2 t% Z5 u+ ? Z0 y5 ?
1 @5 w0 P! ?' T5 P+ B& ~0 ?
: a2 [9 K6 c+ a
' X( i* i- `. Hvoid mexFunction(int nlhs,mxArray *plhs[], int nrhs,const mxArray *prhs[])* V( C$ J/ S( e0 I. x, B
. Q2 o% M% ?6 P3 Y9 J8 O9 K
* {" Q3 d) W, ?7 `* X- T3 h+ _2 t! J) M& ~9 H! d+ P* N7 F8 n
{2 e3 Z4 Z5 Z) i* j( w6 S" o
3 H, W& `. ]4 h$ d0 Y2 Y- 7 }. f+ v) C" n1 m$ }. h2 x0 I
0 n9 g7 k: x, e
double *z;5 c3 g5 T. P/ J
; [3 l6 b/ B7 \' X
/ A, G$ U: k) N' [
" M& p9 o* u6 M& r8 e: d double x, y;
2 }+ O6 v; A) R# d
6 ~. f7 N8 L0 f. A( n& u" g
% ]) x' W; a( a" F5 D$ b
- P) O8 l8 C9 j# {: f% | plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
, i4 Z$ m# {" W# S5 C# W e* Z; X& `% F# B
- ; d& O \4 R' ~) {' T
; L: I! W5 t; o1 G0 o& R
z = mxGetPr(plhs[0]);
5 _2 q9 d: G1 D0 L! e
9 S1 F5 k/ J; ^' k9 _8 [5 `
6 H* C! @- |2 x) @% y4 Z" e7 C; z5 ]$ F& z
x = *(mxGetPr(prhs[0]));
. _! g* t7 t( B8 x, s0 X) Y
9 K2 e+ Z' u" [+ t
0 a& e% b" r( v: z: b* d3 s1 d. u8 p
y = *(mxGetPr(prhs[1]));: |( J2 H! y& G+ ~% I
- h% s( v: B4 r2 x7 Q
! q. [! t3 w: g ]: c5 h7 Y2 g( x& H
*z = add(x, y);" J6 C i! b' `4 C+ Q- ^
2 ~, J7 N; `, g+ D) d" ^8 ^- $ s/ H0 h$ W. f9 X4 X
$ ^+ ~* d/ j3 [8 w3 v! e
}
& U& F! A q+ t. c2 d. s8 M% x- l. ]' H6 c
, O, R' v' Y! I! j3 }
也可以写成: - ; q5 @# ^ e# ~0 ]% B# i5 p; s
! k1 F. F. e7 z1 i
#include "mex.h" // 使用MEX文件必须包含的头文件
- B. f! E1 Z! A' X' B. r
) m( Y" B: u3 K: e
: T/ S5 f8 _" X5 s. T% E/ a6 U8 z6 f; h3 @0 G& \
// MEX文件接口函数* R! J( R1 s* c, C# d0 _
& n' L$ I I8 ~. M C. B
% B9 b |, W3 Z* `5 `* g; z* V( g/ ]: I' p
void mexFunction(int nlhs,mxArray *plhs[], int nrhs,const mxArray *prhs[])% R$ m6 J6 S2 J5 j
2 f% [+ Q5 F, _- # F; {/ [# P7 X
+ x1 G, R5 v9 N* d @$ z
{- e* B, a5 ^; Q6 D8 Z& T2 w8 n: g
/ c4 I, W& t C+ x# p
F) v8 B+ p5 m# v$ I& f. f: h6 i
/ k5 ^7 X. y- U/ c- M2 F double *z;
9 T: A4 p! r: a. p3 L- O
P4 U3 {' b6 _# E
0 i' A0 r* Q: O
8 e8 @, Y# a& v) N double x, y;
; G3 }( }9 m) a2 H, u$ T- M: _6 `' s* A
- # \; w4 A/ z" j1 s0 N
4 M# [) G& L+ b [5 U, W plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);0 s% Y4 a3 R( \
) s- X5 z7 q3 |! T$ l" \
1 y+ L& e" i: \
! f' N( I) M$ z, q- @ z = mxGetPr(plhs[0]);+ A+ C1 Z) k+ }
+ g0 }0 x) A4 b+ K! p. R
- ; P' h1 f8 Q4 f
# p4 N% z& R' B+ b% D! R, j9 ^
x = *(mxGetPr(prhs[0]));, f& ~- b5 Y* O1 j" Q, Q
5 R% y. x3 L7 F, c8 X, Y: g' B8 m - ! d- N1 _/ d2 m1 `5 i, v9 f
9 v# S9 S8 t, O
y = *(mxGetPr(prhs[1]));3 a1 r) |, y' w1 E- m
! b5 g# W3 _, n9 j2 }
- 5 @: r4 B: Y0 P! t$ @! D( |
7 ^$ P2 j7 \4 ?) n- q& N
*z=x+y;: n8 _$ t8 P) r" [
. E' X1 Q0 }5 Z7 x$ j- v+ _ - 2 M, q; |2 q. j
( Z9 `4 c" B2 @" y4 G) I7 b}) D/ J, e5 Z. j8 g# e6 ^* k2 u: M
- N* {$ D( c7 d; U3 @! v- m
+ x6 |/ t) e6 j. }. B
也就是说执行具体功能可以通过编写一个独立的c语言函数在接口函数里调用,也可以直接写在mexFunction接口函数里 我个人推荐将具体功能独立出来,这样一来: c语言函数负责执行具体功能 mexFunction接口函数负责数据的输入输出(将matlab数据输入c语言环境,运算后再输出回matlab)形式组织 再调用c语言函数
分工明确。 于是关键问题就是如何编写mexFunction接口函数了 void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])下面解释一下mexFunction各个参数的含义(这里看到是void,即无返回值,因为传值是通过指针数组plhs[]传出的) 参数 意义 英文全称 类型 8 n0 X V8 J8 a+ P- s4 {9 a( K
nlhs左边输出参数的数目number of left-hand side整型(int)* }! z9 l; o% e9 A( ^: u( L1 ^
plhs指向输出参数的指针pointer of left-hand side指针数组7 \5 a" a6 c3 ]8 A, P1 }8 Q
nrhs右边输入参数的数目number of right-hand side整型(int)4 w u8 B7 O, ]6 I
prhs指向输入参数的指针pointer of right-hand side指针数组
/ l! h' q9 A5 @' v- _, ~% M2 u第一个参数nlhs是输出参数的数目,第二个参数plhs是指向输出参数的指针 第三个参数nrhs是输入参数的数目,第四个参数prhs是指向输入参数的指针 其中plhs和prhs类型都是指向mxArray类型数据的指针,prhs这里还加了个const,这里的const跟之前c语言中是一样的,代表不改变,因为prhs是指向输入参数的指针,mxArray这个数据类型是在头文件mex.h中定义的,in fact ,在matlab中大多数数据都是以这种类型存在。 还是拿之前的栗子来解释:
7 R( Y6 _! y# G. o2 [
- p7 _7 w9 \! W' t7 L#include "mex.h" // 使用MEX文件必须包含的头文件
1 U) [7 C3 t: V4 W
+ }2 G, k" S$ N$ M3 P
; `( z* \1 } f$ r0 e3 P& o0 Y: |) x8 m) i& K- _! G
// MEX文件接口函数
; b9 ^) L7 p+ c: c1 @4 \# e& d8 L+ }& q, a7 V
* w/ `- O4 l: P' e* o. p
2 c( ^$ x4 m- c7 ?1 F, G5 Gvoid mexFunction(int nlhs,mxArray *plhs[], int nrhs,const mxArray *prhs[])1 T; N2 P! ?. R" i; q5 z
$ e* b6 A# Z% D; ~. X+ t
- - ~% S% Y# X: l- [, B
9 Z ~. t9 H, e* {: \+ {+ i
{& C7 _! I% w3 K% e5 \/ p4 j
" G2 m7 P7 o# N+ u* T
# d( w8 r @8 L5 T
0 D9 A5 k) X% w& d8 Z- J( A9 n7 x5 Z double *z;" I: h1 f' B/ M8 M; ]9 W- {. }
1 J6 m$ N. B; c J! i# K- 1 c8 ^/ g. ]* b5 f
) y( Y/ R; C8 w. I/ H
double x, y;
1 r3 t9 b4 B: a0 `0 a4 R' b, A8 w* Q1 m
* ^( M4 O. v" c/ _( T9 A% B4 e( I5 {( ~
) \) k' z% C2 g) a8 g. J$ C/ c* ^/ v/ t
plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);4 ?( c, D( T" `+ Q
" j" E! Z8 Y$ B0 w7 v& j, m
- * z, R. W- g7 _; n
! p* _% D y6 B* d) `( a q z = mxGetPr(plhs[0]);2 j S. l% k# V L
& u% c- B1 u# }+ j
+ p! p; j2 u6 e) H: Z- @
0 D* {2 a8 D0 }: Z* f' Z$ Q x = *(mxGetPr(prhs[0]));2 C5 F$ h* h F
. x. X/ U$ C, n$ S3 z4 z$ X+ }
0 G7 P6 s' I5 C$ x3 Y* N
* L4 A) q7 F& T. d2 T( z y = *(mxGetPr(prhs[1]));
. H9 X' k5 O u
! s6 M) a& g& k- g( I8 C5 W
, A& Z' n) a% [; j* H3 P: y( `# Z3 _
*z=x+y;6 i# p6 `0 I/ f) ^+ m' G
5 a: T) D- V5 d% C8 n+ s; b+ ?
- / T9 z' W& \- M
/ d6 w; F% e! {}
) z0 Y$ c( v5 ]1 E9 S+ U \
3 N. t9 }% `8 e: Y$ s' C1 U [# ?- a+ Y* L% y* z
在这个栗子中,输入参数是x,y,输入参数是z(虽然没有返回) plhs和prhs其实都是指针数组 prhs[0]代表指向第一个输入数据的指针,mxGetPr代表获得这个指针,后面再加个*就是c语言里面再正常不过的按指针取内容了 所以就这样x,y通过下面这两句传入到了mexFunction中 - . ?1 j- n7 u6 }" s* c
4 x8 z) |# t. Ux = *(mxGetPr(prhs[0]));% V0 u$ u" x% S% j
3 R3 V. n9 x* g2 a/ Z+ S9 [$ C
7 i0 I/ j: l+ ^4 ?+ L1 `) Z- e- l/ H3 O8 g5 U: [
y = *(mxGetPr(prhs[1]));
2 r7 z4 X8 X1 [' j) T8 W6 t- R7 ^. R+ V: p$ l4 B
. z V# m5 ?, O; n- d0 M }
mxGetPr函数的功能是从指向mxArray类型数据的指针prhs[0]、prhs[1]中获得了指向double类型的指针 另一个函数是mxGetScalar,Scalar即标量,功能是把通过prhs传递进来的mxArray类型数据的指针所指向的数据(标量)赋给c程序里的变量。前面的mxGetPr是传递矢量的,否则矩阵或者向量就没有办法传进来 这里由于传入的每个参数是单个值,于是也可以用mxGetScalar改写:
( }. a* I3 T) i2 {, y, I7 f
) V& c% M u/ z! J. u7 b0 t! I: w#include "mex.h" // 使用MEX文件必须包含的头文件
- N4 j$ m6 D* b. J- Y% ^; ?/ v/ l7 M$ g% F
- , I- U2 ]& B9 G. @( C% `% z
' W! a9 ~$ E3 a& ?4 C7 |& n
// MEX文件接口函数: g3 K; ~+ }- u2 X9 d' K1 c4 J# _
1 R) t# I7 E7 T* U& o& w J - $ u, c2 N% E$ e1 Y1 V( U8 ?
4 @; q. e/ L& t, ^. c! Mvoid mexFunction(int nlhs,mxArray *plhs[], int nrhs,const mxArray *prhs[])- M W4 [" X, o6 D/ N
; i. V) D' d1 a$ [3 A - x" L8 P, M" C" ~' m
1 x6 H7 C, G8 E9 N" r/ w" P0 z
{
3 p' Y \( d ~. M9 }# u4 J/ S7 A5 C
3 a7 }; |) K& ]9 k
' {2 O' I. a! Q' S
, A t$ D; \2 F double *z;2 m- ~* `9 Z4 |& O$ ]9 J, I1 v
$ h; K9 b4 S; f) k" P' w
- 0 o0 i! z P& w
, o! L0 I& `6 }, q* c; _
double x, y;
, k( x/ o( g: M& u5 o; g' U# M
4 \8 \! k2 t; m/ [
' d/ b3 W) X6 ]+ ?4 | plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);
. _( X, U+ ~0 }9 ]- c/ z2 g, b0 K( e3 w9 Y' w0 A9 H/ `
! \. N9 j/ m7 E A( k- Z
. |6 P- d3 H$ G! Z8 D z = mxGetPr(plhs[0]);
3 X1 ^, k0 _/ t# B2 V
/ q* q+ X9 s& ?- y0 @- , J' k, i' n! S* j$ B9 v1 m
: X# | |9 `! e p& S5 ?0 M
x = mxGetScalar(prhs[0]));- V: m2 k4 y5 \6 y" V. R/ |
) d# I$ x' o% a' A6 D3 v
L1 W G5 n% n$ e. G% e. M! R' Y3 [4 B, K
y = mxGetScalar(prhs[1]));
1 Z; x: `/ o; M. L
1 ~% E3 ]: T' [8 W
4 K8 a7 ^: J9 s1 ]; i. H
7 g$ b# y Q6 p/ s! P& E* }: Y+ z J *z=x+y;
& ]% j8 j' G- t: B
' N% F# F: `( }, t
0 }! e5 k. X- u" I+ ~" [4 V N+ W0 m
}
: P+ e! e3 X& v7 M& D* w4 `; \9 R6 v- J j4 x
- }4 c) f2 i4 X
(注意:输出参数因为需要通过指针传出,因此这里z必须用mxGetPr这个函数来从mxArray指针获取double指针) 但是这样还是有个问题:如果输入的不是单个的数据,是向量或者矩阵,即使我们通过mxGetPr获得了矩阵或者向量的指针,但是我们假如不知道矩阵的shape,还是不好取值或者计算,所以这里又有两个函数mxGetM和mxGetN可以通过传入参数指针获得传入参数(矩阵)的行和列数。 需要注意的是: 在matlab中矩阵的第一行和第一列的序号是从1开始的,而在c语言中是从0开始的,而且在c语言中的一维数组存储矩阵的数据时,是一列列从上到下,从左到右来存储的,也即 matlab中一个m*n的矩阵M(i,j)对应于c语言中一维数组N中的N[j*m+i]
举个栗子:
; `( x6 H+ x! e
, T0 i: R( ^) I! ]#include "mex.h"- @2 @2 c2 ~1 c- [2 w
; i- i! }6 V# ^! R9 M/ \, L- . x& f; R3 H8 g8 ]! f: U
" U% c7 U6 L" i7 a E
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){) A" @8 y4 }1 T
L# `7 R9 T5 ^. O" C( \8 j( H
1 z, q6 t' \3 k9 C" C, j2 a' ?6 Y# y- W
8 k5 M" W- l' r; t$ S
" t, `" x3 D) h1 O$ t
; `( o @6 U9 }( f4 m
- L: n: M6 f# p P; gdouble *data;7 @- h- S1 Y) Q3 n5 ^
" @: d* ~5 v# m, [" i3 _2 p
- $ K, ?8 C, R" P$ O" z
2 s+ R4 _+ R8 F% @( cint M,N;
. \( z2 K. u3 d3 U+ f Z' q: C" j+ u' U+ |) u
- . n/ M1 ^( b! B9 h) G: A
7 W5 \; G6 {8 N; `$ F3 p" w
int i,j;
6 N7 e7 N# C2 r% @4 Z
5 z0 t, F. f. b# _% X" O
) D* s: V$ F3 @* p% T' H' q( P
) i: v4 [ V' ^1 c/ f3 g5 ]$ Fdata=mxGetPr(prhs[0]); //获得指向矩阵的指针5 Z9 |: z- H, r1 l. j
: H+ w7 O3 v D {, S
9 E- k7 v# H# Q/ N. q7 R
; L& Z3 b& D& R' K# g- H" tM=mxGetM(prhs[0]); //获得矩阵的行数" J! P& Y, E0 J9 f5 V1 C
8 [+ f9 D- e. r/ S1 e
# F' o* C5 K5 L9 g4 L" m8 p3 O, V) w% M$ b+ N
N=mxGetN(prhs[0]); //获得矩阵的列数0 i0 o2 ?/ B9 y* X; A! e9 W
3 P7 `) K8 V; v- K0 ^) s- k; f
: L7 \8 _, P9 _1 H
& P7 p5 O' p+ l- |% r% s; M5 Wfor(i=0;i<M;i++)
3 C+ U4 _9 [) h# d4 t
2 L4 z! w# F; Y/ ?. _; m0 C# N
" O4 b& L/ l% m6 {, |" ^ S2 S5 ^6 h0 D, p0 J9 D: _
{
) m- ]3 O1 I/ T- n% l4 q- }) w4 T9 @9 b# P: l ]- i- F; h9 i
6 B* a9 [' \/ t, v+ v
- g0 a# Q2 u N6 t4 b' u for(j=0;j<N;j++)) C* K: ?/ ~, }/ L$ B
3 c' S6 d/ o: C( z- , t+ p5 A; f$ u; g3 I. b5 Y
$ }. T' }/ w" s/ x. ?' h
mexPrintf("%4.3f ",data[j*M+i]);% f7 }* k) `2 I& _5 p
- e# L% V% a! i0 R9 E$ A! G9 l
- Z n5 e: s: C9 @, p& F
4 P3 V6 j: r, |: b* ~" } mexPrintf("\n");" U8 K- `4 G: ?1 h
% x0 g5 D5 Z9 V% k
8 Z; c4 A6 n" X) l+ n/ p3 \( ^
5 u& M6 p+ s% R7 ^/ m8 T4 m} ^8 ^% Z7 z C9 e) t3 a. q
: }* K7 D' ]4 j/ i. K% }3 _
# r# I0 F0 i) g( h& S6 x1 f" ]( T
- Z& L. P$ {- Z" ?. O# ~& i% X) V }
* U. { A7 C0 u" g
% R5 l7 h" v3 k" Q% a% ^- ?
! c1 @$ y; E0 B& S( E2 K+ T
假如是一个形如[1,2,3;4,5,6]的矩阵,则执行上述后会先后打印出1,4,2,5,3,6 以上讲的都是输入参数,由于输入数据在函数调用前就已经在matlab里申请过内存,由于mex函数与matlab共用一个地址空间,因此通过prhs传递指针即可传入输入参数。但输出参数却需要在mex函数里申请内存,才能将指针放在plhs中传递出去。由于返回指针类似必须是mxArray,所以matlab专门提供了一个函数:mxCreateDoubleMatrix来实现内存申请,函数原型为: mxArray * mxCreateDoubleMatrix(int m,int n,mxComplexity ComplexFlag)m,n分别是待申请矩阵的行数和列数,需要注意的是为矩阵申请内存后得到的是mxArray类型的指针,就可以放在plhs[]中传递出去了。但是对这个矩阵的处理(包括赋值)却需要在mex函数或者c语言函数中完成,这就需要通过前面的mxGetPr或者mxGetScalar。使用mxGetPr获得指向这个矩阵的double类型指针后,就可以对这个矩阵进行各种操作和运算了。 还是拿这个栗子讲,这里由于输出是一个数,于是m,n都是1,通过mxGetPr进一步获得指向double数据类型指针z - ) I h5 n$ Z4 A; w# L
! v( U+ o* B5 f" f
( f( t9 u% {0 M( m( j
* B2 o& Z, G# V* K& W8 z9 H7 y$ g - 3 V; ~8 y' I0 ?2 z7 u k! T
4 ~# \& S3 I* ~; A) ` double *z;
4 [+ ~! n3 N' {8 Z y$ U! i- q5 q6 Z
- % Y2 \1 K. }) p/ u2 X; S. N% W: ^
+ q& W% V( v8 P3 n& y5 |+ b& m
plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);6 m1 A R- f2 {. d1 X0 q6 L& p2 {/ _
) r( u) z) s2 m4 _
9 E2 B+ J, s0 l& f$ u9 r. D, U
z = mxGetPr(plhs[0]);4 P x% G" h# Z2 o" F3 u( W
" x, j. W2 B$ ^( q$ p/ E
% Z3 Y9 o- q. A1 f9 S" S$ U" b9 P+ E- k6 {* P& A' _) x8 I
x = *(mxGetPr(prhs[0]));
: M b- j7 J2 U& Z, i1 d# ?
: p0 ^2 x3 d$ z5 Q
2 i1 D8 t& [6 z9 I8 P8 T! P0 m ?. {5 `
y = *(mxGetPr(prhs[1]));' M' ^. h* Z& i1 r8 {: F& ~( p
7 v3 n- C5 N2 I; {- 0 q) |3 ]( l* v
) d# q5 B# U$ N/ O
*z=x+y;( ^! e# m$ f" R. C% w& {
( c' Z ?& `: O
# @, x9 \ ]- U& }$ Y7 x
当然,matlab里使用到的并不只是double类型这一种矩阵,还有字符串类型,结构类型矩阵等,并也提供了对应的处理函数。
, }* g% k" x; u q) T |