Browse Source

Kobo/Elipsa: More fine-grained control over the amount of online CPU

cores

* Only keep a single core online most of the time.
* Device: Add an enableCPUCores method to allow controlling the amount of
  online CPU cores.
* Move the initial core onlining setup to Kobo:init, instead of the startup script.
* Enable two CPU cores while hinting new (e.g., cache miss) pages in PDF land.
* Enable two CPU cores while processing book metadata.
* Drive-by fix to isolate the DocCache pressure check to KoptInterface
  and actually apply it when it matters most (e.g., k2pdfopt stuff).
reviewable/pr8261/r1
NiLuJe 2 months ago
parent
commit
48da545e32
8 changed files with 126 additions and 38 deletions
  1. +4
    -0
      frontend/device/generic/device.lua
  2. +60
    -8
      frontend/device/kobo/device.lua
  3. +2
    -2
      frontend/document/djvudocument.lua
  4. +9
    -5
      frontend/document/document.lua
  5. +41
    -12
      frontend/document/koptinterface.lua
  6. +2
    -2
      frontend/document/pdfdocument.lua
  7. +0
    -9
      platform/kobo/koreader.sh
  8. +8
    -0
      plugins/coverbrowser.koplugin/bookinfomanager.lua

+ 4
- 0
frontend/device/generic/device.lua View File

@ -460,6 +460,10 @@ end
-- Device specific method for toggling the charging LED
function Device:toggleChargingLED(toggle) end
-- Device specific method for enabling a specific amount of CPU cores
-- (Should only be implemented on embedded platforms where we can afford to control that without screwing with the system).
function Device:enableCPUCores(amount) end
--[[
prepare for application shutdown
--]]

+ 60
- 8
frontend/device/kobo/device.lua View File

@ -71,6 +71,8 @@ local Kobo = Generic:new{
touch_dev = "/dev/input/event1",
-- Event code to use to detect contact pressure
pressure_event = nil,
-- Device features multiple CPU cores
isSMP = no,
}
--- @todo hasKeys for some devices?
@ -321,6 +323,7 @@ local KoboEuropa = Kobo:new{
battery_sysfs = "/sys/class/power_supply/battery",
ntx_dev = "/dev/input/by-path/platform-ntx_event0-event",
touch_dev = "/dev/input/by-path/platform-0-0010-event",
isSMP = yes,
}
function Kobo:init()
@ -439,6 +442,9 @@ function Kobo:init()
-- * Turn it off on startup
-- I've chosen the latter, as I find it vaguely saner, more useful, and it matches Nickel's behavior (I think).
self:toggleChargingLED(false)
-- Only enable a single core on startup
self:enableCPUCores(1)
end
function Kobo:setDateTime(year, month, day, hour, min, sec)
@ -657,7 +663,7 @@ function Kobo:suspend()
local has_wakeup_count = false
f = io.open("/sys/power/wakeup_count", "r")
if f ~= nil then
io.close(f)
f:close()
has_wakeup_count = true
end
@ -682,7 +688,7 @@ function Kobo:suspend()
return false
end
re, err_msg, err_code = f:write("1\n")
io.close(f)
f:close()
logger.info("Kobo suspend: asked the kernel to put subsystems to sleep, ret:", re)
if not re then
logger.err('write error: ', err_msg, err_code)
@ -709,7 +715,7 @@ function Kobo:suspend()
err_msg,
err_code)
end
io.close(f)
f:close()
end
--]]
@ -723,7 +729,7 @@ function Kobo:suspend()
logger.err("cannot open /sys/power/state-extended for writing!")
else
ext_fd:write("0\n")
io.close(ext_fd)
ext_fd:close()
end
return false
end
@ -735,7 +741,7 @@ function Kobo:suspend()
if not re then
logger.err('write error: ', err_msg, err_code)
end
io.close(f)
f:close()
-- NOTE: Ideally, we'd need a way to warn the user that suspending
-- gloriously failed at this point...
-- We can safely assume that just from a non-zero return code, without
@ -786,7 +792,7 @@ function Kobo:resume()
return false
end
local re, err_msg, err_code = f:write("0\n")
io.close(f)
f:close()
logger.info("Kobo resume: unflagged kernel subsystems for resume, ret:", re)
if not re then
logger.err('write error: ', err_msg, err_code)
@ -799,7 +805,7 @@ function Kobo:resume()
f = io.open("/sys/devices/virtual/input/input1/neocmd", "w")
if f ~= nil then
f:write("a\n")
io.close(f)
f:close()
end
end
@ -892,7 +898,53 @@ function Kobo:toggleChargingLED(toggle)
f:flush()
end
io.close(f)
f:close()
end
-- Return the highest core number
local function getCPUCount()
local fd = io.open("/sys/devices/system/cpu/possible", "r")
if fd then
local str = fd:read("*line")
fd:close()
-- Format is n-N, where n is the first core, and N the last (e.g., 0-3)
return tonumber(str:match("%d+$")) or 0
else
return 0
end
end
function Kobo:enableCPUCores(amount)
if not self:isSMP() then
return
end
if not self.cpu_count then
self.cpu_count = getCPUCount()
end
-- Not actually SMP or getCPUCount failed...
if self.cpu_count == 0 then
return
end
-- CPU0 is *always* online ;).
for n=1, self.cpu_count do
local path = "/sys/devices/system/cpu/cpu" .. n .. "/online"
local up
if n >= amount then
up = "0"
else
up = "1"
end
local f = io.open(path, "w")
if f then
f:write(up)
f:close()
end
end
end
function Kobo:isStartupScriptUpToDate()

