xref: /xnu-11215/tests/counter/benchmark.lua (revision e6231be0)
1#!/usr/local/bin/recon
2require 'strict'
3
4local benchrun = require 'benchrun'
5local perfdata = require 'perfdata'
6local sysctl = require 'sysctl'
7local csv = require 'csv'
8
9local kDefaultNumWrites = 10000000000
10
11local benchmark = benchrun.new {
12    name = 'xnu.per_cpu_counter',
13    version = 1,
14    arg = arg,
15    modify_argparser = function(parser)
16        parser:argument{
17          name = 'path',
18          description = 'Path to benchmark binary'
19        }
20        parser:option{
21            name = '--cpu-workers',
22            description = 'Number of cpu workers'
23        }
24        parser:flag{
25          name = '--through-max-workers',
26          description = 'Run benchmark for [1..n] cpu workers'
27        }
28        parser:flag{
29          name = '--through-max-workers-fast',
30          description = 'Run benchmark for [1..2] and each power of four value in [4..n] cpu workers'
31        }
32        parser:option {
33            name = "--num-writes",
34            description = "number of writes",
35            default = kDefaultNumWrites
36        }
37        parser:option{
38            name = '--variant',
39            description = 'Which benchmark variant to run (scalable, atomic, or racy)',
40            default = 'scalable',
41            choices = {"scalable", "atomic", "racy"}
42        }
43    end
44}
45
46assert(benchmark.opt.path, "No path supplied for fault throughput binary")
47
48local ncpus, err = sysctl('hw.logicalcpu_max')
49assert(ncpus > 0, 'invalid number of logical cpus')
50local cpu_workers = tonumber(benchmark.opt.cpu_workers) or ncpus
51
52local writes_per_second = perfdata.unit.custom('writes/sec')
53local tests = {}
54
55function QueueTest(num_cores)
56    table.insert(tests, {
57        path = benchmark.opt.path,
58        num_cores = num_cores,
59    })
60end
61
62if benchmark.opt.through_max_workers then
63    for i = 1, cpu_workers do
64        QueueTest(i)
65    end
66elseif benchmark.opt.through_max_workers_fast then
67    local i = 1
68    while i <= cpu_workers do
69        QueueTest(i)
70        -- Always do a run with two threads to see what the first part of
71        -- the scaling curve looks like
72        -- (and to measure perf on dual core systems).
73        if i == 1 and cpu_workers >= 2 then
74            QueueTest(i + 1)
75        end
76        i = i * 4
77    end
78else
79    QueueTest(cpu_workers)
80end
81
82for _, test in ipairs(tests) do
83    local args = {test.path, benchmark.opt.variant, benchmark.opt.num_writes, test.num_cores,
84                     echo = true}
85    for out in benchmark:run(args) do
86        local result = out:match("-----Results-----\n(.*)")
87        benchmark:assert(result, "Unable to find result data in output")
88        local data = csv.openstring(result, {header = true})
89        for field in data:lines() do
90            for k, v in pairs(field) do
91                local unit = writes_per_second
92                local larger_better = true
93                if k == "loss" then
94                    unit = percentage
95                    larger_better = false
96                end
97                benchmark.writer:add_value(k, unit, tonumber(v), {
98                  [perfdata.larger_better] = larger_better,
99                  threads = test.num_cores,
100                  variant = benchmark.opt.variant
101                })
102            end
103        end
104    end
105end
106
107benchmark:finish()
108