-
-
Notifications
You must be signed in to change notification settings - Fork 147
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
Improve performance for creating crdt.TreeNode #930
Comments
I tried implementing it in the following way. func FromTreeNodes(pbNodes []*api.TreeNode) (*crdt.TreeNode, error) {
if len(pbNodes) == 0 {
return nil, nil
}
nodes := make([]*crdt.TreeNode, len(pbNodes))
for i, pbNode := range pbNodes {
node, err := fromTreeNode(pbNode)
if err != nil {
return nil, err
}
nodes[i] = node
}
root := nodes[len(nodes)-1]
depthTable := make(map[int32]*crdt.TreeNode)
depthTable[pbNodes[len(nodes)-1].Depth] = nodes[len(nodes)-1]
for i := len(nodes) - 2; i >= 0; i-- {
var parent *crdt.TreeNode = depthTable[pbNodes[i].Depth-1]
if err := parent.Prepend(nodes[i]); err != nil {
return nil, err
}
depthTable[pbNodes[i].Depth] = nodes[i]
}
// build crdt.Tree from root to construct the links between nodes.
return crdt.NewTree(root, nil).Root(), nil
} When benchmarking against trees of the form Here's an explanation of how it works:
|
I tested raararaara's code by simply creating 10,000 child vertices Test Codefunc TestCreateNode(t *testing.T) {
var chd []json.TreeNode
for i := 0; i < 10000; i++ {
chd = append(chd, json.TreeNode{Type: "p", Children: []json.TreeNode{{Type: "text", Value: "a"}}})
}
root := helper.BuildTreeNode(&json.TreeNode{
Type: "r",
Children: chd,
})
pbNodes := converter.ToTreeNodes(root)
for i := 1; i <= 10; i++ {
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
converter.FromTreeNodes(pbNodes)
})
}
} Current O(n^2) code result
rararara's code result
The time complexity is optimized from O(n^2) to O(n). func FromTreeNodes(pbNodes []*api.TreeNode) (*crdt.TreeNode, error) {
if len(pbNodes) == 0 {
return nil, nil
}
nodes := make([]*crdt.TreeNode, len(pbNodes))
for i, pbNode := range pbNodes {
node, err := fromTreeNode(pbNode)
if err != nil {
return nil, err
}
nodes[i] = node
}
root := nodes[len(nodes)-1]
depthTable := make(map[int32]*crdt.TreeNode)
depthTable[pbNodes[len(nodes)-1].Depth] = nodes[len(nodes)-1]
for i := len(nodes) - 2; i >= 0; i-- {
var parent *crdt.TreeNode = depthTable[pbNodes[i].Depth-1]
if err := parent.Prepend(nodes[i]); err != nil {
return nil, err
}
depthTable[pbNodes[i].Depth] = nodes[i]
}
root.Index.UpdateDescendantsSize()
// build crdt.Tree from root to construct the links between nodes.
return crdt.NewTree(root, nil).Root(), nil
} Can I pr this code? |
With the introduction of depthTable, we get fromTreeNodes with O(N) time complexity. It would be good if it applied on the JS SDK as well. |
Description:
Currently, the time complexity of creating
crdt.TreeNode
isO(N^2)
. While this may not be a significant issue currently, there is a risk that as the number of tree nodes in the protobuf increases, operations will scale quadratically, potentially causing performance bottlenecks.It would be beneficial to optimize the time complexity to prevent potential future performance issues.
yorkie/api/converter/from_pb.go
Lines 571 to 604 in b468f8b
Why:
To prevent performance issues that could happen in the future.
The text was updated successfully, but these errors were encountered: