Skip to content

Commit

Permalink
Added conky_parse bulk eager loading to data module.
Browse files Browse the repository at this point in the history
Many individual conky_parse calls are slow to the point of exceeding a
one second update interval. The EagerLoader will collect requested conky
strings and parse them in bulk on the next update. That way in most
cases only one call to conky_parse happens per update.
  • Loading branch information
philer committed Dec 4, 2019
1 parent cf36d6e commit 5537f6b
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 40 deletions.
131 changes: 95 additions & 36 deletions src/data.lua
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,69 @@ local function convert_unit(from, to, value)
return value
end


-- Gather conky_parse calls and run them in bulk on the next update.
local EagerLoader = util.class()

function EagerLoader:init()
self._vars = {} -- maps vars to max age
self._results = {} -- maps vars to results
end

--- Run a bulk conky_parse with collected strings from previous updates.
-- Called at the begin of each update to greatly improve performance.
-- @function eager_loader:load
function EagerLoader:load()
local vars = {}

-- age remembered variables, queue outdated ones for evaluation
local i = 1
for var, remember in pairs(self._vars) do
remember = remember > 1 and remember - 1 or nil
self._vars[var] = remember
if not remember then
self._results[var] = nil
vars[i] = var
i = i + 1
end
end
if i == 1 then return end

-- parse collected variables
i = 1
local conky_string = "<|" .. table.concat(vars, "|><|") .. "|>"
for result in conky_parse(conky_string):gmatch("<|(.-)|>") do
self._results[vars[i]] = result
i = i + 1
end
end

--- Retrieve a conky_parse result.
-- @function eager_loader:get
-- @int[opt=1] remember
-- @string var string to be evaluated by conky_parse
function EagerLoader:get(remember, var)
if not var then
remember, var = 1, remember
end

-- queue this variable for future updates
if not self._vars[var] then
self._vars[var] = remember or 1
end

-- retrieve the result
if not self._results[var] then
self._results[var] = conky_parse(var)
end
return self._results[var]
end


local loader = EagerLoader()
data.eager_loader = loader


--- Get the current usage percentages of individual CPU cores
-- @int cores number of CPU cores
-- @treturn {number,...}
Expand All @@ -49,7 +112,7 @@ function data.cpu_percentages(cores)
for i = 2, cores do
conky_string = conky_string .. "|${cpu cpu" .. i .. "}"
end
return util.map(tonumber, conky_parse(conky_string):gmatch("%d+"))
return util.map(tonumber, loader:get(conky_string):gmatch("%d+"))
end

--- Get the current frequencies at which individual CPU cores are running
Expand All @@ -60,7 +123,7 @@ function data.cpu_frequencies(cores)
for i = 2, cores do
conky_string = conky_string .. "|${freq_g " .. i .. "}"
end
return util.map(tonumber, conky_parse(conky_string):gmatch("%d+[,.]?%d*"))
return util.map(tonumber, loader:get(conky_string):gmatch("%d+[,.]?%d*"))
end

--- Get the current CPU core temperatures
Expand All @@ -81,7 +144,7 @@ end
-- @tparam ?string unit like "B", "MiB", "kB", ...
-- @treturn number,number,number,number usage, easyfree, free, total
function data.memory(unit)
local conky_output = conky_parse("$mem|$memeasyfree|$memfree|$memmax")
local conky_output = loader:get("$mem|$memeasyfree|$memfree|$memmax")
local results = {}
for value, parsed_unit in conky_output:gmatch("(%d+%p?%d*) ?(%w+)") do
table.insert(results, convert_unit(parsed_unit, unit, tonumber(value)))
Expand All @@ -93,7 +156,7 @@ end
-- @string interface e.g. "eth0"
-- @treturn number,number downspeed and upspeed in KiB
function data.network_speed(interface)
local result = conky_parse(string.format("${downspeedf %s}|${upspeedf %s}", interface, interface))
local result = loader:get(string.format("${downspeedf %s}|${upspeedf %s}", interface, interface))
return unpack(util.map(tonumber, result:gmatch("%d+%p?%d*")))
end

