GitHub PullRequestReviewCommentEvent at 2014-11-28 11:49:21
Repository: jsbear
Actor: wayou
Payload: {"action":"created","comment":{"url":"https://api.github.com/repos/tbfe/jsbear/pulls/comments/21027739","id":21027739,"diff_hunk":"@@ -0,0 +1,207 @@\n+title: JavaScript的基准测试\n+toc: true\n+dropcap: false\n+categories: 译文\n+date: 2014-11-27 10:22:07\n+author: wayou\n+tags:\n+- javascript\n+- 性能\n+- benchmark\n+---\n+\n+>原文：[Bulletproof JavaScript benchmarks](https://mathiasbynens.be/notes/javascript-benchmarking)\n+\n+做JavaScript的基准测试并没有想的那么简单。即使不考虑浏览器差异所带来的影响，也有很多难点-或者说陷阱需要面对。\n+\n+<!-- more -->\n+\n+这是为何我创建了[jsPerf](http://jsperf.com/)的一个原因，一个你可以轻松创建并分享各种代码片段对比结果的简单工具。用起来非常省事，只需把想要测试的代码录入然后jsPerf会为你创建好可以跨平台跑起来的测试用例。\n+\n+内部实现上，最开始jsPerf用的是一个基于[JSLitmus](http://www.broofa.com/Tools/JSLitmus/)的基准测试库，我将它称作[Benchmark.js](http://benchmarkjs.com/)。后续又往里面添加了更多的特性，最近，[John-David Dalton](http://allyoucanleet.com/)干脆将这个库彻底重写了一遍。所以现在Benchmark.js已经比之前好很多了。\n+\n+本文将对JavaScript基准测试的编写和运行有一定的参考意义。\n+\n+# 基准测试的类型\n+\n+有很多方法可以测试一段JavaScript代码的性能。最常见的做法是类似下面这样的：\n+\n+## 方案A\n+\n+```js\n+var totalTime,\n+    start = new Date,\n+    iterations = 6;\n+while (iterations--) {\n+  // 被测试的代码\n+}\n+// totalTime → 运行该测试代码6次需要的时间（单位：毫秒）\n+totalTime = new Date - start;\n+```\n+\n+这种方案将被测试的代码循环执行多次直到预设值（本例为6次）。最后用结束时的时间减去开始的时间，得到运行的总时间。\n+\n+ 方案A被用于[SlickSpeed](https://github.com/kamicane/slickspeed/), [Taskspeed](https://github.com/phiggins42/taskspeed), [SunSpider](http://www2.webkit.org/perf/sunspider/sunspider.html), 和 [Kraken](http://krakenbenchmark.mozilla.org/)这些流行的基准测试库中。\n+\n+## 缺撼\n+\n+ 鉴于现在的设备和浏览器运行得越来越快，这种将代码运行固定次数的测试方法有很大概念会得到一个0ms的时间差结果，显然0是毫无意义的。\n+\n+## 方案B\n+\n+另一种方案是计算固定时间内进行了多少运算量。较之前的做法，这回你不用指定一个固定的循环次数了。\n+\n+```js\n+var hz,\n+    period,\n+    startTime = new Date,\n+    runs = 0;\n+do {\n+  // 被测试的代码\n+  runs++;\n+  totalTime = new Date - startTime;\n+} while (totalTime < 1000);\n+\n+// 将毫秒转为秒\n+totalTime /= 1000;\n+\n+// period → 单位运算的耗时\n+period = totalTime / runs;\n+\n+// hz → 单位时间（1秒）内进行的运算量\n+hz = 1 / period;\n+\n+// 上面两步可以简写如下：\n+// hz = (runs * 1000) / totalTime;\n+```\n+\n+将测试代码一直循环运行直到总耗时`totalTime` 大于等于1000毫秒，也就是1秒种。\n+\n+方案B 用于[Dromaeo](http://dromaeo.com/)和[V8 Benchmark Suite](http://code.google.com/apis/v8/benchmarks.html)这两个库。\n+\n+## 不足\n+\n+由于有垃圾回收，(运行时的)引擎对代码的动态优化以及其他进程等的影响，此方案在重复进行测试时得到的结果不尽相同。为了得到更精确的测试结果，需要多次测试取均值。而上面提到的V8 库只会对测试运行一次，Dromaeo 则会运行5次，但其实还可以做得更彻底以获取更加精准的结果。一个可行的途径就是想办法将目前的测试时间由1000毫秒压缩到50毫秒，当然前提是系统提供给我们一个没有误差且绝对精确的时钟，这能保证时间尽可能多地用于运行测试代码（而不会过多地被操作系统的中间停顿浪费掉）。\n+\n+## 方案 C\n+\n+JSLitmus 这个库结合了前面两种方案的优点。采用方案A 来将测试代码运行`n`次，同时动态调整这个`n`值以保证测试能够进行到一个最小的时长，也就是方案B所描述的那样。\n+\n+## 症结\n+\n+JSLitmus 规避了方案A的缺点但同时引入了方案B的不足之处。为了进一步提高测试的准确率，JSLitmus 将结果进行了量化，取出3次空测试（译注：不太理解这里的`空测试`为何物，不挂测试代码空跑？？）中运行最快的一次，再将每次基准测试的结果减去这个最快值。不幸的是这种做法为了规避B方案的毛病（译注：B方案需要运行多次以得到更多采样集合以取均值，换句话说要得到越准确的结果就要耗费越多的时间）反而使结果更不可靠了，因为取3次中最快的一次本身就不符合统计规律（译注：按统计学的做法，为了得到3次中最快的一次结果，这里又需要运行另外的测试来拿到一个所谓的最快的结果的集合，然后从中求均值）。尽管JSLitmus可以多次运行这样的基准测试，将量化后的均值与每次测试结果的均值进行差额运算，但这样得到的最终结果其身上的误差已经足够掩盖之前我们为了提高准确率而做的任何努力了。\n+\n+## 方案 D\n+\n+前面三种方案的短肋可以通过方法转编(`function compilation` 编译转化之意)和循环展开(loop unrolling)。\n+\n+```js\n+function test() {\n+  x == y;\n+}\n+\n+while (iterations--) {\n+  test();\n+}\n+\n+// …将会编译转化为 →\n+var hz,\n+    startTime = new Date;\n+\n+x == y;\n+x == y;\n+x == y;\n+x == y;\n+x == y;\n+// …\n+\n+hz = (runs * 1000) / (new Date - startTime);\n+```\n+\n+这种做法将测试代码[变成了展开的形式](http://en.wikipedia.org/wiki/Loop_unwinding)，避免了循环和量化工作(译注：没有了循环也就无需统计单位时间内的运算量了)。\n+\n+## 问题\n+\n+然而，纵然如此它还是有不足之处的。将函数转编会消耗大量内存同时也把CPU拖慢。当你把一个测试跑上几百万次时，可以想象到会创建大量的字符串和转编无数的函数。","path":"source/_posts/2014-11-27-javascript-benchmarking.md","position":125,"original_position":125,"commit_id":"3c3454beac4a9f129e16cf3876427054d58c4f56","original_commit_id":"3c3454beac4a9f129e16cf3876427054d58c4f56","user":{"login":"wayou","id":3783096,"avatar_url":"https://avatars.githubusercontent.com/u/3783096?v=3","gravatar_id":"","url":"https://api.github.com/users/wayou","html_url":"https://github.com/wayou","followers_url":"https://api.github.com/users/wayou/followers","following_url":"https://api.github.com/users/wayou/following{/other_user}","gists_url":"https://api.github.com/users/wayou/gists{/gist_id}","starred_url":"https://api.github.com/users/wayou/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wayou/subscriptions","organizations_url":"https://api.github.com/users/wayou/orgs","repos_url":"https://api.github.com/users/wayou/repos","events_url":"https://api.github.com/users/wayou/events{/privacy}","received_events_url":"https://api.github.com/users/wayou/received_events","type":"User","site_admin":false},"body":"+1","created_at":"2014-11-28T11:49:21Z","updated_at":"2014-11-28T11:49:21Z","html_url":"https://github.com/tbfe/jsbear/pull/1#discussion_r21027739","pull_request_url":"https://api.github.com/repos/tbfe/jsbear/pulls/1","_links":{"self":{"href":"https://api.github.com/repos/tbfe/jsbear/pulls/comments/21027739"},"html":{"href":"https://github.com/tbfe/jsbear/pull/1#discussion_r21027739"},"pull_request":{"href":"https://api.github.com/repos/tbfe/jsbear/pulls/1"}}},"pull_request":{"url":"https://api.github.com/repos/tbfe/jsbear/pulls/1","id":25126354,"html_url":"https://github.com/tbfe/jsbear/pull/1","diff_url":"https://github.com/tbfe/jsbear/pull/1.diff","patch_url":"https://github.com/tbfe/jsbear/pull/1.patch","issue_url":"https://api.github.com/repos/tbfe/jsbear/issues/1","number":1,"state":"open","locked":false,"title":"New post: Bulletproof JavaScript benchmarks","user":{"login":"wayou","id":3783096,"avatar_url":"https://avatars.githubusercontent.com/u/3783096?v=3","gravatar_id":"","url":"https://api.github.com/users/wayou","html_url":"https://github.com/wayou","followers_url":"https://api.github.com/users/wayou/followers","following_url":"https://api.github.com/users/wayou/following{/other_user}","gists_url":"https://api.github.com/users/wayou/gists{/gist_id}","starred_url":"https://api.github.com/users/wayou/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wayou/subscriptions","organizations_url":"https://api.github.com/users/wayou/orgs","repos_url":"https://api.github.com/users/wayou/repos","events_url":"https://api.github.com/users/wayou/events{/privacy}","received_events_url":"https://api.github.com/users/wayou/received_events","type":"User","site_admin":false},"body":"","created_at":"2014-11-27T02:31:41Z","updated_at":"2014-11-28T11:49:21Z","closed_at":null,"merged_at":null,"merge_commit_sha":"96395fce9c458339af3ee709ca1dd777e9404096","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/tbfe/jsbear/pulls/1/commits","review_comments_url":"https://api.github.com/repos/tbfe/jsbear/pulls/1/comments","review_comment_url":"https://api.github.com/repos/tbfe/jsbear/pulls/comments/{number}","comments_url":"https://api.github.com/repos/tbfe/jsbear/issues/1/comments","statuses_url":"https://api.github.com/repos/tbfe/jsbear/statuses/3c3454beac4a9f129e16cf3876427054d58c4f56","head":{"label":"wayou:master","ref":"master","sha":"3c3454beac4a9f129e16cf3876427054d58c4f56","user":{"login":"wayou","id":3783096,"avatar_url":"https://avatars.githubusercontent.com/u/3783096?v=3","gravatar_id":"","url":"https://api.github.com/users/wayou","html_url":"https://github.com/wayou","followers_url":"https://api.github.com/users/wayou/followers","following_url":"https://api.github.com/users/wayou/following{/other_user}","gists_url":"https://api.github.com/users/wayou/gists{/gist_id}","starred_url":"https://api.github.com/users/wayou/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wayou/subscriptions","organizations_url":"https://api.github.com/users/wayou/orgs","repos_url":"https://api.github.com/users/wayou/repos","events_url":"https://api.github.com/users/wayou/events{/privacy}","received_events_url":"https://api.github.com/users/wayou/received_events","type":"User","site_admin":false},"repo":{"id":27184440,"name":"jsbear","full_name":"wayou/jsbear","owner":{"login":"wayou","id":3783096,"avatar_url":"https://avatars.githubusercontent.com/u/3783096?v=3","gravatar_id":"","url":"https://api.github.com/users/wayou","html_url":"https://github.com/wayou","followers_url":"https://api.github.com/users/wayou/followers","following_url":"https://api.github.com/users/wayou/following{/other_user}","gists_url":"https://api.github.com/users/wayou/gists{/gist_id}","starred_url":"https://api.github.com/users/wayou/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wayou/subscriptions","organizations_url":"https://api.github.com/users/wayou/orgs","repos_url":"https://api.github.com/users/wayou/repos","events_url":"https://api.github.com/users/wayou/events{/privacy}","received_events_url":"https://api.github.com/users/wayou/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/wayou/jsbear","description":"","fork":true,"url":"https://api.github.com/repos/wayou/jsbear","forks_url":"https://api.github.com/repos/wayou/jsbear/forks","keys_url":"https://api.github.com/repos/wayou/jsbear/keys{/key_id}","collaborators_url":"https://api.github.com/repos/wayou/jsbear/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/wayou/jsbear/teams","hooks_url":"https://api.github.com/repos/wayou/jsbear/hooks","issue_events_url":"https://api.github.com/repos/wayou/jsbear/issues/events{/number}","events_url":"https://api.github.com/repos/wayou/jsbear/events","assignees_url":"https://api.github.com/repos/wayou/jsbear/assignees{/user}","branches_url":"https://api.github.com/repos/wayou/jsbear/branches{/branch}","tags_url":"https://api.github.com/repos/wayou/jsbear/tags","blobs_url":"https://api.github.com/repos/wayou/jsbear/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/wayou/jsbear/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/wayou/jsbear/git/refs{/sha}","trees_url":"https://api.github.com/repos/wayou/jsbear/git/trees{/sha}","statuses_url":"https://api.github.com/repos/wayou/jsbear/statuses/{sha}","languages_url":"https://api.github.com/repos/wayou/jsbear/languages","stargazers_url":"https://api.github.com/repos/wayou/jsbear/stargazers","contributors_url":"https://api.github.com/repos/wayou/jsbear/contributors","subscribers_url":"https://api.github.com/repos/wayou/jsbear/subscribers","subscription_url":"https://api.github.com/repos/wayou/jsbear/subscription","commits_url":"https://api.github.com/repos/wayou/jsbear/commits{/sha}","git_commits_url":"https://api.github.com/repos/wayou/jsbear/git/commits{/sha}","comments_url":"https://api.github.com/repos/wayou/jsbear/comments{/number}","issue_comment_url":"https://api.github.com/repos/wayou/jsbear/issues/comments/{number}","contents_url":"https://api.github.com/repos/wayou/jsbear/contents/{+path}","compare_url":"https://api.github.com/repos/wayou/jsbear/compare/{base}...{head}","merges_url":"https://api.github.com/repos/wayou/jsbear/merges","archive_url":"https://api.github.com/repos/wayou/jsbear/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/wayou/jsbear/downloads","issues_url":"https://api.github.com/repos/wayou/jsbear/issues{/number}","pulls_url":"https://api.github.com/repos/wayou/jsbear/pulls{/number}","milestones_url":"https://api.github.com/repos/wayou/jsbear/milestones{/number}","notifications_url":"https://api.github.com/repos/wayou/jsbear/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/wayou/jsbear/labels{/name}","releases_url":"https://api.github.com/repos/wayou/jsbear/releases{/id}","created_at":"2014-11-26T16:00:25Z","updated_at":"2014-11-27T02:25:04Z","pushed_at":"2014-11-27T02:25:04Z","git_url":"git://github.com/wayou/jsbear.git","ssh_url":"git@github.com:wayou/jsbear.git","clone_url":"https://github.com/wayou/jsbear.git","svn_url":"https://github.com/wayou/jsbear","homepage":"http://tbfe.github.io/jsbear","size":3070,"stargazers_count":0,"watchers_count":0,"language":"CSS","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"tbfe:master","ref":"master","sha":"f06318549871fd89c4bd6c3fca1e9f873fc014dc","user":{"login":"tbfe","id":6056414,"avatar_url":"https://avatars.githubusercontent.com/u/6056414?v=3","gravatar_id":"","url":"https://api.github.com/users/tbfe","html_url":"https://github.com/tbfe","followers_url":"https://api.github.com/users/tbfe/followers","following_url":"https://api.github.com/users/tbfe/following{/other_user}","gists_url":"https://api.github.com/users/tbfe/gists{/gist_id}","starred_url":"https://api.github.com/users/tbfe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tbfe/subscriptions","organizations_url":"https://api.github.com/users/tbfe/orgs","repos_url":"https://api.github.com/users/tbfe/repos","events_url":"https://api.github.com/users/tbfe/events{/privacy}","received_events_url":"https://api.github.com/users/tbfe/received_events","type":"Organization","site_admin":false},"repo":{"id":26581684,"name":"jsbear","full_name":"tbfe/jsbear","owner":{"login":"tbfe","id":6056414,"avatar_url":"https://avatars.githubusercontent.com/u/6056414?v=3","gravatar_id":"","url":"https://api.github.com/users/tbfe","html_url":"https://github.com/tbfe","followers_url":"https://api.github.com/users/tbfe/followers","following_url":"https://api.github.com/users/tbfe/following{/other_user}","gists_url":"https://api.github.com/users/tbfe/gists{/gist_id}","starred_url":"https://api.github.com/users/tbfe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tbfe/subscriptions","organizations_url":"https://api.github.com/users/tbfe/orgs","repos_url":"https://api.github.com/users/tbfe/repos","events_url":"https://api.github.com/users/tbfe/events{/privacy}","received_events_url":"https://api.github.com/users/tbfe/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/tbfe/jsbear","description":"","fork":false,"url":"https://api.github.com/repos/tbfe/jsbear","forks_url":"https://api.github.com/repos/tbfe/jsbear/forks","keys_url":"https://api.github.com/repos/tbfe/jsbear/keys{/key_id}","collaborators_url":"https://api.github.com/repos/tbfe/jsbear/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/tbfe/jsbear/teams","hooks_url":"https://api.github.com/repos/tbfe/jsbear/hooks","issue_events_url":"https://api.github.com/repos/tbfe/jsbear/issues/events{/number}","events_url":"https://api.github.com/repos/tbfe/jsbear/events","assignees_url":"https://api.github.com/repos/tbfe/jsbear/assignees{/user}","branches_url":"https://api.github.com/repos/tbfe/jsbear/branches{/branch}","tags_url":"https://api.github.com/repos/tbfe/jsbear/tags","blobs_url":"https://api.github.com/repos/tbfe/jsbear/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/tbfe/jsbear/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/tbfe/jsbear/git/refs{/sha}","trees_url":"https://api.github.com/repos/tbfe/jsbear/git/trees{/sha}","statuses_url":"https://api.github.com/repos/tbfe/jsbear/statuses/{sha}","languages_url":"https://api.github.com/repos/tbfe/jsbear/languages","stargazers_url":"https://api.github.com/repos/tbfe/jsbear/stargazers","contributors_url":"https://api.github.com/repos/tbfe/jsbear/contributors","subscribers_url":"https://api.github.com/repos/tbfe/jsbear/subscribers","subscription_url":"https://api.github.com/repos/tbfe/jsbear/subscription","commits_url":"https://api.github.com/repos/tbfe/jsbear/commits{/sha}","git_commits_url":"https://api.github.com/repos/tbfe/jsbear/git/commits{/sha}","comments_url":"https://api.github.com/repos/tbfe/jsbear/comments{/number}","issue_comment_url":"https://api.github.com/repos/tbfe/jsbear/issues/comments/{number}","contents_url":"https://api.github.com/repos/tbfe/jsbear/contents/{+path}","compare_url":"https://api.github.com/repos/tbfe/jsbear/compare/{base}...{head}","merges_url":"https://api.github.com/repos/tbfe/jsbear/merges","archive_url":"https://api.github.com/repos/tbfe/jsbear/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/tbfe/jsbear/downloads","issues_url":"https://api.github.com/repos/tbfe/jsbear/issues{/number}","pulls_url":"https://api.github.com/repos/tbfe/jsbear/pulls{/number}","milestones_url":"https://api.github.com/repos/tbfe/jsbear/milestones{/number}","notifications_url":"https://api.github.com/repos/tbfe/jsbear/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/tbfe/jsbear/labels{/name}","releases_url":"https://api.github.com/repos/tbfe/jsbear/releases{/id}","created_at":"2014-11-13T10:00:33Z","updated_at":"2014-11-27T06:43:45Z","pushed_at":"2014-11-27T06:45:31Z","git_url":"git://github.com/tbfe/jsbear.git","ssh_url":"git@github.com:tbfe/jsbear.git","clone_url":"https://github.com/tbfe/jsbear.git","svn_url":"https://github.com/tbfe/jsbear","homepage":"http://tbfe.github.io/jsbear","size":3376,"stargazers_count":1,"watchers_count":1,"language":"CSS","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1,"mirror_url":null,"open_issues_count":1,"forks":1,"open_issues":1,"watchers":1,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/tbfe/jsbear/pulls/1"},"html":{"href":"https://github.com/tbfe/jsbear/pull/1"},"issue":{"href":"https://api.github.com/repos/tbfe/jsbear/issues/1"},"comments":{"href":"https://api.github.com/repos/tbfe/jsbear/issues/1/comments"},"review_comments":{"href":"https://api.github.com/repos/tbfe/jsbear/pulls/1/comments"},"review_comment":{"href":"https://api.github.com/repos/tbfe/jsbear/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/tbfe/jsbear/pulls/1/commits"},"statuses":{"href":"https://api.github.com/repos/tbfe/jsbear/statuses/3c3454beac4a9f129e16cf3876427054d58c4f56"}}}}