{"id":903,"date":"2014-05-26T07:00:00","date_gmt":"2014-05-26T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2014\/05\/26\/find-the-index-of-the-smallest-element-in-a-javascript-array\/"},"modified":"2014-05-26T07:00:00","modified_gmt":"2014-05-26T07:00:00","slug":"find-the-index-of-the-smallest-element-in-a-javascript-array","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20140526-00\/?p=903","title":{"rendered":"Find the index of the smallest element in a JavaScript array"},"content":{"rendered":"<p>\nToday&#8217;s Little Program isn&#8217;t even a program.\nIt&#8217;s just a function.\n<\/p>\n<p>\nThe problem statement is as follows:\nGiven a nonempty JavaScript array of numbers,\nfind the index of the smallest value.\n(If the smallest value appears more than once,\nthen any such index is acceptable.)\n<\/p>\n<p>\nOne solution is simply to do the operation manually,\nsimulating how you would perform the operation with\npaper and pencil:\nYou start by saying that the first element is the winner,\nand then you go through the rest of the elements.\nIf the next element is smaller than the one you have,\nyou declare that element the new provisional winner.\n<\/p>\n<pre>\nfunction indexOfSmallest(a) {\n var lowest = 0;\n for (var i = 1; i &lt; a.length; i++) {\n  if (a[i] &lt; a[lowest]) lowest = i;\n }\n return lowest;\n}\n<\/pre>\n<p>\nAnother solution is to use the <code>reduce<\/code> intrinsic\nto run the loop, so you merely have to provide\nthe business logic of the\ninitial guess\nand the <code>if<\/code> statement.\n<\/p>\n<pre>\nfunction indexOfSmallest(a) {\n return a.reduce(function(lowest, next, index) {\n                   return next &lt; a[lowest] : index ? lowest; },\n                 0);\n}\n<\/pre>\n<p>\nA third solution is to use JavaScript intrinsics to\nfind the smallest element and then convert\nthe element to its index.\n<\/p>\n<pre>\nfunction indexOfSmallest(a) {\n return a.indexOf(Math.min.apply(Math, a));\n}\n<\/pre>\n<p>\nWhich one is fastest?\n<\/p>\n<p>\nOkay, well, first, before you decide which one is fastest,\nyou need to make sure they are all correct.\nOne thing you discover is that the <code>min\/indexOf<\/code>\ntechnique fails once the array gets really, really large,\nor at least it does in Internet Explorer and Firefox.\n(In my case, Internet Explorer and Firefox gave up at around\n250,000 and 500,000 elements, respectively.)\nThat&#8217;s because you start hitting engine limits on the number\nof parameters you can pass to a single function.\nInvoking <code>apply<\/code> on an array of 250,000 elements\nis the equivalent of calling <code>min<\/code> with 250,000\nfunction parameters.\n<\/p>\n<p>\nSo we&#8217;ll limit ourselves to arrays of length at most 250,000.\n<\/p>\n<p STYLE=\"height: 10em\">\nBefore I share the results, I want you to guess which\nalgorithm you think will be the fastest\nand which will be the slowest.\n<\/p>\n<p STYLE=\"height: 10em\">\nStill waiting.\n<\/p>\n<p>\nI expected the manual version to come in last place,\nbecause, after all, it&#8217;s doing everything manually.\nI expected the reduce version to be slightly faster,\nbecause it offloads some of the work into an intrinsic\n(though the function call overhead may have negated\nany of that improvement).\nI expected the min\/indexOf version to be fastest\nbecause nearly all the work is being done in intrinsics,\nand the cost of making two passes over the data\nwould be made up by the improved performance of the intrinsics.\n<\/p>\n<p>\nHere are the timings of the three versions with arrays of\ndifferent size, running on random data.\nI&#8217;ve normalized run times so that the results are independent\nof CPU speed.\n<\/p>\n<table BORDER=\"1\" CELLPADDING=\"3\" STYLE=\"border-collapse: collapse\">\n<caption>\n    <font SIZE=\"+1\"><b>Relative running time per array element<\/b><\/font>\n<\/caption>\n<tr>\n<th>Elements<\/th>\n<th>manual<\/th>\n<th>reduce<\/th>\n<th>min\/indexOf<\/th>\n<\/tr>\n<tr>\n<th COLSPAN=\"4\">Internet Explorer 9<\/th>\n<\/tr>\n<tr>\n<td ALIGN=\"right\">100,000<\/td>\n<td ALIGN=\"right\">1.000<\/td>\n<td ALIGN=\"right\">2.155<\/td>\n<td ALIGN=\"right\">2.739<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"right\">200,000<\/td>\n<td ALIGN=\"right\">1.014<\/td>\n<td ALIGN=\"right\">2.324<\/td>\n<td ALIGN=\"right\">3.099<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"right\">250,000<\/td>\n<td ALIGN=\"right\">1.023<\/td>\n<td ALIGN=\"right\">2.200<\/td>\n<td ALIGN=\"right\">2.330<\/td>\n<\/tr>\n<tr>\n<th COLSPAN=\"4\">Internet Explorer 10<\/th>\n<\/tr>\n<tr>\n<td ALIGN=\"right\">100,000<\/td>\n<td ALIGN=\"right\">1.000<\/td>\n<td ALIGN=\"right\">4.057<\/td>\n<td ALIGN=\"right\">4.302<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"right\">200,000<\/td>\n<td ALIGN=\"right\">1.028<\/td>\n<td ALIGN=\"right\">4.057<\/td>\n<td ALIGN=\"right\">4.642<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"right\">250,000<\/td>\n<td ALIGN=\"right\">1.019<\/td>\n<td ALIGN=\"right\">4.091<\/td>\n<td ALIGN=\"right\">4.068<\/td>\n<\/tr>\n<\/table>\n<p>\nAre you surprised?\nI sure was!\n<\/p>\n<p>\nNot only did I have it completely backwards,\nbut the margin of victory for the manual version was way\nbeyond what I imagined.\n<\/p>\n<p>\n(This shows that the only way to know\nyour program&#8217;s performance characteristics for sure\nis to <i>sit down and measure it<\/i>.)\n<\/p>\n<p>\nWhat I think is going on is that the JavaScript optimizer\ncan do a really good job of optimizing the manual code\nsince it is very simple.\nThere are no function calls, the loop body is just one line,\nit&#8217;s all right out there in the open.\nThe versions that use intrinsics end up hiding some of the\ninformation from the optimizer.\n(After all, the optimizer cannot predict ahead of time whether\nsomebody has overridden the default implementation of\n<code>Array.prototype.reduce<\/code> or\n<code>Math.prototype.min<\/code>,\nso it cannot blindly inline the calls.)\nThe result is that the manual version can run over twice\nas fast on IE9 and over four times as fast on IE10.\n<\/p>\n<p>\nI got it wrong because I thought of JavaScript too much like\nan interpreted language.\nIn a purely interpreted language,\nthe overhead of the interpreter is roughly proportional\nto the number of things you ask it to do,\nas opposed to how hard it was to do any of those things.\nIt&#8217;s like a fixed service fee imposed on every transaction,\nregardless of whether the transaction was for $100 or 50 cents.\nYou therefore try to make one big purchase (call a complex intrinsic)\ninstead of a lot of small ones (read an array element,\ncompare two values, increment a variable, copy one variable to another).\n<\/p>\n<p>\n<b>Bonus chatter<\/b>:\nI ran the test on Firefox, too,\nbecause I happened to have it handy.\n<\/p>\n<table BORDER=\"1\" CELLPADDING=\"3\" STYLE=\"border-collapse: collapse\">\n<caption>\n    <font SIZE=\"+1\"><b>Relative running time per array element<\/b><\/font>\n<\/caption>\n<tr>\n<th>Elements<\/th>\n<th>manual<\/th>\n<th>reduce<\/th>\n<th>min\/indexOf<\/th>\n<\/tr>\n<tr>\n<th COLSPAN=\"4\">Firefox 16<\/th>\n<\/tr>\n<tr>\n<td ALIGN=\"right\">100,000<\/td>\n<td ALIGN=\"right\">1.000<\/td>\n<td ALIGN=\"right\">21.598<\/td>\n<td ALIGN=\"right\">3.958<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"right\">200,000<\/td>\n<td ALIGN=\"right\">0.848<\/td>\n<td ALIGN=\"right\">21.701<\/td>\n<td ALIGN=\"right\">2.515<\/td>\n<\/tr>\n<tr>\n<td ALIGN=\"right\">250,000<\/td>\n<td ALIGN=\"right\">0.839<\/td>\n<td ALIGN=\"right\">21.788<\/td>\n<td ALIGN=\"right\">2.090<\/td>\n<\/tr>\n<\/table>\n<p>\nThe same data collected on Firefox 16\n(which sounds ridiculously old because Firefox\nwill be on version 523 by the time this\narticle reaches the head of the queue)\nshows a different profile,\nalthough the winner is the same.\nThe manual loop and the min\/indexOf get more efficient\nas the array size increases.\nThis suggests that there is fixed overhead that becomes\ngradually less significant as you increase the size of the\ndata set.\n<\/p>\n<p>\nOne thing that jumps out is that the reduce method way\nunderperforms the others.\nMy guess is that setting up the function call\n(in order to transition between the intrinsic and the script)\nis very expensive,\nand that implementors of the JavaScript engines haven&#8217;t\nspent any time optimizing this case because\n<code>reduce<\/code> is not used much in real-world code.\n<\/p>\n<p>\n<b>Update<\/b>:\nI exaggerated my na&iuml;vet&eacute; to make for a better\nnarrative.\nAs I point out in\n<a HREF=\"http:\/\/my.safaribooksonline.com\/book\/programming\/microsoft-windows\/0321440307\/preface\/pref02\">\nthe preface<\/a>\nto\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2006\/12\/07\/1233002.aspx\">\nmy book<\/a>,\nmy stories may not be completely true,\nbut they are true enough.\nOf course I know that JavaScript is jitted nowadays,\nand that changes the calculus.\n(Also, the hidden array copy.)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today&#8217;s Little Program isn&#8217;t even a program. It&#8217;s just a function. The problem statement is as follows: Given a nonempty JavaScript array of numbers, find the index of the smallest value. (If the smallest value appears more than once, then any such index is acceptable.) One solution is simply to do the operation manually, simulating [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-903","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Today&#8217;s Little Program isn&#8217;t even a program. It&#8217;s just a function. The problem statement is as follows: Given a nonempty JavaScript array of numbers, find the index of the smallest value. (If the smallest value appears more than once, then any such index is acceptable.) One solution is simply to do the operation manually, simulating [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/903","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=903"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/903\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=903"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=903"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=903"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}