diff --git a/config/config.dhall b/config/config.dhall index 78747b9..246c398 100644 --- a/config/config.dhall +++ b/config/config.dhall @@ -138,6 +138,23 @@ let FileSystem = , default.geometry = FSGeo::{=} } +let GPUPower = + {- + Controls how the GPU will be queries so it can go to sleep when 'idle'. + + freq_limit: only query the GPU every 'cycle_seconds' when frequency is below + this value; this should be a sensible value representing and + 'idle' gpu state. + cycle_seconds: the number of seconds to query the GPU when below the idle + frequency limit; note that this must be long enough to let + the GPU go to sleep when it actually is ready to sleep, but + not so long that the next query will miss too much activity + in the case the GPU doesn't shut off + -} + { Type = { freq_limit : Natural, cycle_seconds : Natural } + , default = { freq_limit = 250, cycle_seconds = 10 } + } + let Graphics = {- Defines Graphics module configuration @@ -158,8 +175,9 @@ let Graphics = , show_mem_util : Bool , show_vid_util : Bool , geometry : GfxGeo.Type + , power : GPUPower.Type } - , default.geometry = GfxGeo::{=} + , default = { geometry = GfxGeo::{=}, power = GPUPower::{=} } } let Memory = diff --git a/src/modules/graphics.lua b/src/modules/graphics.lua index 1a5786a..eca0c9e 100644 --- a/src/modules/graphics.lua +++ b/src/modules/graphics.lua @@ -20,29 +20,43 @@ return function(update_freq, config, common, width, point) i_o.assert_exe_exists(NVIDIA_EXE) - -- vars to process the nv settings glob - -- + local mk_query_cmd = function(props) + return string.format( + '%s --query-gpu=%s --format=csv,noheader,nounits', + NVIDIA_EXE, + pure.collapse(props, ',') + ) + end - local NV_QUERY = NVIDIA_EXE.. - ' --query-gpu=memory.used,memory.total,temperature.gpu,clocks.gr,clocks.mem,utilization.gpu,utilization.decoder'.. - ' --format=csv,noheader,nounits' + -- TODO also use encoder for video util? + -- TODO add video clock speed? + local query_stats = mk_query_cmd( + { + 'memory.used', + 'temperature.gpu', + 'clocks.gr', + 'clocks.mem', + 'utilization.gpu', + 'utilization.decoder' + } + ) - local NV_REGEX = '(%d+), (%d+), (%d+), (%d+), (%d+), (%d+), (%d+)' + local NV_REGEX = '(%d+), (%d+), (%d+), (%d+), (%d+), (%d+)' local mod_state = { error = false, + total_memory = 0, gpu_frequency = 0, memory_frequency = 0, used_memory = 0, - total_memory = 0, temp_reading = 0, gpu_utilization = 0, vid_utilization = 0 } local sleep_token = 0 - local sleep_limit = 10 - local gpu_idle_freq_limit = 250 + local sleep_limit = config.power.cycle_seconds + local gpu_idle_freq_limit = config.power.freq_limit -- TODO ensure this file exists local runtime_status_file = config.dev_power..'/runtime_status' @@ -74,12 +88,11 @@ return function(update_freq, config, common, width, point) sleep_token = 0 end if is_active and want_nvidia_query and sleep_token == 0 then - local nvidia_settings_glob = i_o.execute_cmd(NV_QUERY) + local nvidia_settings_glob = i_o.execute_cmd(query_stats) if nvidia_settings_glob == nil then mod_state.error = 'Error' else mod_state.used_memory, - mod_state.total_memory, mod_state.temp_reading, mod_state.gpu_frequency, mod_state.memory_frequency, @@ -89,6 +102,11 @@ return function(update_freq, config, common, width, point) mod_state.gpu_frequency = tonumber(mod_state.gpu_frequency) mod_state.error = false end + -- query total memory if we need it (do this here so we don't turn on + -- the gpu every time we start conky) + if mod_state.total_memory == 0 and config.show_mem_util then + mod_state.total_memory = tonumber(i_o.execute_cmd(mk_query_cmd({'memory.total'}))) + end elseif is_active then mod_state.error = false else diff --git a/src/pure.lua b/src/pure.lua index 8b05d92..6485ebd 100644 --- a/src/pure.lua +++ b/src/pure.lua @@ -4,6 +4,7 @@ local err = require 'err' local __math_floor = math.floor local __table_insert = table.insert +local __string_format = string.format -------------------------------------------------------------------------------- -- zippy functions @@ -50,9 +51,52 @@ M.reduce = function(f, init, seq) end end +M.member = function(x, seq) + return M.reduce( + function(acc, next) return acc or next == x end, + false, + M.values(seq) + ) +end + +M.collapse = function(seq, delim) + if #seq == 0 then + return '' + elseif #seq == 1 then + return seq[1] + else + local init, rest = M.head(seq) + return M.reduce( + function(acc, next) return __string_format('%s%s%s', acc, delim, next) end, + init, + rest + ) + end +end + -------------------------------------------------------------------------------- -- mappy functions +M.keys = function(seq) + local r = {} + local n = 1 + for k, _ in pairs(seq) do + r[n] = k + n = n + 1 + end + return r +end + +M.values = function(seq) + local r = {} + local n = 1 + for _, v in pairs(seq) do + r[n] = v + n = n + 1 + end + return r +end + M.map = function(f, seq, ...) local r = {} for i = 1, #seq do @@ -144,6 +188,14 @@ M.length = function(tbl) return n end +M.head = function(seq) + local r = {} + for i = 2, #seq do + r[i - 1] = seq[i] + end + return seq[1], r +end + -- a stupid but composable function M.get = function(key, tbl) return tbl[key]