Browse Source

metrics: Fix --templateMetricsHints

Also improve non-string comparisons.

Fixes #7048
pull/7052/head
Bjørn Erik Pedersen 5 years ago
parent
commit
5eadc4c0a8
  1. 42
      common/types/convert.go
  2. 42
      metrics/metrics.go
  3. 11
      metrics/metrics_test.go

42
common/types/convert.go

@ -13,7 +13,11 @@
package types package types
import "github.com/spf13/cast"
import (
"html/template"
"github.com/spf13/cast"
)
// ToStringSlicePreserveString converts v to a string slice. // ToStringSlicePreserveString converts v to a string slice.
// If v is a string, it will be wrapped in a string slice. // If v is a string, it will be wrapped in a string slice.
@ -26,3 +30,39 @@ func ToStringSlicePreserveString(v interface{}) []string {
} }
return cast.ToStringSlice(v) return cast.ToStringSlice(v)
} }
// TypeToString converts v to a string if it's a valid string type.
// Note that this will not try to convert numeric values etc.,
// use ToString for that.
func TypeToString(v interface{}) (string, bool) {
switch s := v.(type) {
case string:
return s, true
case template.HTML:
return string(s), true
case template.CSS:
return string(s), true
case template.HTMLAttr:
return string(s), true
case template.JS:
return string(s), true
case template.JSStr:
return string(s), true
case template.URL:
return string(s), true
case template.Srcset:
return string(s), true
}
return "", false
}
// ToString converts v to a string.
func ToString(v interface{}) string {
if s, ok := TypeToString(v); ok {
return s
}
return cast.ToString(v)
}

42
metrics/metrics.go

@ -15,6 +15,12 @@
package metrics package metrics
import ( import (
"reflect"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/common/types"
"fmt" "fmt"
"io" "io"
"math" "math"
@ -25,8 +31,6 @@ import (
"time" "time"
"github.com/gohugoio/hugo/compare" "github.com/gohugoio/hugo/compare"
"github.com/gohugoio/hugo/common/hreflect"
) )
// The Provider interface defines an interface for measuring metrics. // The Provider interface defines an interface for measuring metrics.
@ -51,15 +55,17 @@ type diff struct {
simSum int simSum int
} }
var counter = 0
func (d *diff) add(v interface{}) *diff { func (d *diff) add(v interface{}) *diff {
if !hreflect.IsTruthful(v) {
if types.IsNil(d.baseline) {
d.baseline = v d.baseline = v
d.count = 1 d.count = 1
d.simSum = 100 // If we get only one it is very cache friendly. d.simSum = 100 // If we get only one it is very cache friendly.
return d return d
} }
d.simSum += howSimilar(v, d.baseline)
adder := howSimilar(v, d.baseline)
d.simSum += adder
d.count++ d.count++
return d return d
@ -113,6 +119,7 @@ func (s *Store) TrackValue(key string, value interface{}) {
} }
d.add(value) d.add(value)
s.diffmu.Unlock() s.diffmu.Unlock()
} }
@ -135,6 +142,7 @@ func (s *Store) WriteMetrics(w io.Writer) {
var max time.Duration var max time.Duration
diff, found := s.diffs[k] diff, found := s.diffs[k]
cacheFactor := 0 cacheFactor := 0
if found { if found {
cacheFactor = int(math.Floor(float64(diff.simSum) / float64(diff.count))) cacheFactor = int(math.Floor(float64(diff.simSum) / float64(diff.count)))
@ -196,11 +204,19 @@ func (b bySum) Less(i, j int) bool { return b[i].sum > b[j].sum }
// howSimilar is a naive diff implementation that returns // howSimilar is a naive diff implementation that returns
// a number between 0-100 indicating how similar a and b are. // a number between 0-100 indicating how similar a and b are.
func howSimilar(a, b interface{}) int { func howSimilar(a, b interface{}) int {
// TODO(bep) object equality fast path, but remember that
// we can get anytning in here.
t1, t2 := reflect.TypeOf(a), reflect.TypeOf(b)
if t1 != t2 {
return 0
}
as, ok1 := a.(string)
bs, ok2 := b.(string)
if t1.Comparable() && t2.Comparable() {
if a == b {
return 100
}
}
as, ok1 := types.TypeToString(a)
bs, ok2 := types.TypeToString(b)
if ok1 && ok2 { if ok1 && ok2 {
return howSimilarStrings(as, bs) return howSimilarStrings(as, bs)
@ -222,13 +238,21 @@ func howSimilar(a, b interface{}) int {
return 90 return 90
} }
h1, h2 := helpers.HashString(a), helpers.HashString(b)
if h1 == h2 {
return 100
}
return 0 return 0
} }
// howSimilar is a naive diff implementation that returns // howSimilar is a naive diff implementation that returns
// a number between 0-100 indicating how similar a and b are. // a number between 0-100 indicating how similar a and b are.
// 100 is when all words in a also exists in b. // 100 is when all words in a also exists in b.
func howSimilarStrings(a, b string) int { func howSimilarStrings(a, b string) int {
if a == b {
return 100
}
// Give some weight to the word positions. // Give some weight to the word positions.
const partitionSize = 4 const partitionSize = 4

11
metrics/metrics_test.go

@ -14,6 +14,7 @@
package metrics package metrics
import ( import (
"html/template"
"strings" "strings"
"testing" "testing"
@ -39,13 +40,23 @@ func TestSimilarPercentage(t *testing.T) {
c.Assert(howSimilar("The Hugo", "The Hugo Rules"), qt.Equals, 66) c.Assert(howSimilar("The Hugo", "The Hugo Rules"), qt.Equals, 66)
c.Assert(howSimilar("Totally different", "Not Same"), qt.Equals, 0) c.Assert(howSimilar("Totally different", "Not Same"), qt.Equals, 0)
c.Assert(howSimilar(sentence, sentenceReversed), qt.Equals, 14) c.Assert(howSimilar(sentence, sentenceReversed), qt.Equals, 14)
c.Assert(howSimilar(template.HTML("Hugo Rules"), template.HTML("Hugo Rules")), qt.Equals, 100)
c.Assert(howSimilar(map[string]interface{}{"a": 32, "b": 33}, map[string]interface{}{"a": 32, "b": 33}), qt.Equals, 100)
c.Assert(howSimilar(map[string]interface{}{"a": 32, "b": 33}, map[string]interface{}{"a": 32, "b": 34}), qt.Equals, 0)
} }
type testStruct struct {
Name string
}
func TestSimilarPercentageNonString(t *testing.T) { func TestSimilarPercentageNonString(t *testing.T) {
c := qt.New(t) c := qt.New(t)
c.Assert(howSimilar(page.NopPage, page.NopPage), qt.Equals, 100) c.Assert(howSimilar(page.NopPage, page.NopPage), qt.Equals, 100)
c.Assert(howSimilar(page.Pages{}, page.Pages{}), qt.Equals, 90) c.Assert(howSimilar(page.Pages{}, page.Pages{}), qt.Equals, 90)
c.Assert(howSimilar(testStruct{Name: "A"}, testStruct{Name: "B"}), qt.Equals, 0)
c.Assert(howSimilar(testStruct{Name: "A"}, testStruct{Name: "A"}), qt.Equals, 100)
} }
func BenchmarkHowSimilar(b *testing.B) { func BenchmarkHowSimilar(b *testing.B) {

Loading…
Cancel
Save