Skip to content

Commit

Permalink
fix(fielder): eliminate expensive inner calls (#33)
Browse files Browse the repository at this point in the history
## Which problem is this PR solving?

- Fielder is much more expensive than it should be; this only turned up
from profiling a real output, not a dummy output, since Fielder does not
get invoked except on real output.

![image
(25)](https://github.com/honeycombio/loadgen/assets/614704/ec3e7877-821e-45fa-98fb-89635a6fee28)


## Short description of the changes

- Move regexp.MustCompile out of the hot path
- Eliminate getGoroutineId since it requires gathering a full traceback
which is very slow.

---------

Co-authored-by: Kent Quirk <kentquirk@honeycomb.io>
  • Loading branch information
lizthegrey and kentquirk authored Jan 9, 2024
1 parent 9f44a3e commit 5cf1940
Showing 1 changed file with 5 additions and 33 deletions.
38 changes: 5 additions & 33 deletions fielder.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"math/rand"
"os"
"regexp"
"runtime"
"strconv"
"strings"

Expand Down Expand Up @@ -52,6 +51,10 @@ var nouns = []string{
"watch", "wheel", "whip", "whistle", "window", "wing", "wire", "worm",
}

var constpat = regexp.MustCompile(`^([a-zA-Z0-9_]+)=([^/].*)$`)
var genpat = regexp.MustCompile(`^((?:[0-9]+\.)?[a-zA-Z0-9_]+)=/([ibfsu][awxrgqt]?)([0-9.-]+)?(,[0-9.-]+)?$`)
var keypat = regexp.MustCompile(`^([0-9]+)\.(.*$)`)

type Rng struct {
rng *rand.Rand
}
Expand Down Expand Up @@ -122,33 +125,6 @@ func (r Rng) BoolWithProb(p int) bool {
return r.Int(0, 100) < int64(p)
}

func getGoroutineID() uint64 {
var buffer [31]byte
written := runtime.Stack(buffer[:], false)
index := 10
negative := buffer[index] == '-'
if negative {
index = 11
}
id := uint64(0)
for index < written {
byte := buffer[index]
if byte == ' ' {
break
}
if byte < '0' || byte > '9' {
panic("could not get goroutine ID")
}
id *= 10
id += uint64(byte - '0')
index++
}
if negative {
id = -id
}
return id
}

// getProcessID returns the process ID
func getProcessID() int64 {
return int64(os.Getpid())
Expand Down Expand Up @@ -191,8 +167,6 @@ func getWordList(rng Rng, cardinality int, source []string) []string {
// parseUserFields expects a list of fields in the form of name=constant or name=/gen.
// See README.md for more information.
func parseUserFields(rng Rng, userfields []string) (map[string]func() any, error) {
constpat := regexp.MustCompile(`^([a-zA-Z0-9_]+)=([^/].*)$`)
genpat := regexp.MustCompile(`^((?:[0-9]+\.)?[a-zA-Z0-9_]+)=/([ibfsu][awxrgqt]?)([0-9.-]+)?(,[0-9.-]+)?$`)
// groups 1 2 3 4
fields := make(map[string]func() any)
for _, field := range userfields {
Expand Down Expand Up @@ -436,7 +410,7 @@ type Fielder struct {
// service names to generate. The field names are randomly generated by
// combining an adjective and a noun and are consistent for a given fielder.
// The field values are randomly generated.
// Fielder also includes two special fields: goroutine_id and process_id.
// Fielder also includes the process_id.
func NewFielder(seed string, userFields []string, nextras, nservices int) (*Fielder, error) {
rng := NewRng(seed)
gens := rng.getValueGenerators()
Expand All @@ -448,7 +422,6 @@ func NewFielder(seed string, userFields []string, nextras, nservices int) (*Fiel
fieldname := rng.WordPair()
fields[fieldname] = gens[rng.Intn(len(gens))]
}
fields["goroutine_id"] = func() any { return getGoroutineID() }
fields["process_id"] = func() any { return getProcessID() }

names := make([]string, nservices)
Expand All @@ -467,7 +440,6 @@ func (f *Fielder) GetServiceName(n int) string {
// indicate that the field should be included at a specific
// level in the trace, where 0 is the root.
func (f *Fielder) atLevel(name string, level int) (string, bool) {
keypat := regexp.MustCompile(`^([0-9]+)\.(.*$)`)
matches := keypat.FindStringSubmatch(name)
if len(matches) == 0 {
return name, true
Expand Down

0 comments on commit 5cf1940

Please sign in to comment.