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

blockchain: Use skip list for ancestor traversal. #2010

Merged
merged 2 commits into from
Nov 23, 2019

Conversation

davecgh
Copy link
Member

@davecgh davecgh commented Nov 22, 2019

There are several places when processing blocks and headers that require the ability to traverse the chain to ancestors of a given block such as tallying votes and versions to determine threshold states, finding forks, and calculating PoW difficulty, ticket price, and sequence locks.

Currently most traversal of this type is linear and therefore does not scale well as the chain height grows. In addition, the ability to quickly locate ancestors, potentially deep in history, will be needed when decoupling the connection code from the download logic as well as several planned future optimizations.

Consequently, this significantly optimizes ancestor traversal by introducing a deterministic skip list and adds tests to ensure proper functionality.

The following is a before and after comparison of ancestor traversal for a large number of nodes:

benchmark           old ns/op     new ns/op   delta
------------------------------------------------------
BenchmarkAncestor   67901581      158         -100.00%

benchmark           old allocs   new allocs   delta
------------------------------------------------------
BenchmarkAncestor   0            0            +0.00%

benchmark           old bytes    new bytes    delta
------------------------------------------------------
BenchmarkAncestor   0            0            +0.00%

The following is a scatter plot of the number of steps to traverse back to a height of 1 for all heights up to 2^14 along with a logarithmic regression to help visualize the behavior.

steps_vs_height

@davecgh davecgh added this to the 1.6.0 milestone Nov 22, 2019
@davecgh davecgh force-pushed the blockchain_ancestor_skiplist branch 2 times, most recently from f3215fe to fbdd0d4 Compare November 22, 2019 05:51
Copy link
Member

@matheusd matheusd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice :)

I wanted to get some intuition from how the skip list was actually behaving, so I came up with this visualization:

clob = lambda x: x & (x-1)                # Clear Lowest One Bit
slh = lambda x: clob(clob(x))             # Skip List Height
fmt = lambda x: print(format(x, '#034b')) # pretty print in binary

def ancestor(orig, target):
  x = orig
  fmt(x)
  c = 0
  while x != target:
    if slh(x) >= target:
      x = slh(x)
    else:
      x = x -1
    c += 1
    fmt(x)
  print("Count: %d" % c)

ancestor(0xffffffff, 1)

Here's an online jupyter notebook (hopefully it doesn't go away too soon): https://notebooks.gesis.org/binder/jupyter/user/ipython-ipython-in-depth-j8yn9m22/notebooks/binder/skip%20list.ipynb

There are several places when processing blocks and headers that require
the ability to traverse the chain to ancestors of a given block such as
tallying votes and versions to determine threshold states, finding
forks, and calculating PoW difficulty, ticket price, and sequence locks.

Currently most traversal of this type is linear and therefore does not
scale well as the chain height grows.  In addition, the ability to
quickly locate ancestors, potentially deep in history, will be needed
when decoupling the connection code from the download logic as well as
several planned future optimizations.

Consequently, this significantly optimizes ancestor traversal by
introducing a deterministic skip list and adds tests to ensure proper
functionality.

The following is a before and after comparison of ancestor traversal for
a large number of nodes:

benchmark           old ns/op     new ns/op   delta
------------------------------------------------------
BenchmarkAncestor   67901581      158         -100.00%

benchmark           old allocs   new allocs   delta
------------------------------------------------------
BenchmarkAncestor   0            0            +0.00%

benchmark           old bytes    new bytes    delta
------------------------------------------------------
BenchmarkAncestor   0            0            +0.00%
@davecgh
Copy link
Member Author

davecgh commented Nov 23, 2019

I updated the PR description to include a scatter plot of the number of steps to traverse back to a height of 1 for all heights up to 2^14 along with a logarithmic regression to help visualize the behavior.

@davecgh davecgh merged commit b50cadc into decred:master Nov 23, 2019
@davecgh davecgh deleted the blockchain_ancestor_skiplist branch November 23, 2019 10:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants