Skip to content

Commit a9ad813

Browse files
authored
Improve diff performance (#798)
Use a map of strings so we can do O(1) lookups. Now O(n) instead of O(n^2).
1 parent 521a832 commit a9ad813

File tree

1 file changed

+52
-30
lines changed

1 file changed

+52
-30
lines changed

ygot/diff.go

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,30 @@ func getPathSpec(ni *util.NodeInfo) (*pathSpec, error) {
198198
return nil, fmt.Errorf("could not find path specification annotation")
199199
}
200200

201+
// pathInfo contains the path-value information for a single gNMI leaf.
202+
type pathInfo struct {
203+
val interface{}
204+
path *gnmipb.Path
205+
}
206+
207+
// toStringPathMap converts the pathSpec-value map to a simple path-value map.
208+
func toStringPathMap(pathMap map[*pathSpec]interface{}) (map[string]*pathInfo, error) {
209+
strPathMap := map[string]*pathInfo{}
210+
for paths, val := range pathMap {
211+
for _, path := range paths.gNMIPaths {
212+
strPath, err := PathToString(path)
213+
if err != nil {
214+
return nil, err
215+
}
216+
strPathMap[strPath] = &pathInfo{
217+
val: val,
218+
path: path,
219+
}
220+
}
221+
}
222+
return strPathMap, nil
223+
}
224+
201225
// findSetLeaves iteratively walks the fields of the supplied GoStruct, s, and
202226
// returns a map, keyed by the path of the leaves that are set, with a the value
203227
// that the leaf is set to. YANG lists (Go maps), and containers (Go structs) are
@@ -336,18 +360,16 @@ func leastSpecificPath(paths [][]string) []string {
336360
}
337361

338362
// appendUpdate adds an update to the supplied gNMI Notification message corresponding
339-
// to the path and value supplied.
340-
func appendUpdate(n *gnmipb.Notification, path *pathSpec, val interface{}) error {
341-
v, err := EncodeTypedValue(val, gnmipb.Encoding_PROTO)
363+
// to the path and value supplied. path is the string version of the path in pathInfo.
364+
func appendUpdate(n *gnmipb.Notification, path string, pathInfo *pathInfo) error {
365+
v, err := EncodeTypedValue(pathInfo.val, gnmipb.Encoding_PROTO)
342366
if err != nil {
343-
return fmt.Errorf("cannot represent field value %v as TypedValue for path %v: %v", val, path, err)
344-
}
345-
for _, p := range path.gNMIPaths {
346-
n.Update = append(n.Update, &gnmipb.Update{
347-
Path: p,
348-
Val: v,
349-
})
367+
return fmt.Errorf("cannot represent field value %v as TypedValue for path %v: %v", pathInfo.val, path, err)
350368
}
369+
n.Update = append(n.Update, &gnmipb.Update{
370+
Path: pathInfo.path,
371+
Val: v,
372+
})
351373
return nil
352374
}
353375

@@ -440,38 +462,38 @@ func Diff(original, modified GoStruct, opts ...DiffOpt) (*gnmipb.Notification, e
440462
return nil, fmt.Errorf("could not extract set leaves from modified struct: %v", err)
441463
}
442464

443-
matched := map[*pathSpec]bool{}
465+
origLeavesStr, err := toStringPathMap(origLeaves)
466+
if err != nil {
467+
return nil, fmt.Errorf("could not convert leaf path map to string path map: %v", err)
468+
}
469+
modLeavesStr, err := toStringPathMap(modLeaves)
470+
if err != nil {
471+
return nil, fmt.Errorf("could not convert leaf path map to string path map: %v", err)
472+
}
473+
444474
n := &gnmipb.Notification{}
445-
for origPath, origVal := range origLeaves {
446-
var origMatched bool
447-
for modPath, modVal := range modLeaves {
448-
if origPath.Equal(modPath) {
449-
// This path is set in both of the structs, so check whether the value
450-
// is equal.
451-
matched[modPath] = true
452-
origMatched = true
453-
if !reflect.DeepEqual(origVal, modVal) {
454-
// The contents of the value should indicate that value a has changed
455-
// to value b.
456-
if err := appendUpdate(n, origPath, modVal); err != nil {
457-
return nil, err
458-
}
475+
for origPath, origVal := range origLeavesStr {
476+
if modVal, ok := modLeavesStr[origPath]; ok {
477+
if !reflect.DeepEqual(origVal.val, modVal.val) {
478+
// The contents of the value should indicate that value a has changed
479+
// to value b.
480+
if err := appendUpdate(n, origPath, modVal); err != nil {
481+
return nil, err
459482
}
460483
}
461-
}
462-
if !origMatched {
484+
} else if !ok {
463485
// This leaf was set in the original struct, but not in the modified
464486
// struct, therefore it has been deleted.
465-
n.Delete = append(n.Delete, origPath.gNMIPaths...)
487+
n.Delete = append(n.Delete, origVal.path)
466488
}
467489
}
468490
if hasIgnoreAdditions(opts) != nil {
469491
return n, nil
470492
}
471493
// Check that all paths that are in the modified struct have been examined, if
472494
// not they are updates.
473-
for modPath, modVal := range modLeaves {
474-
if !matched[modPath] {
495+
for modPath, modVal := range modLeavesStr {
496+
if _, ok := origLeavesStr[modPath]; !ok {
475497
if err := appendUpdate(n, modPath, modVal); err != nil {
476498
return nil, err
477499
}

0 commit comments

Comments
 (0)