{"id":3898,"date":"2011-10-24T16:58:55","date_gmt":"2011-10-24T15:58:55","guid":{"rendered":"http:\/\/www.walkingrandomly.com\/?p=3898"},"modified":"2011-10-24T16:58:55","modified_gmt":"2011-10-24T15:58:55","slug":"matlab-mex-functions-using-the-nag-c-library-in-windows","status":"publish","type":"post","link":"https:\/\/walkingrandomly.com\/?p=3898","title":{"rendered":"MATLAB mex functions using the NAG C Library in Windows"},"content":{"rendered":"<p>The <a href=\"http:\/\/www.nag.co.uk\/numeric\/CL\/cldescription.asp\">NAG C Library<\/a> is one of the largest commercial collections of numerical software currently available and I often find it very useful when writing MATLAB <a href=\"http:\/\/www.mathworks.co.uk\/support\/tech-notes\/1600\/1605.html\">mex files<\/a>.\u00a0 &#8220;<em>Why is that?<\/em>&#8221; I hear you ask.<\/p>\n<p>One of the main reasons for writing a mex file is to gain more speed over native MATLAB.\u00a0 However, one of the main problems with writing mex files is that you have to do it in a low level language such as Fortran or C and so you lose much of the ease of use of MATLAB.<\/p>\n<p>In particular, you lose straightforward access to most of the massive collections of MATLAB routines that you take for granted.\u00a0 Technically speaking that&#8217;s a lie because you could use the mex function <strong>mexCallMATLAB<\/strong> to call a MATLAB routine from within your mex file but then you&#8217;ll be paying a time overhead every time you go in and out of the mex interface.\u00a0 Since you are going down the mex route in order to gain speed, this doesn&#8217;t seem like the best idea in the world.\u00a0 This is also the reason why you&#8217;d use the NAG C Library and not the <a href=\"http:\/\/nag.com\/numeric\/MB\/start.asp\">NAG Toolbox for MATLAB<\/a> when writing mex functions.<\/p>\n<p>One way out that I use often is to take advantage of the NAG C library and it turns out that it is extremely easy to add the NAG C library to your mex projects on Windows.\u00a0 Let&#8217;s look at a trivial example.\u00a0 The following code, <a href=\"https:\/\/www.walkingrandomly.com\/images\/NAG\/nag_normcdf.c\">nag_normcdf.c<\/a>, uses the NAG function <strong>nag_cumul_normal <\/strong>to produce a simplified version of MATLAB&#8217;s normcdf function (laziness is all that prevented me from implementing a full replacement).<\/p>\n<pre>\/* A simplified version of normcdf that uses the NAG C library\r\n * Written to demonstrate how to compile MATLAB mex files that use the NAG C Library\r\n * Only returns a normcdf where mu=0 and sigma=1\r\n * October 2011 Michael Croucher\r\n * www.walkingrandomly.com\r\n *\/\r\n\r\n#include &lt;math.h&gt;\r\n#include \"mex.h\"\r\n#include \"nag.h\"\r\n#include \"nags.h\"\r\n\r\nvoid mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] )\r\n{\r\n    double *in,*out;\r\n    int rows,cols,num_elements,i;\r\n\r\n    if(nrhs&gt;1)\r\n    {\r\n        mexErrMsgIdAndTxt(\"NAG:BadArgs\",\"This simplified version of normcdf only takes 1 input argument\");\r\n    } \r\n\r\n    \/*Get pointers to input matrix*\/\r\n    in = mxGetPr(prhs[0]);\r\n    \/*Get rows and columns of input matrix*\/\r\n    rows = mxGetM(prhs[0]);\r\n    cols = mxGetN(prhs[0]);\r\n    num_elements = rows*cols;\r\n\r\n    \/* Create output matrix *\/\r\n    plhs[0] = mxCreateDoubleMatrix(rows, cols, mxREAL);\r\n    \/* Assign pointer to the output *\/\r\n    out = mxGetPr(plhs[0]);\r\n\r\n    for(i=0; i&lt;num_elements; i++){\r\n          out[i] = nag_cumul_normal(in[i]);\r\n         }\r\n}<\/pre>\n<p>To compile this in MATLAB, just use the following command<\/p>\n<pre> mex nag_normcdf.c CLW6I09DA_nag.lib<\/pre>\n<p>If your system is set up the same as mine then the above should &#8216;just work&#8217; (see <strong>System Information<\/strong> at the bottom of this post).\u00a0 The new function works just as you would expect it to<\/p>\n<pre>&gt;&gt; format long\r\n&gt;&gt; format compact\r\n&gt;&gt; nag_normcdf(1)\r\nans =\r\n   0.841344746068543<\/pre>\n<p>Compare the result to normcdf from the statistics toolbox<\/p>\n<pre>&gt;&gt; normcdf(1)\r\nans =\r\n   0.841344746068543<\/pre>\n<p>So far so good.  I could stop the post here since all I really wanted to do was say <strong>&#8216;The NAG C library is useful for MATLAB mex functions and it&#8217;s <a href=\"http:\/\/www.thefreedictionary.com\/doddle\">a doddle<\/a> to use &#8211; here&#8217;s a toy example and here&#8217;s the mex command to compile it&#8217;<\/strong><\/p>\n<p>However, out of curiosity, I looked to see if my toy version of normcdf was any faster than The Mathworks&#8217; version.\u00a0 Let there be 10 million numbers:<\/p>\n<pre>&gt;&gt; x=rand(1,10000000);<\/pre>\n<p>Let&#8217;s time how long it takes MATLAB to take the normcdf of those numbers<\/p>\n<pre>&gt;&gt; tic;y=normcdf(x);toc\r\nElapsed time is 0.445883 seconds.\r\n&gt;&gt; tic;y=normcdf(x);toc\r\nElapsed time is 0.405764 seconds.\r\n&gt;&gt; tic;y=normcdf(x);toc\r\nElapsed time is 0.366708 seconds.\r\n&gt;&gt; tic;y=normcdf(x);toc\r\nElapsed time is 0.409375 seconds.<\/pre>\n<p>Now let&#8217;s look at my toy-version that uses NAG.<\/p>\n<pre>&gt;&gt; tic;y=nag_normcdf(x);toc\r\nElapsed time is 0.544642 seconds.\r\n&gt;&gt; tic;y=nag_normcdf(x);toc\r\nElapsed time is 0.556883 seconds.\r\n&gt;&gt; tic;y=nag_normcdf(x);toc\r\nElapsed time is 0.553920 seconds.\r\n&gt;&gt; tic;y=nag_normcdf(x);toc\r\nElapsed time is 0.540510 seconds.<\/pre>\n<p>So my version is slower!\u00a0 Never mind, I&#8217;ll just make my version parallel using OpenMP &#8211; Here is the code:<a href=\"https:\/\/www.walkingrandomly.com\/images\/NAG\/nag_normcdf_openmp.c\"> nag_normcdf_openmp.c<\/a><\/p>\n<pre>\/* A simplified version of normcdf that uses the NAG C library\r\n * Written to demonstrate how to compile MATLAB mex files that use the NAG C Library\r\n * Only returns a normcdf where mu=0 and sigma=1\r\n * October 2011 Michael Croucher\r\n * www.walkingrandomly.com\r\n *\/\r\n\r\n#include &lt;math.h&gt;\r\n#include \"mex.h\"\r\n#include \"nag.h\"\r\n#include \"nags.h\"\r\n#include &lt;omp.h&gt;\r\n\r\nvoid do_calculation(double in[],double out[],int num_elements)\r\n{\r\n    int i,tid;\r\n\r\n#pragma omp parallel for shared(in,out,num_elements) private(i,tid)\r\n    for(i=0; i&lt;num_elements; i++){\r\n          out[i] = nag_cumul_normal(in[i]);\r\n         }\r\n}\r\n\r\nvoid mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] )\r\n{\r\n    double *in,*out;\r\n    int rows,cols,num_elements;\r\n\r\n    if(nrhs&gt;1)\r\n    {\r\n        mexErrMsgIdAndTxt(\"NAG_NORMCDF:BadArgs\",\"This simplified version of normcdf only takes 1 input argument\");\r\n    } \r\n\r\n    \/*Get pointers to input matrix*\/\r\n    in = mxGetPr(prhs[0]);\r\n    \/*Get rows and columns of input matrix*\/\r\n    rows = mxGetM(prhs[0]);\r\n    cols = mxGetN(prhs[0]);\r\n    num_elements = rows*cols;\r\n\r\n    \/* Create output matrix *\/\r\n    plhs[0] = mxCreateDoubleMatrix(rows, cols, mxREAL);\r\n    \/* Assign pointer to the output *\/\r\n    out = mxGetPr(plhs[0]);\r\n\r\n    do_calculation(in,out,num_elements);\r\n}<\/pre>\n<p>Compile that with<\/p>\n<pre> mex  COMPFLAGS=\"$COMPFLAGS \/openmp\"  nag_normcdf_openmp.c CLW6I09DA_nag.lib<\/pre>\n<p>and on my quad-core machine I get the following timings<\/p>\n<pre>&gt;&gt; tic;y=nag_normcdf_openmp(x);toc\r\nElapsed time is 0.237925 seconds.\r\n&gt;&gt; tic;y=nag_normcdf_openmp(x);toc\r\nElapsed time is 0.197531 seconds.\r\n&gt;&gt; tic;y=nag_normcdf_openmp(x);toc\r\nElapsed time is 0.206511 seconds.\r\n&gt;&gt; tic;y=nag_normcdf_openmp(x);toc\r\nElapsed time is 0.211416 seconds.<\/pre>\n<p>This is faster than MATLAB and so normal service is resumed :)<\/p>\n<p><strong>System Information<\/strong><\/p>\n<ul>\n<li>64bit Windows 7<\/li>\n<li>MATLAB 2011b<\/li>\n<li>NAG C Library Mark 9 &#8211; CLW6I09DAL<\/li>\n<li>Visual Studio 2008<\/li>\n<li><a href=\"http:\/\/www.notebookcheck.net\/Intel-Core-i7-2630QM-Notebook-Processor.41483.0.html\"> Intel Core i7-2630QM<\/a> processor<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>The NAG C Library is one of the largest commercial collections of numerical software currently available and I often find it very useful when writing MATLAB mex files.\u00a0 &#8220;Why is that?&#8221; I hear you ask. One of the main reasons for writing a mex file is to gain more speed over native MATLAB.\u00a0 However, one [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[53,11,28,41,7,42],"tags":[],"class_list":["post-3898","post","type-post","status-publish","format-standard","hentry","category-making-matlab-faster","category-matlab","category-nag-library","category-parallel-programming","category-programming","category-tutorials"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p3swhs-10S","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/posts\/3898","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3898"}],"version-history":[{"count":7,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/posts\/3898\/revisions"}],"predecessor-version":[{"id":3905,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=\/wp\/v2\/posts\/3898\/revisions\/3905"}],"wp:attachment":[{"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3898"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3898"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/walkingrandomly.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3898"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}