{"id":444,"date":"2013-05-24T13:32:10","date_gmt":"2013-05-24T17:32:10","guid":{"rendered":"http:\/\/eligrey.com\/blog\/?p=444"},"modified":"2017-07-11T01:44:52","modified_gmt":"2017-07-11T08:44:52","slug":"cpu-core-estimation-with-javascript","status":"publish","type":"post","link":"https:\/\/eligrey.com\/blog\/cpu-core-estimation-with-javascript\/","title":{"rendered":"CPU core estimation with JavaScript"},"content":{"rendered":"<h2>(Update) Standardization<\/h2>\n<p>I have standardized navigator.cores as <a href=\"https:\/\/html.spec.whatwg.org\/multipage\/workers.html#navigator.hardwareconcurrency\">navigator.hardwareConcurrency<\/a>,\u00a0and it is now supported natively in Chrome, Safari, Firefox, and Opera. Our polyfill has renamed the APIs accordingly. Since the initial blog post, Core Estimator has been updated to estimate much faster and now has instant estimation in Chrome through PNaCl.<\/p>\n<h2>navigator.cores<\/h2>\n<p>So you just built some cool scalable multithreaded feature into your webapp with web workers. Maybe it&#8217;s machine learning-based webcam object recognition\u2014or a compression algorithm like LZMA2 that runs faster with the more cores that you have. Now, all you have to do is simply set the number of worker threads to use the user&#8217;s CPU as efficiently as possible&#8230;<\/p>\n<p>You might be thinking &#8220;Easy, there&#8217;s probably a <code>navigator.cores<\/code> API that will tell me how many cores the user&#8217;s CPU has.&#8221; That was our thought while porting xz to JavaScript (which will be released in the future as xz.js), and we were amazed there was no such API or any equivalent whatsoever in any browser! With all the new features of HTML5 which give more control over native resources, there must be a way to find out how many cores a user possesses.<\/p>\n<p>I immediately envisioned a timing attack that could attempt to estimate a user&#8217;s CPU cores to provide the optimal number of workers to spawn in parallel. It would scale from one to thousands of cores. With the help of <a href=\"https:\/\/github.com\/dsamarin\">Devin Samarin<\/a>, <a href=\"http:\/\/jon-carlos.com\/\">Jon-Carlos Rivera<\/a>, and <a href=\"http:\/\/devyn.me\/\">Devyn Cairns<\/a>, we created the open source library, <a href=\"https:\/\/github.com\/oftn\/core-estimator\">Core Estimator<\/a>. It implements a <code>navigator.cores<\/code> value that will only be computed once it is accessed. Hopefully in the future, this will be added to the HTML5 specification.<\/p>\n<h3>Live demo<\/h3>\n<p>Try out Core Estimator with the <a href=\"https:\/\/oswg.oftn.org\/projects\/core-estimator\/demo\/\">live demo<\/a> on our website.<\/p>\n<p><a title=\"Core Estimator demo run on an i7 3930k\" href=\"https:\/\/oswg.oftn.org\/projects\/core-estimator\/demo\/\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-448 size-full\" style=\"border: 1px solid rgba(0, 0, 0, 0.2);\" src=\"https:\/\/eligrey.com\/blog\/wp-content\/uploads\/2016\/02\/core-estimator-demo.png\" alt=\"screenshot of the demo being run on an i7 3930k\" width=\"681\" height=\"324\" \/><\/a><\/p>\n<h3>How the timing attack works and scales<\/h3>\n<p>The estimator works by performing a statistical test on running different numbers of simultaneous web workers. It measures the time it takes to run a single worker and compares this to the time it takes to run different numbers of workers simultaneously. As soon as this measurement starts to increase excessively, it has found the maximum number of web workers which can be run in parallel without degrading performance.<\/p>\n<p><a href=\"https:\/\/eligrey.com\/blog\/wp-content\/uploads\/2016\/02\/core-estimator-graph.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-447 size-full\" style=\"border: 1px solid rgba(0, 0, 0, 0.2);\" src=\"https:\/\/eligrey.com\/blog\/wp-content\/uploads\/2016\/02\/core-estimator-graph.png\" alt=\"\" width=\"617\" height=\"280\" \/><\/a><\/p>\n<p>In the early stages of testing whether this would work, we did a few experiments on various desktops to visualize the data being produced. The graphs being produced clearly showed that it was\u00a0feasible\u00a0on the average machine. Pictured are the results of running an early version of Core Estimator on Google Chrome 26 on an Intel Core i5-3570K 3.4GHz Quad-Core Processor with 1,000 time samples taken for each core test. We used 1,000 samples just to really be able to see the spread of data but it took over 15 minutes to collect this data. For Core Estimator, 5 samples seem to be sufficient.<\/p>\n<p>The astute observer will note that it doesn&#8217;t test each number of simultaneous workers by simply counting up. Instead, Core Estimator performs a binary search. This way the running time is logarithmic in the number of cores\u2014O(log n) instead of O(n). At most, 2 * floor(log2(n)) + 1 tests will be done to find the number of cores.<\/p>\n<h3>Benefits<\/h3>\n<p>Previously, you had to either manually code in an amount of threads or ask the user how many cores they have, which can be pretty difficult for less tech savvy users. This can even be a problem with tech savvy users\u2014few people know how many cores their phone has. Core Estimator helps you simplify your APIs so thread count parameters can be optional. The xz.js API will be as simple as <code>xz.compress(Blob data, callback(Blob compressed), optional int preset=6, optional int threads=navigator.cores)<\/code>, making it this easy to implement a &#8220;save .xz&#8221; button for your webapp (in conjunction with <a href=\"https:\/\/github.com\/eligrey\/FileSaver.js\">FileSaver.js<\/a>):<\/p>\n<pre lang=\"javascript\">save_button.addEventListener(\"click\", function() {\r\n    xz.compress(serializeDB(), function(compressed) {\r\n        saveAs(compressed, \"db.xz\");\r\n    });\r\n});<\/pre>\n<h3>Supported browsers and platforms<\/h3>\n<p>Early Core Estimator has been tested to support all current release versions of IE, Firefox, Chrome, and Safari on ARM and x86 (as of May 2013). The accuracy of Core Estimator on systems with Intel hyper-threading and Turbo Boost technology is somewhat lesser as the\u00a0time to complete a workload is less predictable. In any case it will try to tend towards estimating a larger number of cores than actually available to provide a somewhat reasonable number.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>(Update) Standardization I have standardized navigator.cores as navigator.hardwareConcurrency,\u00a0and it is now supported natively in Chrome, Safari, Firefox, and Opera. Our polyfill has renamed the APIs accordingly. Since the initial blog post, Core Estimator has been updated to estimate much faster and now has instant estimation in Chrome through PNaCl. navigator.cores So you just built some [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-444","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/pfpUD-7a","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/eligrey.com\/blog\/wp-json\/wp\/v2\/posts\/444","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/eligrey.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/eligrey.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/eligrey.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/eligrey.com\/blog\/wp-json\/wp\/v2\/comments?post=444"}],"version-history":[{"count":0,"href":"https:\/\/eligrey.com\/blog\/wp-json\/wp\/v2\/posts\/444\/revisions"}],"wp:attachment":[{"href":"https:\/\/eligrey.com\/blog\/wp-json\/wp\/v2\/media?parent=444"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/eligrey.com\/blog\/wp-json\/wp\/v2\/categories?post=444"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/eligrey.com\/blog\/wp-json\/wp\/v2\/tags?post=444"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}