Skip to content

EPUB: limit image download display updates to once per second#13073

Merged
Frenzie merged 2 commits into
koreader:masterfrom
Frenzie:wikipedia-epub-benchmark
Jan 17, 2025
Merged

EPUB: limit image download display updates to once per second#13073
Frenzie merged 2 commits into
koreader:masterfrom
Frenzie:wikipedia-epub-benchmark

Conversation

@Frenzie

@Frenzie Frenzie commented Jan 15, 2025

Copy link
Copy Markdown
Member

On Wikipedia articles with for example 30 images this reduces the download time from ~12 seconds to ~8 seconds (depending on image size and connection speed), which is fairly significant.


For context, I was looking into doing something like https://www.lua.org/pil/9.4.html to speed up image downloads when I noticed that a lot of the time spent came from the 100 ms spent waiting in between downloading every image.

I'm inclined to think it's fine if not better as currently in this PR, but regular non-waiting InfoMessages could be interspersed along these lines if desired:

diff --git a/frontend/ui/wikipedia.lua b/frontend/ui/wikipedia.lua
index 36cdcecfa..208e6ab17 100644
--- a/frontend/ui/wikipedia.lua
+++ b/frontend/ui/wikipedia.lua
@@ -1502,12 +1502,16 @@ abbr.abbr {
     local socket = require("socket")
     local before_images_time = socket.gettime()
     local time_prev = before_images_time
+    local info_message
 
     -- ----------------------------------------------------------------
     -- OEBPS/images/*
     if include_images then
         local nb_images = #images
         for inum, img in ipairs(images) do
+            if info_message then
+                UIManager:close(info_message)
+            end
             -- Process can be interrupted every second between image downloads
             -- by tapping while the InfoMessage is displayed
             -- We use the fast_refresh option from image #2 for a quicker download
@@ -1520,6 +1524,11 @@ abbr.abbr {
                     break
                 end
                 time_prev = now
+            else
+                info_message = InfoMessage:new{
+                    text = T(_("Retrieving image %1 / %2 …"), inum, nb_images),
+                }
+                UIManager:show(info_message)
             end
             local src = img.src
             if use_img_2x and img.src2x then

This change is Reviewable

@Frenzie Frenzie added this to the 2025.01 milestone Jan 15, 2025
@Frenzie Frenzie requested a review from poire-z January 15, 2025 17:30
@poire-z

poire-z commented Jan 15, 2025

Copy link
Copy Markdown
Contributor

Not really caring about the speed, and I guess it could feel a bit strange not seeing ALL the numbers :) but some random step of 2 or 3.
(Also not super fan of seeing introduced with socket.gettime() another time source :) @NiLuJe added already a lot.)

On Wikipedia articles with for example 30 images this reduces the download time from ~12 seconds to ~8 seconds [...]
when I noticed that a lot of the time spent came from the 100 ms spent waiting in between downloading every image.

Just curious: what if you change that 100ms to 30ms ? And is it as interruptible as previously ? (not sure if a tap done not in these 100ms is processed when later we show the infomessage).

I'm inclined to think it's fine if not better as currently in this PR, but regular non-waiting InfoMessages could be interspersed along these lines if desired:

There's some optimisations in Trapper:info() to not close/show() each InfoMessage I think, so it can be quite fast updating. I'm quite sure your :close/show() will introduce some slowness on eInk devices (and maybe they won't show at all ? because Trapper:info() force repaint, I think close/show let this be done by UIManager next time it has nothing to do ?)

@Frenzie

Frenzie commented Jan 15, 2025

Copy link
Copy Markdown
Member Author

(Also not super fan of seeing introduced with socket.gettime() another time source :) @NiLuJe added already a lot.)

It came up organically because socket is already used a lot there and it's directly related to network things. Granted, not quite as much in this version of the code. In any case, os.time() isn't precise enough (seconds only). I forgot about http://koreader.rocks/doc/modules/time.html; I'll switch to it then.

Just curious: what if you change that 100ms to 30ms ? And is it as interruptible as previously ? (not sure if a tap done not in these 100ms is processed when later we show the infomessage).

A small improvement. It seems to be about as interruptible but it's hard to say if that would be true on a variety of different hardware.

There's some optimisations in Trapper:info() to not close/show() each InfoMessage I think, so it can be quite fast updating. I'm quite sure your :close/show() will introduce some slowness on eInk devices (and maybe they won't show at all ? because Trapper:info() force repaint, I think close/show let this be done by UIManager next time it has nothing to do ?)

In this form it still adds an extra second but it does show all of them. It's not my preference, I'd rather it be faster with fewer screen updates, but it's fine. (You could make it something like elseif diff % 0.2 < 0.05 then but that's not quite right in its intent.)

diff --git a/frontend/ui/trapper.lua b/frontend/ui/trapper.lua
index b98992ff2..3e677e0f1 100644
--- a/frontend/ui/trapper.lua
+++ b/frontend/ui/trapper.lua
@@ -121,7 +121,7 @@ exact same size.
     Trapper:info("some text about step or progress")
     go_on = Trapper:info()
 ]]
-function Trapper:info(text, fast_refresh)
+function Trapper:info(text, fast_refresh, skip_dismiss_check)
     local _coroutine = coroutine.running()
     if not _coroutine then
         logger.info("unwrapped info:", text)
@@ -137,16 +137,18 @@ function Trapper:info(text, fast_refresh)
         -- the coroutine.yield() that follows.
         -- If no dismiss_callback was fired, we need to get this code resumed:
         -- that will be done with the following go_on_func schedule in 0.1 second.
-        local go_on_func = function() coroutine.resume(_coroutine, true) end
-        -- delay matters: 0.05 or 0.1 seems fine
-        -- 0.01 is too fast: go_on_func is called before our dismiss_callback is processed
-        UIManager:scheduleIn(0.1, go_on_func)
-
-        local go_on = coroutine.yield() -- gives control back to UIManager
-        -- go_on is the 2nd arg given to the coroutine.resume() that got us resumed:
-        -- false if it was a dismiss_callback
-        -- true if it was the schedule go_on_func
-
+        local go_on = true
+        if not skip_dismiss_check then
+            local go_on_func = function() coroutine.resume(_coroutine, true) end
+            -- delay matters: 0.05 or 0.1 seems fine
+            -- 0.01 is too fast: go_on_func is called before our dismiss_callback is processed
+            UIManager:scheduleIn(0.03, go_on_func)
+    
+            go_on = coroutine.yield() -- gives control back to UIManager
+            -- go_on is the 2nd arg given to the coroutine.resume() that got us resumed:
+            -- false if it was a dismiss_callback
+            -- true if it was the schedule go_on_func
+        end
         if not go_on then -- dismiss_callback called
             UIManager:unschedule(go_on_func) -- no more need for this scheduled action
             -- Don't just return false without confirmation (this tap may have been
diff --git a/frontend/ui/wikipedia.lua b/frontend/ui/wikipedia.lua
index 36cdcecfa..974e7172f 100644
--- a/frontend/ui/wikipedia.lua
+++ b/frontend/ui/wikipedia.lua
@@ -1520,6 +1520,8 @@ abbr.abbr {
                     break
                 end
                 time_prev = now
+            else
+                UI:info(T(_("Retrieving image %1 / %2 …"), inum, nb_images), inum >= 2, true)
             end
             local src = img.src
             if use_img_2x and img.src2x then

Comment on lines +586 to +587
local now = time.now()
if time.to_ms(now - time_prev) > 1000 then

@NiLuJe NiLuJe Jan 16, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a time.since wrapper for this sort of stuff ;).

Suggested change
local now = time.now()
if time.to_ms(now - time_prev) > 1000 then
if time.since(time_prev) > 1.0 then

@Frenzie Frenzie Jan 16, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear, you'd prefer this? Because in principle the suggestion doesn't seem to make much sense to me.

diff --git a/plugins/newsdownloader.koplugin/epubdownloadbackend.lua b/plugins/newsdownloader.koplugin/epubdownloadbackend.lua
index 97cd84157..c958abdac 100644
--- a/plugins/newsdownloader.koplugin/epubdownloadbackend.lua
+++ b/plugins/newsdownloader.koplugin/epubdownloadbackend.lua
@@ -583,14 +583,13 @@ function EpubDownloadBackend:createEpub(epub_path, html, url, include_images, me
             -- by tapping while the InfoMessage is displayed
             -- We use the fast_refresh option from image #2 for a quicker download
             local go_on
-            local now = time.now()
-            if time.to_ms(now - time_prev) > 1000 then
+            if time.since(time_prev) > 1.0 then
+                time_prev = time.now()
                 go_on = UI:info((message and message ~= "" and message .. "\n\n" or "") .. T(_("Retrieving image %1 / %2 …"), inum, nb_images), inum >= 2)
                 if not go_on then
                     cancelled = true
                     break
                 end
-                time_prev = now
             end
             local src = img.src
             if use_img_2x and img.src2x then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep!

Comment thread frontend/ui/wikipedia.lua Outdated
Comment on lines +1515 to +1516
local now = time.now()
if time.to_ms(now - time_prev) > 1000 then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They're purposefully identical except for the addition of (message and message ~= "" and message .. "\n\n" or "") .. on a single line. ;-)

I'd like to see it refactored a bit but it's not immediately obvious if it can be done without making things less clear. Luckily the part under if include_images may be doable.

@Frenzie

Frenzie commented Jan 17, 2025

Copy link
Copy Markdown
Member Author

@poire-z Good by you as is or do you prefer the skip_dismiss_check option from #13073 (comment)?

@poire-z

poire-z commented Jan 17, 2025

Copy link
Copy Markdown
Contributor

I prefer your skip_dismiss_check idea, which focuses on the issue (yielding too often for too long), rather than remotely-from-the-issue skipping numbers.

@Frenzie

Frenzie commented Jan 17, 2025

Copy link
Copy Markdown
Member Author

Needlessly refreshing all the time is also the issue imho. I would really prefer not to do that.

@Frenzie

Frenzie commented Jan 17, 2025

Copy link
Copy Markdown
Member Author

It still wastes a full second to do so. It's just not as bad as wasting four seconds.

@Frenzie Frenzie force-pushed the wikipedia-epub-benchmark branch from 044afcc to 4309237 Compare January 17, 2025 21:17
@poire-z

poire-z commented Jan 17, 2025

Copy link
Copy Markdown
Contributor

No user really ever complained about the time taken downloading stuff :) We are waiting, 20 or 30 seconds won't change much the wait. Seeing numbers skipped would feel like a bug. And if there is really a download of a specific image, the number you will see on display may still be an old one.
(If really you want it faster for newsdownloader which may do a lot of image downloads for a lot of articles in a batch, do it - but Wikipedia is on a single article basis, and I'd rather have it precise and smooth).

@Frenzie

Frenzie commented Jan 17, 2025

Copy link
Copy Markdown
Member Author

Well no, it's on Wikipedia that it bothers me. I don't care about NewsDownloader.

@Frenzie

Frenzie commented Jan 17, 2025

Copy link
Copy Markdown
Member Author

I believe I found an acceptable compromise. It avoids a slew of updates for small, fast image downloads without causing much overall delay.

@Frenzie Frenzie force-pushed the wikipedia-epub-benchmark branch from d964b7f to 00d9170 Compare January 17, 2025 21:52
@poire-z

poire-z commented Jan 17, 2025

Copy link
Copy Markdown
Contributor

I dunno, these 100ms and 1000ms feel random, we may still see skipped numbers - and maybe on eInk devices where a forceRepaint will always be over 100ms and we'll see them all or always only odd or even onces... Are you testing all this on your Kobo(s) ?

@Frenzie

Frenzie commented Jan 17, 2025

Copy link
Copy Markdown
Member Author

we may still see skipped numbers

Well of course, the very point is to skip. It's still a blurry mess and I'd rather go for 200. It's too fast and too much blurring of one number into another to even notice if it is or isn't skipping anything.

100ms and 1000ms feel random

1000 ms is what I considered to be a still reasonable response time. It represents the highest possible value. It's arbitrary in the sense that I rounded some 1200-1300 down to 1000, but not quite because that simultaneously accounts for extra large high quality images possibly taking a bit longer.

100 ms is a value you like; I think it's too low and should be 200.

@Frenzie

Frenzie commented Jan 17, 2025

Copy link
Copy Markdown
Member Author

@poire-z

poire-z commented Jan 17, 2025

Copy link
Copy Markdown
Contributor

Looks ok when it goes that fast without issue.
Still not a fan of that fuzzy overengineering, but if you care that bad about the speed, go ahead.

@Frenzie

Frenzie commented Jan 17, 2025

Copy link
Copy Markdown
Member Author

I'll add it in if or when it makes more sense. There's a natural limitation of at least 10-20 ms in there right now regardless. Amusingly it kind of feels faster even though it's actually slower.

Overengineering for posterity (not that it's difficult to write, but for the moment at least it's easier to apply).

diff --git a/frontend/ui/wikipedia.lua b/frontend/ui/wikipedia.lua
index 774c8fb5c..1a64a845d 100644
--- a/frontend/ui/wikipedia.lua
+++ b/frontend/ui/wikipedia.lua
@@ -1506,6 +1506,7 @@ abbr.abbr {
         local nb_images = #images
         local before_images_time = time.now()
         local time_prev = before_images_time
+        local time_prev_no_check = before_images_time
         for inum, img in ipairs(images) do
             -- Process can be interrupted every second between image downloads
             -- by tapping while the InfoMessage is displayed
@@ -1513,12 +1514,14 @@ abbr.abbr {
             local go_on
             if time.to_ms(time.since(time_prev)) > 1000 then
                 time_prev = time.now()
+                time_prev_no_check = time_prev
                 go_on = UI:info(T(_("Retrieving image %1 / %2 …"), inum, nb_images), inum >= 2)
                 if not go_on then
                     cancelled = true
                     break
                 end
-            else
+            elseif time.to_ms(time.since(time_prev_no_check)) > 100 then
+                time_prev_no_check = time.now()
                 UI:info(T(_("Retrieving image %1 / %2 …"), inum, nb_images), inum >= 2, true)
             end
             local src = img.src

…second

On Wikipedia articles with for example 30 images this reduces the download time from ~12 seconds to ~8 seconds (depending on image size and connection speed), which is fairly significant.
@Frenzie Frenzie force-pushed the wikipedia-epub-benchmark branch from 41fa79f to e2f3faa Compare January 17, 2025 23:13
@Frenzie Frenzie merged commit 923b690 into koreader:master Jan 17, 2025
@Frenzie Frenzie deleted the wikipedia-epub-benchmark branch January 17, 2025 23:15
Frenzie added a commit to Frenzie/koreader that referenced this pull request Jan 18, 2025
Frenzie added a commit that referenced this pull request Jan 19, 2025
Commodore64user pushed a commit to Commodore64user/KOReader_fork that referenced this pull request Jan 27, 2025
0xstillb pushed a commit to 0xstillb/koreader-thai that referenced this pull request May 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants