{"id":19364213,"url":"https://github.com/mindrones/d3-benchmarks","last_synced_at":"2025-08-23T09:09:09.960Z","repository":{"id":90726677,"uuid":"75218509","full_name":"mindrones/d3-benchmarks","owner":"mindrones","description":"Testing some d3 functions","archived":false,"fork":false,"pushed_at":"2017-01-31T16:59:11.000Z","size":1403,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-24T13:13:02.982Z","etag":null,"topics":["benchmarking","d3js","data-visualization","path","performance","rounding"],"latest_commit_sha":null,"homepage":"https://mindrones.github.io/d3-benchmarks","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mindrones.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-11-30T19:14:25.000Z","updated_at":"2017-02-07T18:54:08.000Z","dependencies_parsed_at":null,"dependency_job_id":"7c66af00-b657-4573-97e0-d63a7878fa51","html_url":"https://github.com/mindrones/d3-benchmarks","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mindrones/d3-benchmarks","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mindrones%2Fd3-benchmarks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mindrones%2Fd3-benchmarks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mindrones%2Fd3-benchmarks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mindrones%2Fd3-benchmarks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mindrones","download_url":"https://codeload.github.com/mindrones/d3-benchmarks/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mindrones%2Fd3-benchmarks/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271746301,"owners_count":24813556,"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-08-23T02:00:09.327Z","response_time":69,"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":["benchmarking","d3js","data-visualization","path","performance","rounding"],"created_at":"2024-11-10T07:36:35.908Z","updated_at":"2025-08-23T09:09:09.933Z","avatar_url":"https://github.com/mindrones.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# d3 benchmarks\n\nSome tests to support the discussion in [this d3-path issue](https://github.com/d3/d3-path/issues/10).\n\n## Installation\n\n- `npm install`\n- `npm run build` (always run this after modifying files in `src/implementations`)\n\n## Benches\n\n### path\n\nThis bench compares different implementations of d3's `path`, saving:\n- **execution time**,\n- **heap memory** used by the instance of `path` after running a command (`moveTo`, etc) `N` times.\n\nRun with: `npm run path`.\n\n#### Implementations of `d3.path()`\n\n- [current](https://github.com/d3/d3-path/blob/master/src/path.js): official version\n- [withFormat](https://github.com/mindrones/d3-benchmarks/blob/master/src/path/withFormat.js):\n\n  - `pathCoerceFixed`: input value coercion to a number and truncation via `.toFixed()` (copied from [this PR](https://github.com/d3/d3-path/blob/fixed/src/path.js) and renamed `pathFixed` -\u003e `pathCoerceFixed` as we use `pathFixed` with no input value coercion):\n\n    ```js\n    export function pathCoerceFixed(digits) {\n        var path = new Path;\n        (digits = +digits).toFixed(digits); // Validate digits.\n        path._format = function(x) { return +x.toFixed(digits); };\n        return path;\n    }\n    ```\n\n  - `pathFixed`: similar to the previous one without input value coercion, truncation via `.toFixed()`:\n\n    ```js\n    export function pathFixed(digits) {\n        var path = new Path;\n        (digits = +digits).toFixed(digits); // Validate digits.\n        path._format = function(x) { return x.toFixed(digits); };\n        return path;\n    }\n    ```\n\n  - `pathCoerceRound`: input value coercion to a number and truncation via [round](https://github.com/d3/d3-format/issues/32):\n\n    ```js\n    import {round} from '../utils/round'\n    export function pathCoerceRound(digits) {\n        var path = new Path;\n        (digits = +digits).toFixed(digits); // Validate digits.\n        path._format = function(x) { return round(+x, digits); };\n        return path;\n    }\n    ```\n\n  - `pathRound`: no input value coercion and truncation via same `round` as above:\n\n    ```js\n    export function pathRound(digits) {\n      var path = new Path;\n      digits = +digits).toFixed(digits); // Validate digits.\n      path._format = function(x) { return round(x, digits); };\n      return path;\n    }\n    ```\n\n- [withIf](https://github.com/mindrones/d3-benchmarks/blob/master/src/path/withIf.js): instead of using a `format()` function, use `if`s and round if we provided `digits`:\n  ```js\n  import {round as R} from '../utils/round'\n\n  // ...\n  moveTo: function(x, y) {\n    if (this._d) {\n      this._ += `M${R(this._x0 = this._x1 = x, this._d)},${R(this._y0 = this._y1 = y, this._d)}`;\n    } else {\n      this._ += `M${this._x0 = this._x1 = x},${this._y0 = this._y1 = y}`;\n    }\n  },\n  ```\n  This implementation duplicates code, so if the case we could test assigning values to temporary vars like this:\n  ```js\n  moveTo: function(x, y) {\n    this._x0 = this._x1 = this._d ? R(x, this._d) : x\n    this._y0 = this._y1 = this._d ? R(y, this._d) : y\n    this._ += `M${this._x0},${this._y0}`;\n  },\n  ```\n\n#### Results\n\nResults are saved in `./data/path.json` as a list of objects like:\n\n```js\n{\n  \"impl\": \"path.current.path\",\n  \"digits\": null,\n  \"command\": \"moveTo\",\n  \"calls\": 1,\n  \"heap\": 46464.64,\n  \"duration\": 3.170281801295085e-7\n}\n```\n\n- `impl`: name of the implementation\n- `digits`: digits we passed to `path()`\n- `command`: executed path command ('moveTo', 'lineTo', etc)\n- `calls`: how many times we invoked the command on the path instance `p`\n- `heap`: heap memory used by the `p` instance after calling the `coommand` `calls` times, in bytes\n- `duration`: mean test execution time, in seconds\n\n**You can explore the results with this [interactive chart](https://mindrones.github.io/d3-benchmarks/path)**\n\n#### How to interpret results\n\nIdeally, for a given amount of calls of a certain command (i.e. '3 calls of `moveTo`' meaning `p.moveTo(N,N).moveTo(N,N).moveTo(N,N)`), we should expect this kind of results:\n\n![Ideal results](./doc/images/path_bench_ideal_result.png)\n\nThis is because calling a command with no digits always returns the same path string, the longest possible, no matter the chosen implementation, while passing values of `digits` lower than the maximum precision allowed by the platform (see [Number.EPSILON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON)) shortens the returned path string.\n\nFor example, assuming `N = 10.1234567890123456` (16 digits):\n\n- `withFormat.path().moveTo(N,N)        =\u003e \"M10.1234567890123456,10.1234567890123456\"`\n- `withFormat.pathRound(15).moveTo(N,N) =\u003e \"M10.123456789012346,10.123456789012346\"`\n- `withFormat.pathRound(10).moveTo(N,N) =\u003e \"M10.1234567890,10.1234567890\"`\n- `withFormat.pathRound(5).moveTo(N,N)  =\u003e \"M10.12346,10.12346\"`\n- `withFormat.pathRound(0).moveTo(N,N)  =\u003e \"M10,10\"`\n\nHence, decreasing digits should lower the used heap as the instance of path has to store a shorter string, but rounding the input value should increase the execution time.\n\nHere's an example of we what get in practice:\n\n![Example](./doc/images/path_bench_example.png)\n\n#### Let's try this on a real visualization!\n\nThis [interactive chart](https://mindrones.github.io/d3-benchmarks/path_client) shows a moving circle that we control via:\n\n- how many points the circle is made of: 1e2, 1e3, 1e4, 1e5;\n- which implementation of `line()` to be used: current official `d3.line()` or custom implementation using `withFormat.path()` (see above);\n- how many digits to be used to round numbers.\n\nAt least of my machine:\n- below 1e5 points I see no difference in performance, no matter the implementation or number of digits;\n- using 1e5 points, not rounding would seem ~2x slower than rounding with 0 digits, so there's indeed an effect on performance.\n\n![path_client: withFormat, no rounding ](./doc/images/path_client_withFormat_null.png)\n![path_client: withFormat, rounding with 3 digits](./doc/images/path_client_withFormat_3.png)\n![path_client: withFormat, rounding with 0 digits](./doc/images/path_client_withFormat_0.png)\n\n\n### round\n\nCompare two rounding functions and `.toFixed()`, also coercing the input (be it a number or a string) to a number, to check the coercion impact on speed.\n\nTested implementations:\n\n  - [mbostock's round](https://github.com/d3/d3-format/issues/32)\n  - [MDN php-like round](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round#PHP-Like_rounding_Method)\n\nRun with: `npm run round`.\n\nResults are saved in `./data/rounding.json` as a list of objects like:\n\n```js\n{\n  \"impl\": \"round\",\n  \"transform\": null,\n  \"digits\": 0,\n  \"duration\": 1.5478552534985366e-8\n},\n```\n\n- `impl`: name of the rounding implementation\n- `transform`: input coercion\n  - null: no coercion\n  - 'CoerceNumber': `+N`\n  - 'CoerceString': `+(N.toString())`\n- `digits`: digits we passed to the rounding function\n  - `round(N, digits)`,\n  - `roundMDN(N, digits)`,\n  - `N.toFixed(digits)`\n- `duration`: mean test execution time, in seconds\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmindrones%2Fd3-benchmarks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmindrones%2Fd3-benchmarks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmindrones%2Fd3-benchmarks/lists"}