Coder Social home page Coder Social logo

mindrones / d3-benchmarks Goto Github PK

View Code? Open in Web Editor NEW
0.0 2.0 0.0 1.37 MB

Testing some d3 functions

Home Page: https://mindrones.github.io/d3-benchmarks

License: GNU General Public License v3.0

JavaScript 90.84% CSS 8.11% HTML 1.05%
d3js benchmarking rounding performance path data-visualization

d3-benchmarks's Introduction

# d3 benchmarks

Some tests to support the discussion in this d3-path issue.

Installation

  • npm install
  • npm run build (always run this after modifying files in src/implementations)

Benches

path

This bench compares different implementations of d3's path, saving:

  • execution time,
  • heap memory used by the instance of path after running a command (moveTo, etc) N times.

Run with: npm run path.

Implementations of d3.path()

  • current: official version

  • withFormat:

    • pathCoerceFixed: input value coercion to a number and truncation via .toFixed() (copied from this PR and renamed pathFixed -> pathCoerceFixed as we use pathFixed with no input value coercion):

      export function pathCoerceFixed(digits) {
          var path = new Path;
          (digits = +digits).toFixed(digits); // Validate digits.
          path._format = function(x) { return +x.toFixed(digits); };
          return path;
      }
    • pathFixed: similar to the previous one without input value coercion, truncation via .toFixed():

      export function pathFixed(digits) {
          var path = new Path;
          (digits = +digits).toFixed(digits); // Validate digits.
          path._format = function(x) { return x.toFixed(digits); };
          return path;
      }
    • pathCoerceRound: input value coercion to a number and truncation via round:

      import {round} from '../utils/round'
      export function pathCoerceRound(digits) {
          var path = new Path;
          (digits = +digits).toFixed(digits); // Validate digits.
          path._format = function(x) { return round(+x, digits); };
          return path;
      }
    • pathRound: no input value coercion and truncation via same round as above:

      export function pathRound(digits) {
        var path = new Path;
        digits = +digits).toFixed(digits); // Validate digits.
        path._format = function(x) { return round(x, digits); };
        return path;
      }
  • withIf: instead of using a format() function, use ifs and round if we provided digits:

    import {round as R} from '../utils/round'
    
    // ...
    moveTo: function(x, y) {
      if (this._d) {
        this._ += `M${R(this._x0 = this._x1 = x, this._d)},${R(this._y0 = this._y1 = y, this._d)}`;
      } else {
        this._ += `M${this._x0 = this._x1 = x},${this._y0 = this._y1 = y}`;
      }
    },

    This implementation duplicates code, so if the case we could test assigning values to temporary vars like this:

    moveTo: function(x, y) {
      this._x0 = this._x1 = this._d ? R(x, this._d) : x
      this._y0 = this._y1 = this._d ? R(y, this._d) : y
      this._ += `M${this._x0},${this._y0}`;
    },

Results

Results are saved in ./data/path.json as a list of objects like:

{
  "impl": "path.current.path",
  "digits": null,
  "command": "moveTo",
  "calls": 1,
  "heap": 46464.64,
  "duration": 3.170281801295085e-7
}
  • impl: name of the implementation
  • digits: digits we passed to path()
  • command: executed path command ('moveTo', 'lineTo', etc)
  • calls: how many times we invoked the command on the path instance p
  • heap: heap memory used by the p instance after calling the coommand calls times, in bytes
  • duration: mean test execution time, in seconds

You can explore the results with this interactive chart

How to interpret results

Ideally, 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:

Ideal results

This 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) shortens the returned path string.

For example, assuming N = 10.1234567890123456 (16 digits):

  • withFormat.path().moveTo(N,N) => "M10.1234567890123456,10.1234567890123456"
  • withFormat.pathRound(15).moveTo(N,N) => "M10.123456789012346,10.123456789012346"
  • withFormat.pathRound(10).moveTo(N,N) => "M10.1234567890,10.1234567890"
  • withFormat.pathRound(5).moveTo(N,N) => "M10.12346,10.12346"
  • withFormat.pathRound(0).moveTo(N,N) => "M10,10"

Hence, 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.

Here's an example of we what get in practice:

Example

Let's try this on a real visualization!

This interactive chart shows a moving circle that we control via:

  • how many points the circle is made of: 1e2, 1e3, 1e4, 1e5;
  • which implementation of line() to be used: current official d3.line() or custom implementation using withFormat.path() (see above);
  • how many digits to be used to round numbers.

At least of my machine:

  • below 1e5 points I see no difference in performance, no matter the implementation or number of digits;
  • using 1e5 points, not rounding would seem ~2x slower than rounding with 0 digits, so there's indeed an effect on performance.

path_client: withFormat, no rounding path_client: withFormat, rounding with 3 digits path_client: withFormat, rounding with 0 digits

round

Compare 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.

Tested implementations:

Run with: npm run round.

Results are saved in ./data/rounding.json as a list of objects like:

{
  "impl": "round",
  "transform": null,
  "digits": 0,
  "duration": 1.5478552534985366e-8
},
  • impl: name of the rounding implementation
  • transform: input coercion
    • null: no coercion
    • 'CoerceNumber': +N
    • 'CoerceString': +(N.toString())
  • digits: digits we passed to the rounding function
    • round(N, digits),
    • roundMDN(N, digits),
    • N.toFixed(digits)
  • duration: mean test execution time, in seconds

d3-benchmarks's People

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.