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