Implement profiler
This commit is contained in:
@@ -128,7 +128,7 @@ handle:close()
|
||||
|
||||
--print(require("serialize")(data, "\t"))
|
||||
|
||||
-- Halyde terminal doesn't support bold (CSI 1 m) but who cares
|
||||
-- Halyde io doesn't support bold (CSI 1 m) but who cares
|
||||
|
||||
if data.command then
|
||||
terminal.write("\27[1mUsage: \27[0m\n")
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
COMMAND echo
|
||||
USAGE [TEXT]...
|
||||
DESCRIPTION Concatenates and prints text to the terminal.
|
||||
DESCRIPTION Concatenates and prints text to the standard output.
|
||||
ARG1 TEXT
|
||||
ARG1DESCRIPTION Text to print.
|
||||
EXAMPLE1 echo test
|
||||
EXAMPLE2 echo Hello World!
|
||||
EXAMPLE1DESCRIPTION Prints "test" to the terminal.
|
||||
EXAMPLE2DESCRIPTION Prints "Hello World!" to the terminal.
|
||||
EXAMPLE1DESCRIPTION Prints "test" to the standard output.
|
||||
EXAMPLE2DESCRIPTION Prints "Hello World!" to the standard output.
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
-- TODO: make it somehow not depend on ESC s and ESC u
|
||||
local profiler = require("profiler")
|
||||
local PAL = {
|
||||
{ fg = "\27[31m", bg = "\27[41m" },
|
||||
{ fg = "\27[32m", bg = "\27[42m" },
|
||||
{ fg = "\27[33m", bg = "\27[43m" },
|
||||
{ fg = "\27[34m", bg = "\27[44m" },
|
||||
{ fg = "\27[35m", bg = "\27[45m" },
|
||||
{ fg = "\27[36m", bg = "\27[46m" },
|
||||
{ fg = "\27[37m", bg = "\27[47m" }
|
||||
}
|
||||
local function pal(i) return PAL[((i - 1) % #PAL) + 1] end
|
||||
|
||||
local results = profiler.results()
|
||||
if not results then
|
||||
print("No profiling data")
|
||||
return
|
||||
end
|
||||
|
||||
local radius
|
||||
local W = terminal.getResolution()
|
||||
if (W == 160) then radius = 20
|
||||
elseif (W == 80) then radius = 6
|
||||
else radius = 0; PAL = {{fg = "", bg = ""}} end
|
||||
|
||||
local MIN_PC = math.min(3, 100 / (radius * 2))
|
||||
|
||||
local total = 0
|
||||
for _, r in ipairs(results) do total = total + r.time end
|
||||
|
||||
local main = {}
|
||||
local other = {}
|
||||
local ot = 0
|
||||
local on_ = 0
|
||||
for _, r in ipairs(results) do
|
||||
if r.time / total * 100 >= MIN_PC then
|
||||
table.insert(main, r)
|
||||
else
|
||||
table.insert(other, r)
|
||||
ot = ot + r.time;
|
||||
on_ = on_ + 1
|
||||
end
|
||||
end
|
||||
if on_ then
|
||||
table.insert(main, { label = "other (" .. on_ .. ")", time = ot })
|
||||
end
|
||||
|
||||
local START = -math.pi / 2
|
||||
local slices = {}
|
||||
local cur = START
|
||||
for i, m in ipairs(main) do
|
||||
local sw = m.time / total * 2 * math.pi
|
||||
slices[i] = {
|
||||
label = m.label,
|
||||
time = m.time,
|
||||
pc = ("%.1f%%"):format(m.time / total * 100),
|
||||
color = pal(i),
|
||||
sa = cur,
|
||||
sw = sw
|
||||
}
|
||||
cur = cur + sw
|
||||
end
|
||||
if on_ then slices[#slices].color={fg = "\27[30m", bg = "\27[40m"} end
|
||||
|
||||
local ASP = 2
|
||||
local cx = radius * ASP
|
||||
local cy = radius
|
||||
|
||||
local function slice_at(sx, sy)
|
||||
local dy = sy - cy
|
||||
local dx = (sx - cx) / ASP
|
||||
if dx * dx + dy * dy > radius * radius then return nil end
|
||||
local a = math.atan2(dy, dx)
|
||||
if a < START then a = a + 2 * math.pi end
|
||||
for i, sl in ipairs(slices) do
|
||||
if a >= sl.sa and a < sl.sa + sl.sw then return i end
|
||||
end
|
||||
return #slices
|
||||
end
|
||||
|
||||
local SUB = {
|
||||
{ dx = -0.25, dy = -0.375 },
|
||||
{ dx = -0.25, dy = -0.125 },
|
||||
{ dx = -0.25, dy = 0.125 },
|
||||
{ dx = 0.25, dy = -0.375 },
|
||||
{ dx = 0.25, dy = -0.125 },
|
||||
{ dx = 0.25, dy = 0.125 },
|
||||
{ dx = -0.25, dy = 0.375 },
|
||||
{ dx = 0.25, dy = 0.375 },
|
||||
}
|
||||
|
||||
for y = 0, radius * 2 do
|
||||
local lx, rx = math.ceil(cx - radius * ASP), math.floor(cx + radius * ASP)
|
||||
|
||||
terminal.write(("\27[%dC"):format(lx))
|
||||
for x = lx, rx do
|
||||
local c = {}
|
||||
local n = 0
|
||||
for k = 1, 8 do
|
||||
local s = slice_at(x + SUB[k].dx, y + SUB[k].dy)
|
||||
c[k] = s
|
||||
if s then n = n + 1 end
|
||||
end
|
||||
|
||||
if n == 0 then
|
||||
terminal.write("\27[0m ")
|
||||
else
|
||||
local counts = {}
|
||||
local order = {}
|
||||
for k = 1, 8 do
|
||||
if c[k] then
|
||||
local key = c[k]
|
||||
if not counts[key] then
|
||||
counts[key] = 0
|
||||
table.insert(order, key)
|
||||
end
|
||||
counts[key] = counts[key] + 1
|
||||
end
|
||||
end
|
||||
table.sort(order, function(a, b) return counts[a] > counts[b] end)
|
||||
local dom = order[1]
|
||||
local sub = order[2]
|
||||
|
||||
if n == 8 then
|
||||
terminal.write(slices[dom].color.bg)
|
||||
if sub == nil then
|
||||
terminal.write(" ")
|
||||
else
|
||||
local mask = 0
|
||||
for k = 1, 8 do
|
||||
if c[k] == sub then mask = mask + (1 << (k - 1)) end
|
||||
end
|
||||
terminal.write(slices[sub].color.fg)
|
||||
terminal.write(utf8.char(0x2800 + mask))
|
||||
end
|
||||
else
|
||||
local mask = 0
|
||||
for k = 1, 8 do
|
||||
if c[k] == dom then mask = mask + (1 << (k - 1)) end
|
||||
end
|
||||
terminal.write("\27[0m")
|
||||
terminal.write(slices[dom].color.fg)
|
||||
terminal.write(utf8.char(0x2800 + mask))
|
||||
end
|
||||
end
|
||||
end
|
||||
terminal.write('\n')
|
||||
end
|
||||
|
||||
terminal.write("\27[" .. radius * 2 + 1 .. "A\27[s")
|
||||
local function draw_text(col, row, s)
|
||||
terminal.write("\27[u\27[" .. col .. "C\27[" .. row - 1 .. "B" .. s)
|
||||
end
|
||||
|
||||
terminal.write("\27[0m")
|
||||
if radius == 0 then goto tier1gpu end
|
||||
for _, sl in ipairs(slices) do
|
||||
local y = math.floor(cy + radius * 0.62 * math.sin(sl.sa + sl.sw / 2) + 0.5)
|
||||
|
||||
local function in_slice(x, row)
|
||||
local a = math.atan2(row - cy, (x - cx) / ASP)
|
||||
if a < START then
|
||||
a = a + 2 * math.pi
|
||||
end
|
||||
return a >= sl.sa and a < sl.sa + sl.sw
|
||||
end
|
||||
local function centered_draw(row, text)
|
||||
local dy = row - cy
|
||||
local half_w = math.sqrt(radius * radius - dy * dy) * ASP
|
||||
local lx, rx = math.ceil(cx - half_w), math.floor(cx + half_w)
|
||||
while lx <= rx and not in_slice(lx, row) do
|
||||
lx = lx + 1
|
||||
end
|
||||
while rx >= lx and not in_slice(rx, row) do
|
||||
rx = rx - 1
|
||||
end
|
||||
local avail = rx - lx + 1
|
||||
if avail < 3 then
|
||||
return
|
||||
end
|
||||
local t = #text > avail and text:sub(1, avail - 3) .. "..." or text
|
||||
local start_x = lx + math.floor((avail - #t) / 2)
|
||||
draw_text(start_x, row, sl.color.bg .. t)
|
||||
end
|
||||
centered_draw(y, sl.label)
|
||||
centered_draw(y + 1, sl.pc)
|
||||
end
|
||||
::tier1gpu::
|
||||
|
||||
local max_w = 0
|
||||
for _, sl in pairs(slices) do
|
||||
max_w = math.max(#sl.label + 1, max_w)
|
||||
end
|
||||
for _, sl in pairs(other) do
|
||||
max_w = math.max(#sl.label + 3, max_w)
|
||||
end
|
||||
local x = radius * 2 * ASP + 2
|
||||
if radius == 0 then x = 0 end
|
||||
local cw = W - x - 1
|
||||
local suffix_w = 18
|
||||
local max_lbl = math.max(4, cw - suffix_w)
|
||||
terminal.write("\27[u\27[" .. x .. "C" .. ("\27[0m%" .. max_w .. "s %9s %6s"):format("label", "time", "share"))
|
||||
terminal.write("\n")
|
||||
for _, sl in ipairs(slices) do
|
||||
local lbl = sl.label
|
||||
if #lbl > max_lbl then
|
||||
lbl = sl.label:sub(1, max_lbl - 1) .. "…"
|
||||
end
|
||||
terminal.write("\n\27[" .. x .. "C" .. ("%s%" .. max_w .. "s\27[0m %9.4fs %6s"):format(sl.color.bg, lbl, sl.time, sl.pc))
|
||||
end
|
||||
for _, sl in ipairs(other) do
|
||||
local lbl = sl.label
|
||||
if #lbl > max_lbl - 2 then
|
||||
lbl = sl.label:sub(1, max_lbl - 3) .. "…"
|
||||
end
|
||||
terminal.write("\n\27[" .. x .. "C" .. ("%s%" .. max_w .. "s\27[0m %9.4fs %6s"):format(slices[#slices].color.bg, lbl, sl.time, ("%.1f%%"):format(sl.time / total * 100)))
|
||||
end
|
||||
|
||||
if radius * 2 + 2 > #slices + #other then
|
||||
terminal.write("\27[" .. radius * 2 + 2 .. "B")
|
||||
end
|
||||
terminal.write("\n")
|
||||
@@ -0,0 +1,15 @@
|
||||
for i = 1, 100 do
|
||||
profiler.start("math_work")
|
||||
local x = 0
|
||||
for j = 1, 200000 do x = x + math.sqrt(j) end
|
||||
profiler.stop("math_work")
|
||||
|
||||
profiler.start("string_work")
|
||||
local s = ""
|
||||
for j = 1, 2000 do s = s .. tostring(j) end
|
||||
profiler.stop("string_work")
|
||||
end
|
||||
|
||||
for _, r in ipairs(profiler.results()) do
|
||||
print(r.label, r.time)
|
||||
end
|
||||
@@ -11,6 +11,7 @@ gpu.bind(screenAddress)
|
||||
gpu.setResolution(gpu.maxResolution())
|
||||
|
||||
local log = assert(loadfile("/lib/log.lua")(loadfile))
|
||||
_G.profiler = assert(loadfile("/lib/profiler.lua")())
|
||||
|
||||
log.kernel.info("Bound GPU to screen " .. tostring(screenAddress))
|
||||
|
||||
@@ -67,10 +68,10 @@ require("/halyde/kernel/datatools.lua") -- If this is not imported BEFORE modloa
|
||||
log.kernel.info("Loading modules")
|
||||
require("/halyde/kernel/modload.lua")
|
||||
|
||||
package.preload("component")
|
||||
package.preload("computer")
|
||||
package.preload("log")
|
||||
package.preload("event")
|
||||
local toPreload = { "component", "computer", "log", "event" }
|
||||
for _, p in pairs(toPreload) do
|
||||
profiler.profile("pre-loading " .. p, package.preload, p)
|
||||
end
|
||||
|
||||
local computer = require("computer")
|
||||
function wait(seconds)
|
||||
|
||||
@@ -19,8 +19,10 @@ local moduleTypes = {}
|
||||
local modulesLoaded = {}
|
||||
|
||||
local function loadModule(modName)
|
||||
local stop = profiler.start("loadModule(" .. tostring(modName) .. ")")
|
||||
if table.find(modulesLoaded, modName) then
|
||||
log.kernel.warn(string.format("[modload: %s] Module was already loaded - skipping", modName))
|
||||
stop()
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -28,6 +30,7 @@ local function loadModule(modName)
|
||||
if not moduleData then
|
||||
log.kernel.warn(string.format("[modload: %s] Could not find module data.", modName))
|
||||
table.remove(moduleList, table.find(moduleList, modName))
|
||||
stop()
|
||||
return true
|
||||
end
|
||||
local ready = false
|
||||
@@ -42,6 +45,7 @@ local function loadModule(modName)
|
||||
end
|
||||
if not ready then
|
||||
log.kernel.info(string.format("[modload: %s] Module not ready - skipping", modName))
|
||||
stop()
|
||||
return false
|
||||
end
|
||||
if type(moduleData.dependencies) == "table" then
|
||||
@@ -74,16 +78,19 @@ local function loadModule(modName)
|
||||
tostring(err or "unknown error")
|
||||
)
|
||||
)
|
||||
stop()
|
||||
return false
|
||||
else
|
||||
table.insert(modulesLoaded, modName)
|
||||
table.remove(moduleList, table.find(moduleList, modName))
|
||||
end
|
||||
end
|
||||
stop()
|
||||
return true
|
||||
end
|
||||
|
||||
for _, modName in pairs(moduleList) do -- Get all the module types
|
||||
local stop = profiler.start("getting module " .. modName .. ")")
|
||||
log.kernel.info(string.format("[modload: %s] Getting data from module", modName))
|
||||
local moduleData
|
||||
local status, err = pcall(function()
|
||||
@@ -123,6 +130,7 @@ for _, modName in pairs(moduleList) do -- Get all the module types
|
||||
moduleTypes[modName] = moduleData.type -- Not the other way around because there can be multiple modules of the same type, but there can't be multiple entries with the same key
|
||||
end
|
||||
::continue::
|
||||
stop()
|
||||
end
|
||||
|
||||
local function loadAllModules() -- attempt at loading all modules, unless if they're not ready
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
local module = {}
|
||||
|
||||
module.dependencies = { "terminal" }
|
||||
module.dependencies = { "io" }
|
||||
|
||||
function module.check()
|
||||
return true -- This module should always be loaded
|
||||
|
||||
@@ -17,9 +17,8 @@ function module.init()
|
||||
local event = require("event")
|
||||
|
||||
local component = require("component")
|
||||
local computer = require("computer")
|
||||
local gpu = component.gpu
|
||||
_G._PUBLIC.terminal = {}
|
||||
_G._PUBLIC.io = {}
|
||||
|
||||
local readHistory = {}
|
||||
function _PUBLIC.terminal.getHistory(id)
|
||||
@@ -145,9 +144,6 @@ function module.init()
|
||||
|
||||
local ANSIColorPalette = getColorPalette(gpu.maxDepth())
|
||||
|
||||
local expecting_unicode_bytes = 0
|
||||
local unicode_bytes_left = 0
|
||||
local unicode_codepoint = 0
|
||||
local cursor = { x = 1, y = 1, X = nil, Y = nil } -- X and Y are managed by ESC s and ESC u
|
||||
local printState = 0 -- 0:none 1:in ESC 2:in CSI
|
||||
local color = {
|
||||
@@ -566,7 +562,7 @@ function module.init()
|
||||
local args = {...}
|
||||
local stringArgs = {}
|
||||
for _, arg in pairs(args) do
|
||||
if type(arg)=="table" then
|
||||
if type(arg) == "table" then
|
||||
table.insert(stringArgs, serialize(arg))
|
||||
elseif tostring(arg) then
|
||||
table.insert(stringArgs, tostring(arg))
|
||||
@@ -815,7 +811,7 @@ function module.init()
|
||||
end
|
||||
|
||||
function module.exit()
|
||||
_G._PUBLIC.terminal = nil
|
||||
_G._PUBLIC.io = nil
|
||||
end
|
||||
|
||||
return module
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
local thing = _G._PUBLIC or _G
|
||||
thing.__PROFILER_INSTANCE = thing.__PROFILER_INSTANCE or { timers = {} }
|
||||
local timers = thing.__PROFILER_INSTANCE.timers
|
||||
local profiler = {}
|
||||
|
||||
function profiler.start(label, overwrite)
|
||||
thing.__PROFILER_INSTANCE.lastadded = label
|
||||
timers[label] = timers[label] or {}
|
||||
if not timers[label].start or overwrite then
|
||||
timers[label].start = os.clock()
|
||||
end
|
||||
return function() timers[label].time = timers[label].time or os.clock() - timers[label].start end
|
||||
end
|
||||
|
||||
function profiler.results()
|
||||
local _now = nil
|
||||
local function now()
|
||||
_now = _now or os.clock()
|
||||
return _now
|
||||
end
|
||||
local out = {}
|
||||
for label, t in pairs(timers) do
|
||||
table.insert(out, { label = label, time = t.time or now() - t.start })
|
||||
end
|
||||
table.sort(out, function(a, b) return a.time > b.time end)
|
||||
return out
|
||||
end
|
||||
|
||||
function profiler.profile(label, func, ...)
|
||||
local stop = profiler.start(label)
|
||||
func(...)
|
||||
stop()
|
||||
end
|
||||
|
||||
return profiler
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
local io = require("io")
|
||||
local io = require("terminal")
|
||||
local component = require("component")
|
||||
if not component.isAvailable("internet") then
|
||||
io.stderr.write("This program requires an internet card to run.")
|
||||
|
||||
Reference in New Issue
Block a user