{"id":32957566,"url":"https://github.com/kogarashisan/PerfTests","last_synced_at":"2025-11-16T19:01:26.520Z","repository":{"id":27921734,"uuid":"31413907","full_name":"kogarashisan/PerfTests","owner":"kogarashisan","description":"JavaScript inheritance performance tests done right","archived":false,"fork":false,"pushed_at":"2015-05-02T12:23:30.000Z","size":236,"stargazers_count":1,"open_issues_count":2,"forks_count":1,"subscribers_count":2,"default_branch":"gh-pages","last_synced_at":"2024-06-21T06:35:15.969Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://www.lava-framework.com/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kogarashisan.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-02-27T10:12:35.000Z","updated_at":"2022-07-27T12:41:16.000Z","dependencies_parsed_at":"2022-08-02T10:51:47.142Z","dependency_job_id":null,"html_url":"https://github.com/kogarashisan/PerfTests","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/kogarashisan/PerfTests","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kogarashisan%2FPerfTests","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kogarashisan%2FPerfTests/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kogarashisan%2FPerfTests/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kogarashisan%2FPerfTests/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kogarashisan","download_url":"https://codeload.github.com/kogarashisan/PerfTests/tar.gz/refs/heads/gh-pages","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kogarashisan%2FPerfTests/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":284759739,"owners_count":27058842,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-16T02:00:05.974Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-11-12T23:00:25.276Z","updated_at":"2025-11-16T19:01:26.515Z","avatar_url":"https://github.com/kogarashisan.png","language":"JavaScript","funding_links":[],"categories":["Benchmark - JavaScript"],"sub_categories":["Meetups"],"readme":"\u003ci\u003eUpdate 09.03.15: \"Native Unwrapped\" and ClassManager \"polymorphic\" and \"full export\" \ncases need to be removed as irrelevant. Will do that in spare time.\u003c/i\u003e\n\n# JavaScript class inheritance performance tests\n\nAccurate tests of JavaScript class inheritance models, which model real-world application behaviour \nwith aim to providing better accuracy.\n\nOnly the fastest and most popular class systems are tested. For example: speed of Prototype.js classes is about 1% of native,\nso there is no reason to include it. The same applies to MooTools. Ext.JS is not included, cause it affects results of\nother tests (from the words of other authors).\n\n##Where to see the results?\n\n- Overridden method calls: http://jsperf.com/js-inheritance-method-calls/7\n- Object construction: http://jsperf.com/js-inheritance-object-construction/3\n\n##Comparison to other test suites\n\nCreating micro-benchmarks is an art, which requires deep understanding of browser internals and some human adequateness.\n\nFor example: test that mixes class generation and method calls is incorrect (inadequate), cause class generation\nusually happens once, when page is loaded, and it takes much more time than a method call. Some systems, like\nTypeScript, CoffeeScript and Lava.ClassManager are able to generate classes on server - \nso it's absolutely incorrect to mix them with browser-only solutions, like John Resig's Class.\nAuthors of such tests can manipulate your opinion by adjusting loop counter numbers.\nSuch bad examples include tests of Fiber.js from LinkedIn and DotNetWise - they do just that.\n\nWhat else to watch for:\n- open the page with tests in a new window (important!) and run a test twice in a row.\nIf the second time some test runs slower, and result is stable - then it's wrong.\n- if result changes depending on order, in which you run tests - they are wrong,\ncause they affect each other.\n- if a test case shows high variance, like \"+/- 27.98%\" - this may also be a sign, that the test is wrong.\n\n\u003ci\u003eI do not claim absolute correctness of these tests, they are just more relevant than most of others.\u003c/i\u003e \n\n##Another bad example: how not to write tests\n\nLet's test speed of counter increment. Preparation code:\n\n```\n// declare a counter in window root:\nvar counter = 0;\nvar method = function() { counter++; }\n```\n\nNow the test itself. Nothing more than a loop:\n\n```\nfor (var i = 0; i \u003c 10000; i++) {\n    method();\n}\n```\n\nThis test is wrong in many ways:\n- first, `method` most likely will be inlined. In micro-benchmarks it may lead to incorrect results, \nespecially when you compare it to other methods, which were not inlined.\n- next, at some moment `counter` will overflow. When this happens, it's internal type will be changed from integer to double.\nDoubles are boxed like objects, so they are much slower than integers. \n- promotion to double will also make `method` a little heavier (if it wasn't inlined).\n\nTo create the right tests you should also understand how your benchmark suite works, \nwhat is polymorphism in property accessors, scope internals and many other things.\nIf you want to learn more, then I recommend you this site for reference: [mrale.ph](http://mrale.ph/) \n(it's not mine, but currently it's one of the best sources about JavaScript engine internals).\n\n##Techniques used\n\nThese tests try to provide most accurate results, although it's not guaranteed that they are 100% correct.\nFor example: browser vendors can implement different kinds of optimizations, which will lead to wrong results.\nIf I find any mistakes or room for improvement - I will do my best to maintain their quality.\n\nTo ensure accuracy, these tests use the following techniques:\n\n- Tested method's body should be large enough, so that it's not inlined. This is accomplished by the following code:\n```\nif (prevent_inline) {\n\t// lots of dummy code, which will never be executed\n}\n```\n\n- `counter` must not overflow. This is accomplished by cycling it in a small range:\n```\nif (this.counter \u003e 99) this.counter = this.counter / 2;\n```\nWhen `counter` hits 100, it's set to 50.\n\n- Tested method's body should be complex enough, so that result is not predicted by optimizer.\nThis is not guaranteed :)\n\n- Cache busting: all methods must have unique source code, so they are not reused by V8.\nThis is accomplished by appending unique variable declaration to the beginning of each method in the test suite\n(like `var cache_buster_0012;`).\n\n- Create polymorphism: each individual test creates instances of \u003ci\u003eseveral different\u003c/i\u003e child classes, that call same \n\u003ci\u003eoverridden\u003c/i\u003e parent's method. This shows issues with parent's method becoming polymorphic.\n\n##Explanation of Native\n\nNative model comes in two flavours:\n- unwrapped. Classes are defined in the root of `window` object\n- wrapped. The same classes, but wrapped into a closure and exported to `window`\n\nYou should notice the difference in method call speed between `Native (wrapped)` and `Native (unwrapped)` test cases.\nWhen classes are in the window root - Chrome often applies some kind of optimization, \nwhich boosts the speed of \"Native (unwrapped)\" test case twice. It happens time to time, and result is not consistent.\nTo make this optimization happen, your classes must strictly follow the pattern from `src/Native/unwrapped.js`.\nAt current moment this optimization seems to be disabled by wrapping classes into a closure.\n\n\u003cb\u003eIn real world most of your code will be wrapped into a closure:\u003c/b\u003e \nfor better compression, isolation, and for module loader compatibility.\nAnd inside Node.js environment each module is automatically wrapped in a closure.\n\nSo, to summarize this: you should use `Native (wrapped)` test case as reference - \nit's the real speed of the Native model. If you put your classes in window root - then you MAY get the speed of\n`Native (unwrapped)`, but... it does not always happen. Speed boost of unwrapped model may also mean, \nthat there is an error in my test case.\n\n##Changelog\n\nSee [CHANGELOG.md](https://github.com/kogarashisan/PerfTests/blob/gh-pages/CHANGELOG.md)\n\n##P.S.\n\nTo learn more about ClassManager, please visit it's official repository:\nhttps://github.com/kogarashisan/ClassManager","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkogarashisan%2FPerfTests","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkogarashisan%2FPerfTests","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkogarashisan%2FPerfTests/lists"}