Skip to content

Commit ecc1d45

Browse files
committed
Update benchmarks
- Add concurrent benchmark - Cleaned up - Add results to README
1 parent 9500fec commit ecc1d45

File tree

2 files changed

+76
-47
lines changed

2 files changed

+76
-47
lines changed

README.md

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,41 @@ This doesn't look too interesting on it's own but that can be helped by quickly
9191
* Access to all net-messages
9292
* Chat & console messages <sup id="achat1">1</sup>
9393
* [Easy debugging](#debugging) via build-flags
94-
* Built with concurrency in mind
94+
* Built with performance & concurrency in mind
9595

9696
1. <small id="f1">Only for some demos; in MM demos the chat is encrypted for example.</small>
9797

98+
## Performance / Benchmarks
99+
100+
One of the top priorities of this parser is performance and concurrency.
101+
102+
Here are some benchmark results from a system with a Intel i7 2600k CPU and SSD disk running Windows 10 and a demo with 85'000 frames.
103+
104+
### Overview
105+
106+
|Benchmark|Description|Average Duration|Speed|
107+
|-|-|-|-|
108+
|`BenchmarkConcurrent`|Read and parse 8 demos concurrently|2.90 s (per 8 demos)|~234'000 ticks / s|
109+
|`BenchmarkDemoInfoCs`|Read demo from drive and parse|1.39 s|~61'000 ticks / s
110+
|`BenchmarkInMemory`|Read demo from memory and parse|1.38 s|~61'000 ticks / s
111+
112+
### Raw output
113+
114+
```
115+
$ go test -run _NONE_ -bench . -benchtime 30s -benchmem -concurrentdemos 8
116+
goos: windows
117+
goarch: amd64
118+
pkg: github.com/markus-wa/demoinfocs-golang
119+
BenchmarkDemoInfoCs-8 30 1397398190 ns/op 162254528 B/op 839779 allocs/op
120+
BenchmarkInMemory-8 30 1384877250 ns/op 162109924 B/op 839628 allocs/op
121+
BenchmarkConcurrent-8 20 2902574295 ns/op 1297042534 B/op 6717163 allocs/op
122+
--- BENCH: BenchmarkConcurrent-8
123+
demoinfocs_test.go:425: Running concurrency benchmark with 8 demos
124+
demoinfocs_test.go:425: Running concurrency benchmark with 8 demos
125+
PASS
126+
ok github.com/markus-wa/demoinfocs-golang 147.800s
127+
```
128+
98129
## Versioning
99130

100131
We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/markus-wa/demoinfocs-golang/tags).

demoinfocs_test.go

Lines changed: 44 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package demoinfocs_test
33
import (
44
"bytes"
55
"crypto/rand"
6+
"flag"
67
"fmt"
78
"io/ioutil"
89
"os"
@@ -28,7 +29,12 @@ const defaultDemPath = csDemosPath + "/default.dem"
2829
const unexpectedEndOfDemoPath = csDemosPath + "/unexpected_end_of_demo.dem"
2930
const valveMatchmakingDemoPath = csDemosPath + "/valve_matchmaking.dem"
3031

32+
var concurrentDemos int
33+
3134
func init() {
35+
flag.IntVar(&concurrentDemos, "concurrentdemos", 2, "The `number` of current demos")
36+
flag.Parse()
37+
3238
if _, err := os.Stat(defaultDemPath); err != nil {
3339
panic(fmt.Sprintf("Failed to read test demo %q", defaultDemPath))
3440
}
@@ -287,36 +293,46 @@ func TestHeaderNotParsed(t *testing.T) {
287293
}
288294

289295
func TestConcurrent(t *testing.T) {
296+
t.Logf("Running concurrency test with %d demos\n", concurrentDemos)
297+
290298
var i int64
291299
runner := func() {
292-
f, err := os.Open(defaultDemPath)
293-
if err != nil {
294-
t.Fatal(err)
295-
}
296-
defer f.Close()
297-
298-
p := dem.NewParser(f)
299-
300-
_, err = p.ParseHeader()
301-
if err != nil {
302-
t.Fatal(err)
303-
}
304-
305300
n := atomic.AddInt64(&i, 1)
306-
fmt.Printf("Starting runner %d\n", n)
301+
fmt.Printf("Starting concurrent runner %d\n", n)
307302

308303
ts := time.Now()
309304

310-
err = p.ParseToEnd()
311-
if err != nil {
312-
t.Fatal(err)
313-
}
305+
parseDefaultDemo(t)
314306

315307
fmt.Printf("Runner %d took %s\n", n, time.Since(ts))
316308
}
317309

310+
runConcurrently(runner)
311+
}
312+
313+
func parseDefaultDemo(tb testing.TB) {
314+
f, err := os.Open(defaultDemPath)
315+
if err != nil {
316+
tb.Fatal(err)
317+
}
318+
defer f.Close()
319+
320+
p := dem.NewParser(f)
321+
322+
_, err = p.ParseHeader()
323+
if err != nil {
324+
tb.Fatal(err)
325+
}
326+
327+
err = p.ParseToEnd()
328+
if err != nil {
329+
tb.Fatal(err)
330+
}
331+
}
332+
333+
func runConcurrently(runner func()) {
318334
var wg sync.WaitGroup
319-
for j := 0; j < 2; j++ {
335+
for i := 0; i < concurrentDemos; i++ {
320336
wg.Add(1)
321337
go func() { runner(); wg.Done() }()
322338
}
@@ -344,7 +360,7 @@ func TestDemoSet(t *testing.T) {
344360
defer func() {
345361
pErr := recover()
346362
if pErr != nil {
347-
t.Errorf("Failed to parse '%s/%s': %s\n", demSetPath, name, pErr)
363+
t.Errorf("Parsing of '%s/%s' paniced: %s\n", demSetPath, name, pErr)
348364
}
349365
}()
350366

@@ -367,29 +383,7 @@ func TestDemoSet(t *testing.T) {
367383

368384
func BenchmarkDemoInfoCs(b *testing.B) {
369385
for i := 0; i < b.N; i++ {
370-
func() {
371-
f, err := os.Open(defaultDemPath)
372-
if err != nil {
373-
b.Fatal(err)
374-
}
375-
defer f.Close()
376-
377-
p := dem.NewParser(f)
378-
379-
_, err = p.ParseHeader()
380-
if err != nil {
381-
b.Fatal(err)
382-
}
383-
384-
ts := time.Now()
385-
386-
err = p.ParseToEnd()
387-
if err != nil {
388-
b.Fatal(err)
389-
}
390-
391-
b.Logf("Took %s\n", time.Since(ts))
392-
}()
386+
parseDefaultDemo(b)
393387
}
394388
}
395389

@@ -420,13 +414,17 @@ func BenchmarkInMemory(b *testing.B) {
420414
b.Fatal(err)
421415
}
422416

423-
ts := time.Now()
424-
425417
err = p.ParseToEnd()
426418
if err != nil {
427419
b.Fatal(err)
428420
}
421+
}
422+
}
429423

430-
b.Logf("Took %s\n", time.Since(ts))
424+
func BenchmarkConcurrent(b *testing.B) {
425+
b.Logf("Running concurrency benchmark with %d demos\n", concurrentDemos)
426+
427+
for i := 0; i < b.N; i++ {
428+
runConcurrently(func() { parseDefaultDemo(b) })
431429
}
432430
}

0 commit comments

Comments
 (0)