mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
working on caching transferelems
This commit is contained in:
parent
fb641ac717
commit
f8b8e757fc
@ -531,87 +531,6 @@ func (r *RootElem) MakeVDom() *VDomElem {
|
|||||||
return r.makeVDom(r.Root)
|
return r.makeVDom(r.Root)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConvertElemsToTransferElems(elems []VDomElem) []VDomTransferElem {
|
|
||||||
var transferElems []VDomTransferElem
|
|
||||||
textCounter := 0 // Counter for generating unique IDs for #text nodes
|
|
||||||
|
|
||||||
// Helper function to recursively process each VDomElem in preorder
|
|
||||||
var processElem func(elem VDomElem) string
|
|
||||||
processElem = func(elem VDomElem) string {
|
|
||||||
// Handle #text nodes by generating a unique placeholder ID
|
|
||||||
if elem.Tag == "#text" {
|
|
||||||
textId := fmt.Sprintf("text-%d", textCounter)
|
|
||||||
textCounter++
|
|
||||||
transferElems = append(transferElems, VDomTransferElem{
|
|
||||||
WaveId: textId,
|
|
||||||
Tag: elem.Tag,
|
|
||||||
Text: elem.Text,
|
|
||||||
Props: nil,
|
|
||||||
Children: nil,
|
|
||||||
})
|
|
||||||
return textId
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert children to WaveId references, handling potential #text nodes
|
|
||||||
childrenIds := make([]string, len(elem.Children))
|
|
||||||
for i, child := range elem.Children {
|
|
||||||
childrenIds[i] = processElem(child) // Children are not roots
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the VDomTransferElem for the current element
|
|
||||||
transferElem := VDomTransferElem{
|
|
||||||
WaveId: elem.WaveId,
|
|
||||||
Tag: elem.Tag,
|
|
||||||
Props: elem.Props,
|
|
||||||
Children: childrenIds,
|
|
||||||
Text: elem.Text,
|
|
||||||
}
|
|
||||||
transferElems = append(transferElems, transferElem)
|
|
||||||
|
|
||||||
return elem.WaveId
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start processing each top-level element, marking them as roots
|
|
||||||
for _, elem := range elems {
|
|
||||||
processElem(elem)
|
|
||||||
}
|
|
||||||
|
|
||||||
return transferElems
|
|
||||||
}
|
|
||||||
|
|
||||||
func DedupTransferElems(elems []VDomTransferElem) []VDomTransferElem {
|
|
||||||
seen := make(map[string]int) // maps WaveId to its index in the result slice
|
|
||||||
var result []VDomTransferElem
|
|
||||||
|
|
||||||
for _, elem := range elems {
|
|
||||||
if idx, exists := seen[elem.WaveId]; exists {
|
|
||||||
// Overwrite the previous element with the latest one
|
|
||||||
result[idx] = elem
|
|
||||||
} else {
|
|
||||||
// Add new element and store its index
|
|
||||||
seen[elem.WaveId] = len(result)
|
|
||||||
result = append(result, elem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (beUpdate *VDomBackendUpdate) CreateTransferElems() {
|
|
||||||
var vdomElems []VDomElem
|
|
||||||
for idx, reUpdate := range beUpdate.RenderUpdates {
|
|
||||||
if reUpdate.VDom == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
vdomElems = append(vdomElems, *reUpdate.VDom)
|
|
||||||
beUpdate.RenderUpdates[idx].VDomWaveId = reUpdate.VDom.WaveId
|
|
||||||
beUpdate.RenderUpdates[idx].VDom = nil
|
|
||||||
}
|
|
||||||
transferElems := ConvertElemsToTransferElems(vdomElems)
|
|
||||||
transferElems = DedupTransferElems(transferElems)
|
|
||||||
beUpdate.TransferElems = transferElems
|
|
||||||
}
|
|
||||||
|
|
||||||
// SplitBackendUpdate splits a large VDomBackendUpdate into multiple smaller updates
|
// SplitBackendUpdate splits a large VDomBackendUpdate into multiple smaller updates
|
||||||
// The first update contains all the core fields, while subsequent updates only contain
|
// The first update contains all the core fields, while subsequent updates only contain
|
||||||
// array elements that need to be appended
|
// array elements that need to be appended
|
||||||
|
@ -55,6 +55,9 @@ type Client struct {
|
|||||||
OverrideUrlHandler http.Handler
|
OverrideUrlHandler http.Handler
|
||||||
NewBlockFlag bool
|
NewBlockFlag bool
|
||||||
SetupFn func()
|
SetupFn func()
|
||||||
|
TransferElemCache map[string][]byte
|
||||||
|
TextNodeCache map[string]int
|
||||||
|
TextNodeNextId int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetIsDone() bool {
|
func (c *Client) GetIsDone() bool {
|
||||||
@ -90,11 +93,14 @@ func MakeClient(appOpts AppOpts) *Client {
|
|||||||
appOpts.NewBlockFlag = "n"
|
appOpts.NewBlockFlag = "n"
|
||||||
}
|
}
|
||||||
client := &Client{
|
client := &Client{
|
||||||
Lock: &sync.Mutex{},
|
Lock: &sync.Mutex{},
|
||||||
AppOpts: appOpts,
|
AppOpts: appOpts,
|
||||||
Root: vdom.MakeRoot(),
|
Root: vdom.MakeRoot(),
|
||||||
DoneCh: make(chan struct{}),
|
DoneCh: make(chan struct{}),
|
||||||
UrlHandlerMux: mux.NewRouter(),
|
UrlHandlerMux: mux.NewRouter(),
|
||||||
|
TransferElemCache: make(map[string][]byte),
|
||||||
|
TextNodeCache: make(map[string]int),
|
||||||
|
TextNodeNextId: 1,
|
||||||
Opts: vdom.VDomBackendOpts{
|
Opts: vdom.VDomBackendOpts{
|
||||||
CloseOnCtrlC: appOpts.CloseOnCtrlC,
|
CloseOnCtrlC: appOpts.CloseOnCtrlC,
|
||||||
GlobalKeyboardEvents: appOpts.GlobalKeyboardEvents,
|
GlobalKeyboardEvents: appOpts.GlobalKeyboardEvents,
|
||||||
|
@ -75,7 +75,7 @@ func (impl *VDomServerImpl) VDomRenderCommand(ctx context.Context, feUpdate vdom
|
|||||||
} else {
|
} else {
|
||||||
update, err = impl.Client.incrementalRender()
|
update, err = impl.Client.incrementalRender()
|
||||||
}
|
}
|
||||||
update.CreateTransferElems()
|
impl.Client.CreateTransferElems(update)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respChan <- wshrpc.RespOrErrorUnion[*vdom.VDomBackendUpdate]{
|
respChan <- wshrpc.RespOrErrorUnion[*vdom.VDomBackendUpdate]{
|
||||||
|
139
pkg/vdom/vdomclient/vdomtransfer.go
Normal file
139
pkg/vdom/vdomclient/vdomtransfer.go
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
// Copyright 2024, Command Line Inc.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package vdomclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/wavetermdev/waveterm/pkg/vdom"
|
||||||
|
)
|
||||||
|
|
||||||
|
func transferElemsEqual(t1 *vdom.VDomTransferElem, t2 *vdom.VDomTransferElem) bool {
|
||||||
|
if t1 == nil || t2 == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if t1.WaveId != t2.WaveId || t1.Tag != t2.Tag || t1.Text != t2.Text {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(t1.Children) != len(t2.Children) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := range t1.Children {
|
||||||
|
if t1.Children[i] != t2.Children[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) ConvertElemsToTransferElems(elems []vdom.VDomElem) []vdom.VDomTransferElem {
|
||||||
|
var transferElems []vdom.VDomTransferElem
|
||||||
|
var textCacheHits int
|
||||||
|
var teCacheHits int
|
||||||
|
var numTextNodes int
|
||||||
|
|
||||||
|
// Helper function to recursively process each VDomElem in preorder
|
||||||
|
var processElem func(elem vdom.VDomElem) string
|
||||||
|
processElem = func(elem vdom.VDomElem) string {
|
||||||
|
// Handle #text nodes by generating a unique placeholder ID
|
||||||
|
if elem.Tag == "#text" {
|
||||||
|
textId := c.TextNodeCache[elem.Text]
|
||||||
|
if textId == 0 {
|
||||||
|
textId = c.TextNodeNextId
|
||||||
|
c.TextNodeNextId++
|
||||||
|
c.TextNodeCache[elem.Text] = textId
|
||||||
|
} else {
|
||||||
|
textCacheHits++
|
||||||
|
}
|
||||||
|
textIdStr := fmt.Sprintf("text-%d", textId)
|
||||||
|
transferElems = append(transferElems, vdom.VDomTransferElem{
|
||||||
|
WaveId: textIdStr,
|
||||||
|
Tag: elem.Tag,
|
||||||
|
Text: elem.Text,
|
||||||
|
Props: nil,
|
||||||
|
Children: nil,
|
||||||
|
})
|
||||||
|
return textIdStr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert children to WaveId references, handling potential #text nodes
|
||||||
|
childrenIds := make([]string, len(elem.Children))
|
||||||
|
for i, child := range elem.Children {
|
||||||
|
childrenIds[i] = processElem(child) // Children are not roots
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the VDomTransferElem for the current element
|
||||||
|
transferElem := vdom.VDomTransferElem{
|
||||||
|
WaveId: elem.WaveId,
|
||||||
|
Tag: elem.Tag,
|
||||||
|
Props: elem.Props,
|
||||||
|
Children: childrenIds,
|
||||||
|
Text: elem.Text,
|
||||||
|
}
|
||||||
|
transferElems = append(transferElems, transferElem)
|
||||||
|
|
||||||
|
return elem.WaveId
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start processing each top-level element, marking them as roots
|
||||||
|
for _, elem := range elems {
|
||||||
|
processElem(elem)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, te := range transferElems {
|
||||||
|
if te.Tag == "#text" {
|
||||||
|
numTextNodes++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if te.WaveId == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
curTe := c.TransferElemCache[te.WaveId]
|
||||||
|
teBytes, _ := json.Marshal(te)
|
||||||
|
if bytes.Equal(curTe, teBytes) {
|
||||||
|
teCacheHits++
|
||||||
|
} else {
|
||||||
|
c.TransferElemCache[te.WaveId] = teBytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Converted, transferelems: %d/%d, textcache: %d/%d\n", teCacheHits, len(transferElems)-numTextNodes, textCacheHits, numTextNodes)
|
||||||
|
return transferElems
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) DedupTransferElems(elems []vdom.VDomTransferElem) []vdom.VDomTransferElem {
|
||||||
|
seen := make(map[string]int) // maps WaveId to its index in the result slice
|
||||||
|
var result []vdom.VDomTransferElem
|
||||||
|
|
||||||
|
for _, elem := range elems {
|
||||||
|
if idx, exists := seen[elem.WaveId]; exists {
|
||||||
|
// Overwrite the previous element with the latest one
|
||||||
|
result[idx] = elem
|
||||||
|
} else {
|
||||||
|
// Add new element and store its index
|
||||||
|
seen[elem.WaveId] = len(result)
|
||||||
|
result = append(result, elem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) CreateTransferElems(beUpdate *vdom.VDomBackendUpdate) {
|
||||||
|
var vdomElems []vdom.VDomElem
|
||||||
|
for idx, reUpdate := range beUpdate.RenderUpdates {
|
||||||
|
if reUpdate.VDom == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
vdomElems = append(vdomElems, *reUpdate.VDom)
|
||||||
|
beUpdate.RenderUpdates[idx].VDomWaveId = reUpdate.VDom.WaveId
|
||||||
|
beUpdate.RenderUpdates[idx].VDom = nil
|
||||||
|
}
|
||||||
|
transferElems := c.ConvertElemsToTransferElems(vdomElems)
|
||||||
|
transferElems = c.DedupTransferElems(transferElems)
|
||||||
|
beUpdate.TransferElems = transferElems
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user