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