+ 2
- 2
frontend/document/djvudocument.lua View File

@ -136,8 +136,8 @@ function DjvuDocument:findText(pattern, origin, reverse, caseInsensitive, page)
return self.koptinterface:findText(self, pattern, origin, reverse, caseInsensitive, page)
end
function DjvuDocument:renderPage(pageno, rect, zoom, rotation, gamma, render_mode)
return self.koptinterface:renderPage(self, pageno, rect, zoom, rotation, gamma, render_mode)
function DjvuDocument:renderPage(pageno, rect, zoom, rotation, gamma, render_mode, hinting)
return self.koptinterface:renderPage(self, pageno, rect, zoom, rotation, gamma, render_mode, hinting)
end
function DjvuDocument:hintPage(pageno, zoom, rotation, gamma, render_mode)

+ 9
- 5
frontend/document/document.lua View File

@ -1,6 +1,7 @@
local Blitbuffer = require("ffi/blitbuffer")
local CacheItem = require("cacheitem")
local Configurable = require("configurable")
local Device = require("device")
local DocCache = require("document/doccache")
local DrawContext = require("ffi/drawcontext")
local CanvasContext = require("document/canvascontext")
@ -392,7 +393,7 @@ function Document:getFullPageHash(pageno, zoom, rotation, gamma, render_mode, co
..(self.reflowable_font_size and "|"..self.reflowable_font_size or "")
end
function Document:renderPage(pageno, rect, zoom, rotation, gamma, render_mode)
function Document:renderPage(pageno, rect, zoom, rotation, gamma, render_mode, hinting)
local hash_excerpt
local hash = self:getFullPageHash(pageno, zoom, rotation, gamma, render_mode, self.render_color)
local tile = DocCache:check(hash, TileCacheItem)
@ -411,6 +412,9 @@ function Document:renderPage(pageno, rect, zoom, rotation, gamma, render_mode)
end
end
if hinting then
Device:enableCPUCores(2)
end
self:preRenderPage()
local page_size = self:getPageDimensions(pageno, zoom, rotation)
@ -466,17 +470,17 @@ function Document:renderPage(pageno, rect, zoom, rotation, gamma, render_mode)
DocCache:insert(hash, tile)
self:postRenderPage()
if hinting then
Device:enableCPUCores(1)
end
return tile
end
-- a hint for the cache engine to paint a full page to the cache
--- @todo this should trigger a background operation
function Document:hintPage(pageno, zoom, rotation, gamma, render_mode)
--- @note: Crappy safeguard around memory issues like in #7627: if we're eating too much RAM, drop half the cache...
DocCache:memoryPressureCheck()
logger.dbg("hinting page", pageno)
self:renderPage(pageno, nil, zoom, rotation, gamma, render_mode)
self:renderPage(pageno, nil, zoom, rotation, gamma, render_mode, true)
end
--[[

+ 41
- 12
frontend/document/koptinterface.lua View File

@ -6,6 +6,7 @@ local CacheItem = require("cacheitem")
local CanvasContext = require("document/canvascontext")
local DataStorage = require("datastorage")
local DEBUG = require("dbg")
local Device = require("device")
local DocCache = require("document/doccache")
local Document = require("document/document")
local Geom = require("ui/geometry")
@ -99,12 +100,20 @@ function KoptInterface:setDefaultConfigurable(configurable)
end
function KoptInterface:waitForContext(kc)
-- if koptcontext is being processed in background thread
-- the isPreCache will return 1.
-- If our koptcontext is busy in a background thread, isPreCache will return 1.
local waited = false
while kc and kc:isPreCache() == 1 do
waited = true
logger.dbg("waiting for background rendering")
util.usleep(100000)
end
if waited or self.bg_thread then
-- Background thread is done, go back to a single CPU core.
Device:enableCPUCores(1)
self.bg_thread = nil
end
return kc
end
@ -266,7 +275,7 @@ function KoptInterface:getCachedContext(doc, pageno)
logger.dbg("reflowing page", pageno, "in foreground")
-- reflow page
--local secs, usecs = util.gettime()
page:reflow(kc, 0)
page:reflow(kc)
page:close()
--local nsecs, nusecs = util.gettime()
--local dur = nsecs - secs + (nusecs - usecs) / 1000000
@ -322,13 +331,13 @@ function KoptInterface:getCoverPageImage(doc)
end
end
function KoptInterface:renderPage(doc, pageno, rect, zoom, rotation, gamma, render_mode)
function KoptInterface:renderPage(doc, pageno, rect, zoom, rotation, gamma, render_mode, hinting)
if doc.configurable.text_wrap == 1 then
return self:renderReflowedPage(doc, pageno, rect, zoom, rotation, render_mode)
return self:renderReflowedPage(doc, pageno, rect, zoom, rotation, render_mode, hinting)
elseif doc.configurable.page_opt == 1 then
return self:renderOptimizedPage(doc, pageno, rect, zoom, rotation, render_mode)
return self:renderOptimizedPage(doc, pageno, rect, zoom, rotation, render_mode, hinting)
else
return Document.renderPage(doc, pageno, rect, zoom, rotation, gamma, render_mode)
return Document.renderPage(doc, pageno, rect, zoom, rotation, gamma, render_mode, hinting)
end
end
@ -372,7 +381,7 @@ Render optimized page into tile cache.
Inherited from common document interface.
--]]
function KoptInterface:renderOptimizedPage(doc, pageno, rect, zoom, rotation, render_mode)
function KoptInterface:renderOptimizedPage(doc, pageno, rect, zoom, rotation, render_mode, hinting)
doc.render_mode = render_mode
local bbox = doc:getPageBBox(pageno)
local hash_list = { "renderoptpg" }
@ -381,6 +390,10 @@ function KoptInterface:renderOptimizedPage(doc, pageno, rect, zoom, rotation, re
local cached = DocCache:check(hash, TileCacheItem)
if not cached then
if hinting then
Device:enableCPUCores(2)
end
local page_size = Document.getNativePageDimensions(doc, pageno)
local full_page_bbox = {
x0 = 0, y0 = 0,
@ -409,6 +422,11 @@ function KoptInterface:renderOptimizedPage(doc, pageno, rect, zoom, rotation, re
tile.size = tonumber(tile.bb.stride) * tile.bb.h + 512 -- estimation
kc:free()
DocCache:insert(hash, tile)
if hinting then
Device:enableCPUCores(1)
end
return tile
else
return cached
@ -416,10 +434,13 @@ function KoptInterface:renderOptimizedPage(doc, pageno, rect, zoom, rotation, re
end
function KoptInterface:hintPage(doc, pageno, zoom, rotation, gamma, render_mode)
--- @note: Crappy safeguard around memory issues like in #7627: if we're eating too much RAM, drop half the cache...
DocCache:memoryPressureCheck()
if doc.configurable.text_wrap == 1 then
self:hintReflowedPage(doc, pageno, zoom, rotation, gamma, render_mode)
self:hintReflowedPage(doc, pageno, zoom, rotation, gamma, render_mode, true)
elseif doc.configurable.page_opt == 1 then
self:renderOptimizedPage(doc, pageno, nil, zoom, rotation, gamma, render_mode)
self:renderOptimizedPage(doc, pageno, nil, zoom, rotation, gamma, render_mode, true)
else
Document.hintPage(doc, pageno, zoom, rotation, gamma, render_mode)
end
@ -434,24 +455,32 @@ off by calling self:waitForContext(kctx)
Inherited from common document interface.
--]]
function KoptInterface:hintReflowedPage(doc, pageno, zoom, rotation, gamma, render_mode)
function KoptInterface:hintReflowedPage(doc, pageno, zoom, rotation, gamma, render_mode, hinting)
local bbox = doc:getPageBBox(pageno)
local hash_list = { "kctx" }
self:getContextHash(doc, pageno, bbox, hash_list)
local hash = table.concat(hash_list, "|")
local cached = DocCache:check(hash)
if not cached then
if hinting then
Device:enableCPUCores(2)
end
local kc = self:createContext(doc, pageno, bbox)
local page = doc._document:openPage(pageno)
logger.dbg("hinting page", pageno, "in background")
-- reflow will return immediately and running in background thread
kc:setPreCache()
page:reflow(kc, 0)
self.bg_thread = true
page:reflow(kc)
page:close()
DocCache:insert(hash, ContextCacheItem:new{
size = self.last_context_size or self.default_context_size,
kctx = kc,
})
-- We'll wait until the background thread is done to go back to a single core, as this returns immediately!
-- c.f., :waitForContext
end
end

+ 2
- 2
frontend/document/pdfdocument.lua View File

@ -340,8 +340,8 @@ function PdfDocument:findText(pattern, origin, reverse, caseInsensitive, page)
return self.koptinterface:findText(self, pattern, origin, reverse, caseInsensitive, page)
end
function PdfDocument:renderPage(pageno, rect, zoom, rotation, gamma, render_mode)
return self.koptinterface:renderPage(self, pageno, rect, zoom, rotation, gamma, render_mode)
function PdfDocument:renderPage(pageno, rect, zoom, rotation, gamma, render_mode, hinting)
return self.koptinterface:renderPage(self, pageno, rect, zoom, rotation, gamma, render_mode, hinting)
end
function PdfDocument:hintPage(pageno, zoom, rotation, gamma, render_mode)

+ 0
- 9
platform/kobo/koreader.sh View File

@ -262,15 +262,6 @@ else
KOBO_TS_INPUT="/dev/input/event1"
fi
# Make sure we only keep two cores online on the Elipsa.
# NOTE: That's a bit optimistic, we might actually need to tone that down to one,
# and just toggle the second one on demand (e.g., PDF).
if [ "${PRODUCT}" = "europa" ]; then
echo "1" >"/sys/devices/system/cpu/cpu1/online"
echo "0" >"/sys/devices/system/cpu/cpu2/online"
echo "0" >"/sys/devices/system/cpu/cpu3/online"
fi
# We'll want to ensure Portrait rotation to allow us to use faster blitting codepaths @ 8bpp,
# so remember the current one before fbdepth does its thing.
IFS= read -r ORIG_FB_ROTA <"/sys/class/graphics/fb0/rotate"

+ 8
- 0
plugins/coverbrowser.koplugin/bookinfomanager.lua View File

@ -659,6 +659,9 @@ function BookInfoManager:collectSubprocesses()
end
end
end
-- We're done, back to a single core
Device:enableCPUCores(1)
end
function BookInfoManager:terminateBackgroundJobs()
@ -698,6 +701,11 @@ function BookInfoManager:extractInBackground(files)
self.cleanup_needed = true -- so we will remove temporary cache directory created by subprocess
-- If it's the first subprocess we're launching, enable 2 CPU cores
if #self.subprocesses_pids == 0 then
Device:enableCPUCores(2)
end
-- Run task in sub-process, and remember its pid
local task_pid = FFIUtil.runInSubProcess(task)
if not task_pid then

Loading…
Cancel
Save