{"id":260,"date":"2017-11-21T17:50:48","date_gmt":"2017-11-21T15:50:48","guid":{"rendered":"http:\/\/benediktehinger.de\/blog\/science\/?p=260"},"modified":"2017-11-23T14:16:37","modified_gmt":"2017-11-23T12:16:37","slug":"matlab-performance-for-loops-vs-vectorization-vs-bsxfun","status":"publish","type":"post","link":"https:\/\/benediktehinger.de\/blog\/science\/matlab-performance-for-loops-vs-vectorization-vs-bsxfun\/","title":{"rendered":"[matlab] performance for-loops vs. vectorization vs. bsxfun"},"content":{"rendered":"<p>From time to time I explain my students certain concepts. To archive those and as an extended memory, I share them here. We also recently had some discussion on vectorization in our research group. e.g. in <a href=\"https:\/\/towardsdatascience.com\/why-you-should-forget-for-loop-for-data-science-code-and-embrace-vectorization-696632622d5f\">python<\/a> and <a href=\"http:\/\/www.matlabtips.com\/matlab-is-no-longer-slow-at-for-loops\/\">matlab<\/a>. With the second link claiming for-loops in matlab are performing much better than before.<\/p>\n<p>&nbsp;<\/p>\n<h2>Goal<\/h2>\n<p>Show that for-loops are still quite slow in matlab. Compare bsxfun against vectorized arithmetic expansion in matlab against bsxfun<\/p>\n<h2>The contenders<\/h2>\n<ul>\n<li>good old for-loop: Easy to understand, can be found everywhere, slow<\/li>\n<li><a href=\"https:\/\/blogs.mathworks.com\/loren\/2016\/10\/24\/matlab-arithmetic-expands-in-r2016b\/\">arithmetic expansion<\/a>: medium difficulty, should be general used, fast<\/li>\n<li>bsxfun: somewhat difficult to understand, I use it regularily, fast (often)<\/li>\n<\/ul>\n<h2>Comparisons<\/h2>\n<p>While demonstrating this to my student, I noticed that subsetting an array has interesting effects on the performance differences. The same is true for different array sizes. Therefore, I decided to systematically compare those.<\/p>\n<p><a href=\"http:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2017\/11\/matlab_comparison.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-261 size-full\" src=\"http:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2017\/11\/matlab_comparison.png\" alt=\"\" width=\"987\" height=\"420\" srcset=\"https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2017\/11\/matlab_comparison.png 987w, https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2017\/11\/matlab_comparison-300x128.png 300w, https:\/\/benediktehinger.de\/blog\/science\/upload\/sites\/2\/2017\/11\/matlab_comparison-768x327.png 768w\" sizes=\"auto, (max-width: 987px) 100vw, 987px\" \/><\/a><\/p>\n<p>I subtract one row from either a subset (first 50 rows, dashed line) or all rows of an [n x m] matrix with n= [100, 1000, 10 000] and m = [10, 100, 1000, 10 000]. Mean + SE<\/p>\n<h2>Three take home messages:<\/h2>\n<ul>\n<li>for loop is very slow<\/li>\n<li>vectorization is fastest for small first dimension, then equally fast as bsxfun<\/li>\n<li>bsxfun is fastest if one needs to subset a medium sized array (n x m &gt;100 x 1000), but see update below!<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h2>Update:<\/h2>\n<p>Prompted by <a href=\"https:\/\/anneurai.net\/\">Anne Urai<\/a>, I redid the analysis with multiplication &amp; devision. The pattern is the same. I did notice that allocating new matrices before doing the arithmetic expansion (vectorization) results in the same behaviour as bsxfun (but more lines of code)<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">A = data(ix,:);\r\nB = data(1,:);\r\nx = A.\/B;<\/pre>\n<p>&nbsp;<\/p>\n<h2>matlab code<\/h2>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">tAll = [];\r\nfor dim1 = [100 1000 10000]\r\n    for dim2 = [10 100 1000 10000]\r\n        tStart = tic;\r\n        for subset = [0 1]\r\n            if subset\r\n                ix = 1:50;\r\n            else\r\n                ix = 1:dim1;\r\n            end\r\n            for run = 1:10\r\n                data = rand(dim1,dim2);\r\n                \r\n                % for-loop\r\n                x = data;\r\n                tic\r\n                for k= 1:size(data,2)\r\n                    x(ix,k) = data(ix,k)-data(1,k);\r\n                end\r\n                t = toc;\r\n                tAll = [tAll; table(dim1,dim2,subset,{'for-loop'},t)];\r\n                %vectorized\r\n                tic\r\n                x = data(ix,:)-data(1,:);\r\n                t = toc;\r\n                tAll = [tAll; table(dim1,dim2,subset,{'vectorization'},t)];\r\n                % bsxfun\r\n                \r\n                tic\r\n                x= bsxfun(@minus,data(ix,:),data(1,:));\r\n                t = toc;\r\n                tAll = [tAll; table(dim1,dim2,subset,{'bsxfun'},t)];  \r\n            end\r\n        end\r\n        fprintf('finished dim1=%i,dim2=%i - took me %.2fs\\n',dim1,dim2,toc(tStart))\r\n    end\r\nend\r\n\r\n% Plotting using the awesome GRAMM-toolbox\r\n% https:\/\/github.com\/piermorel\/gramm\r\nfigure\r\ng = gramm('x',log10(tAll.dim2),'y',log10(tAll.t),'color',tAll.Var4,'linestyle',tAll.subset);\r\ng.facet_grid([],tAll.dim1)\r\ng.stat_summary()\r\ng.set_names('x','log10(second dimension [n x *M*])','y','log10(time) [log10(s)]','column','first dimension [ *N* x m]','linestyle','subset 1:50?')\r\ng.draw()<\/pre>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>From time to time I explain my students certain concepts. To archive those and as an extended memory, I share them here. We also recently had some discussion on vectorization in our research group. e.g. in python and matlab. With the second link claiming for-loops in matlab are performing much better than before. &nbsp; Goal Show that for-loops are still quite slow in matlab. Compare bsxfun against vectorized arithmetic expansion in matlab against bsxfun The contenders good old for-loop: Easy to understand, can be found everywhere, slow arithmetic expansion: medium difficulty, should be general used, fast bsxfun: somewhat difficult to&#8230;<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-260","post","type-post","status-publish","format-standard","hentry","category-blog"],"_links":{"self":[{"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/posts\/260","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/comments?post=260"}],"version-history":[{"count":0,"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/posts\/260\/revisions"}],"wp:attachment":[{"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/media?parent=260"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/categories?post=260"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/benediktehinger.de\/blog\/science\/wp-json\/wp\/v2\/tags?post=260"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}