Expand All @@ -112,7 +175,7 @@ end
--- Get current GPU frequency.
-- @treturn number
function data.gpu_frequency()
return tonumber(conky_parse("${nvidia gpufreq}"))
return tonumber(loader:get("${nvidia gpufreq}"))
end

--- Get current GPU temperature.
Expand Down Expand Up @@ -144,6 +207,33 @@ function data.gpu_top()
end


--- Is the given path a mount? (see conky's is_mounted)
-- @string path
-- @treturn bool
function data.is_mounted(path)
return "1" == loader:get(5, string.format("${if_mounted %s}1${endif}", path))
end

--- Get the drive usage in percent for the given path.
-- @string path
-- @treturn number
function data.drive_percentage(path)
return tonumber(loader:get(5, string.format("${fs_used_perc %s}", path)))
end

--- Get activity of a drive. If unit is specified the value will be converted
-- to that unit.
-- @string device e.g. /dev/sda1
-- @string[opt] mode "read" or "write"; both if nil
-- @string[opt] unit like "B", "MiB", "kB", ...; no conversion if nil
-- @treturn number,string activity, unit
function data.diskio(device, mode, unit)
mode = mode and "_" .. mode or ""
local result = loader:get(("${diskio%s %s}"):format(mode, device))
local value, parsed_unit = result:match("(%d+%p?%d*) ?(%w+)")
return convert_unit(parsed_unit, unit, tonumber(value)), unit or parsed_unit
end

--- Detect mount points and their respective devices plus physical devices.
-- @function data.find_devices
-- @treturn table mapping of mount points (paths) to value pairs of
Expand All @@ -163,37 +253,6 @@ data.find_devices = util.memoize(10, function()
return mounts
end)

--- Get the drive usage in percent for the given path.
-- @function data.drive_percentag
-- @string path
-- @treturn number
data.drive_percentage = util.memoize(5, function(path)
return tonumber(conky_parse(string.format("${fs_used_perc %s}", path)))
end)


--- Is the given path a mount? (see conky's is_mounted)
-- @function data.is_mounted
-- @string path
-- @treturn bool
data.is_mounted = util.memoize(5, function(path)
return "1" == conky_parse(string.format("${if_mounted %s}1${else}0${endif}", path))
end)


--- Get activity of a drive. If unit is specified the value will be converted
-- to that unit.
-- @string device e.g. /dev/sda1
-- @string[opt] mode "read" or "write"; both if nil
-- @string[opt] unit like "B", "MiB", "kB", ...; no conversion if nil
-- @treturn number,string activity, unit
function data.diskio(device, mode, unit)
mode = mode and "_" .. mode or ""
local result = conky_parse(("${diskio%s %s}"):format(mode, device))
local value, parsed_unit = result:match("(%d+%p?%d*) ?(%w+)")
return convert_unit(parsed_unit, unit, tonumber(value)), unit or parsed_unit
end

--- Get current HDD/SSD temperatures.
-- Relies on hddtemp to be running daemon mode. The results depend on what
-- hddtemp reports and may require manual configuration,
Expand Down
8 changes: 5 additions & 3 deletions src/polycore.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
pcall(function() require('cairo') end)

local util = require('src/util')
local data = require('src/data')

--- Draw debug information
-- @bool DEBUG
Expand All @@ -27,7 +28,10 @@ local function update()
return
end

local update_count = tonumber(conky_parse('${updates}'))
data.eager_loader:load()
local update_count = tonumber(data.eager_loader:get('$updates'))
util.reset_data(update_count)

polycore.renderer:update(update_count)

local cs = cairo_xlib_surface_create(conky_window.display,
Expand All @@ -39,8 +43,6 @@ local function update()
cairo_surface_destroy(cs)
polycore.renderer:render(cr)
cairo_destroy(cr)

util.reset_data(update_count)
end


Expand Down
2 changes: 1 addition & 1 deletion src/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ local memoization_clearers = {}
-- @func fn function to be memoized; should only take stringable
-- arguments and return a non-nil value
function util.memoize(delay, fn)
if fn == nil then
if not fn then
delay, fn = 0, delay
end
local results = {}
Expand Down

0 comments on commit 5537f6b

Please sign in to comment.