Skip to content

Commit 6991d24

Browse files
committed
Optimize splice
The old code was a cute one-liner, but it allocated needlessly, quite a lot. Replace it with slightly more careful code that only allocates and copies as necessary. name old time/op new time/op delta DiffCommonPrefix-8 135ns ± 2% 133ns ± 1% ~ (p=0.213 n=10+9) DiffCommonSuffix-8 142ns ± 3% 141ns ± 2% ~ (p=0.173 n=10+9) DiffHalfMatch-8 107µs ± 0% 107µs ± 0% ~ (p=0.400 n=9+9) DiffCleanupSemantic-8 11.4ms ± 1% 0.9ms ± 1% -91.72% (p=0.000 n=10+9) DiffMain-8 1.01s ± 0% 1.01s ± 0% ~ (p=0.780 n=10+9) DiffMainLarge-8 134ms ± 1% 101ms ± 4% -24.45% (p=0.000 n=9+9) DiffMainRunesLargeLines-8 707µs ± 0% 681µs ± 2% -3.61% (p=0.000 n=9+10) name old alloc/op new alloc/op delta DiffCommonPrefix-8 0.00B 0.00B ~ (all equal) DiffCommonSuffix-8 0.00B 0.00B ~ (all equal) DiffHalfMatch-8 106kB ± 0% 106kB ± 0% ~ (all equal) DiffCleanupSemantic-8 17.7MB ± 0% 0.3MB ± 0% -98.56% (p=0.000 n=9+9) DiffMain-8 16.4MB ± 0% 16.4MB ± 0% -0.00% (p=0.000 n=10+10) DiffMainLarge-8 63.8MB ± 0% 4.8MB ± 0% -92.42% (p=0.000 n=9+10) DiffMainRunesLargeLines-8 209kB ± 0% 175kB ± 0% -16.54% (p=0.000 n=10+10) name old allocs/op new allocs/op delta DiffCommonPrefix-8 0.00 0.00 ~ (all equal) DiffCommonSuffix-8 0.00 0.00 ~ (all equal) DiffHalfMatch-8 2.00 ± 0% 2.00 ± 0% ~ (all equal) DiffCleanupSemantic-8 11.4k ± 0% 3.1k ± 0% -72.46% (p=0.000 n=10+10) DiffMain-8 89.0 ± 0% 83.0 ± 0% -6.74% (p=0.000 n=10+10) DiffMainLarge-8 55.3k ± 0% 46.5k ± 0% -15.94% (p=0.000 n=10+10) DiffMainRunesLargeLines-8 1.19k ± 0% 1.09k ± 0% -8.60% (p=0.000 n=10+8)
1 parent a16f115 commit 6991d24

File tree

1 file changed

+34
-1
lines changed

1 file changed

+34
-1
lines changed

diffmatchpatch/diff.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,41 @@ type Diff struct {
4040
Text string
4141
}
4242

43+
// splice removes amount elements from slice at index index, replacing them with elements.
4344
func splice(slice []Diff, index int, amount int, elements ...Diff) []Diff {
44-
return append(slice[:index], append(elements, slice[index+amount:]...)...)
45+
if len(elements) == amount {
46+
// Easy case: overwrite the relevant items.
47+
copy(slice[index:], elements)
48+
return slice
49+
}
50+
if len(elements) < amount {
51+
// Fewer new items than old.
52+
// Copy in the new items.
53+
copy(slice[index:], elements)
54+
// Shift the remaining items left.
55+
copy(slice[index+len(elements):], slice[index+amount:])
56+
// Calculate the new end of the slice.
57+
end := len(slice) - amount + len(elements)
58+
// Zero stranded elements at end so that they can be garbage collected.
59+
tail := slice[end:]
60+
for i := range tail {
61+
tail[i] = Diff{}
62+
}
63+
return slice[:end]
64+
}
65+
// More new items than old.
66+
// Make room in slice for new elements.
67+
// There's probably an even more efficient way to do this,
68+
// but this is simple and clear.
69+
need := len(slice) - amount + len(elements)
70+
for len(slice) < need {
71+
slice = append(slice, Diff{})
72+
}
73+
// Shift slice elements right to make room for new elements.
74+
copy(slice[index+len(elements):], slice[index+amount:])
75+
// Copy in new elements.
76+
copy(slice[index:], elements)
77+
return slice
4578
}
4679

4780
// DiffMain finds the differences between two texts.

0 commit comments

Comments
 (0)