mirror of
https://github.com/koreader/koreader.git
synced 2025-12-13 20:36:53 +01:00
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).
This commit is contained in:
@@ -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
|
||||
--]]
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
--[[
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user