diff --git a/system/utils.go b/system/utils.go index b6a2894..5dcf687 100644 --- a/system/utils.go +++ b/system/utils.go @@ -46,7 +46,14 @@ func MustInt(v string) int { // over the websocket. If a line exceeds that size, it is truncated and only that // amount is sent over. func ScanReader(r io.Reader, callback func(line []byte)) error { - br := bufio.NewReader(r) + // Based on benchmarking this seems to be the best size for the reader buffer + // to maintain fast enough workflows without hammering the CPU for allocations. + // + // Additionally, most games are outputting a high-frequency of smaller lines, + // rather than a bunch of massive lines. This allocation amount is the total + // number of bytes being output for each call to ReadLine() before it moves on + // to the next data pull. + br := bufio.NewReaderSize(r, 256) // Avoid constantly re-allocating memory when we're flooding lines through this // function by using the same buffer for the duration of the call and just truncating // the value back to 0 every loop. diff --git a/system/utils_test.go b/system/utils_test.go index b05019f..1e4f9aa 100644 --- a/system/utils_test.go +++ b/system/utils_test.go @@ -1,13 +1,15 @@ package system import ( + "math/rand" "strings" "testing" + "time" . "github.com/franela/goblin" ) -func TestScanReader(t *testing.T) { +func Test_Utils(t *testing.T) { g := Goblin(t) g.Describe("ScanReader", func() { @@ -39,3 +41,19 @@ func TestScanReader(t *testing.T) { }) }) } + +func Benchmark_ScanReader(b *testing.B) { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + var str string + for i := 0; i < 10; i++ { + str += strings.Repeat("hello \rworld", r.Intn(2000)) + "\n" + } + reader := strings.NewReader(str) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = ScanReader(reader, func(line []byte) { + // no op + }) + } +}