Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Introducing web workers #233

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

princiya
Copy link
Collaborator

This is an initial attempt to improve the graph's performance for large no. of nodes.

Issue #232

The idea in this PR is to off-load the heavy force layout computations to the web worker. This improves performance during page load.

I am also experimenting on passing the logic to web workers during the drag events. This part is still work in progress.

Here is the rough idea that I try to achieve:

drag

In the present situation, when we drag, the force layout restarts the simulation. When there are large no. of nodes, the graph takes a lot of time to achieve equilibrium, thereby dropping the frame rates. Restarting the simulation is necessary, to compute the updated coordinates and give the soft transition, else the drag effect doesn't appear smooth.

Using web workers, I try to keep the dragged coordinates in the main thread, invoke the web worker during dragEnd. The web worker computes the updated coordinates and waits until the graph attains equilibrium. I need to figure a way to get the updated coordinates and achieve smooth animations.

cc @jonathanKingston @biancadanforth

@jonathanKingston
Copy link
Contributor

I think this is a cool experiment but I actually think a lot of the perf improvements could be improved in the main thread first which I did in #232. I can't remember the line of code but essentially I just reduced the large amount of recalc.

@princiya
Copy link
Collaborator Author

princiya commented Nov 19, 2017

@jonathanKingston
I think, you commented out the manualTick() and things still looked good. But there are other use cases and now that I understand how alpha really works, I shall try to explain them below.

simulateForce() {
    if (!this.simulation) {
      this.simulation = d3.forceSimulation(this.nodes);
      this.simulation.on('tick', () => this.drawOnCanvas());
      this.registerSimulationForces();
    } else {
      this.simulation.nodes(this.nodes);
    }
    this.registerLinkForce();
    this.manualTick();
  }
  • The simulation is initialised with d3.forceSimulation(this.nodes).
  • Default value of alpha is 1.
  • The tick() runs from alpha=1 to alpha=0.
  • The graph achieves equilibrium when alpha=0.
  • At each tick() the graph updates x, y, vx, vy for each node, by adhering to the registered forces.
    • Example: The updated x, y, vx, vy shouldn't result in a collision and this it takes care by making sure the collisionRadius is applied.

The tick function increments the current alpha by (alphaTarget – alpha) × alphaDecay; then invokes each registered force, passing the new alpha; then decrements each node’s velocity by velocity × velocityDecay; lastly increments each node’s position by velocity.

Case 1: Page Load

  • this.manualTick() is unnecessary.
  • The tick() runs from alpha=1 to alpha=0.
  • User sees animation with updated node coordinates and graph comes to stop when alpha=0.

Case 2: Add new nodes (alpha is still not 0)

  • Meanwhile, if new nodes are added, and the graph has still not achieved equilibrium (from the page load), then we get the right graph, i.e. simulation still tries to accommodate the newly added nodes and apply the registered forces via the tick() function.
  • The graph along with these newly added nodes are centered w.r.to to the display area, collision has been resolved etc.

Case 3: Add new nodes (alpha is 0)

  • When the graph has cooled down (alpha=0), and when new nodes come in, then these newly added nodes appear on top left.
  • This is because, the tick() function has stopped.
  • The tick() function only runs for alpha ranging from 1 to 0.
  • I didn't know the internals and this is why I had added manualTick() initially.

Next Steps:

  • One way to accommodate new nodes when alpha=0 is to restart the simulation.
  • i.e. Reset the value of alpha in the range [1,0] and simulation.restart().
  • Mere restarting the simulation without resetting alpha has no point because the tick() function will not run if alpha=0.
  • Resetting the alpha and restarting the simulation makes the tick() function run and we again get the right graph.
  • But when the graph has lot of nodes, as reported in Low framerate on visualization #232, this reset and restart will get expensive, as the graph tries to adhere to all the registered forces and as a result, the animations will constantly slow down the graph, thereby making it extremely difficult for interactivity.
  • This is the main motivation to add the web worker approach.

cc @biancadanforth

@princiya
Copy link
Collaborator Author

Debugging the ALPHA! :)

d3 force layout_1

@princiya princiya mentioned this pull request Nov 19, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants