-
Notifications
You must be signed in to change notification settings - Fork 495
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Performance issues caused by UniFile findFile implementation #705
Comments
I see that the reason for this "document tree" abstraction may be that it is what is returned by the directory picker / permissions model that is currently in use by Mihon: mihon/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt Lines 107 to 120 in f27ca3b
For example, on my device, the returned URI is I guess the questions are:
I will do some research. |
Being able to support other storage locations is just a feature of SAF, which has to be used due to scoped storage. |
I have this issue, but in addition to causing poor performance it also causes crashes. I cannot even open the application without deleting some chapters through a file manager. |
I have gotten a local development environment set up and am setting about adjusting UniFile to use more efficient DocumentContract methods to implement the commonly used filesystem operations. Given that Mihon uses UniFile methods that have O(n) performance instead of O(1) in many different places, I think fixing the issue at the library level will be the most fruitful. Pull request to follow. |
I happen to disagree with you on that point, but let's keep the discussion on-topic and related to the bug report in this thread. |
#728 and tachiyomiorg/UniFile#2, together, fix the reported problem. There may be other areas as well where Mihon's performance suffers due to unoptimized usage of SAF, but this at least remediates a number of problem hotspots by fixing a commonly used library subroutine - for example, this also improves the performance of |
tachiyomiorg/UniFile#2 is definitely a good improvement, but do you have benchmark results of that vs lifting Actually, I think we could consider running them in parallel as they are rather independent. |
From checking back through the git history of the project, UniFile was first added to the project in 2016 with 6f29716 (archive of the PR). Since then, there have been changes to both it and its uses, the last of which I could find was about 5 months ago with 46aeab9, a74a689, and ca54984 in late November 2023. I wasn't even aware of Tachiyomi at that point, let alone involved with it, so I have no insights to share. |
Sure! To perform the following tests, I used a source with 1,000 chapters already downloaded, and then initiated the download of 50 additional chapters. Based on timestamps of added logging lines, I determined the total time from initiating the add-to-queue operation to the first chapter beginning to download. Here are the results for various implementations:
The file list precomputation I did in a very silly way just to get the performance numbers - it would have to be redone more intelligently if we move forward with that option. Here is what I did: Patch against
|
Nice! That looks promising. |
I will run some tests to verify that all visible performance issues relating to large numbers of downloaded chapters are now resolved, and if so, close this issue. Otherwise, will file more PRs to address the other issues. |
I compiled a release build and, let me tell you, the difference is unreal. Reader performance has gone from freezing the UI for 10-15 seconds every time you flip a page, to no perceptible delay. There are further optimizations that we could make, for example #731 and the caching I mentioned in point 4 under #705 (comment), but I think this is good enough to mitigate almost all of the user-visible problems in most cases. 🎉 |
Steps to reproduce
The performance of Mihon degrades linearly with the number of downloaded chapters for a specific manga. You can experience this behavior by selecting a manga with a large number of chapters and downloading about 1,000 of them. You will then observe the following behavior:
Expected behavior
Adding any number of chapters to the download queue should take no observable time, because this requires at most one filesystem operation to list existing downloaded chapters, no network calls, and no significant data processing.
Loading an already-downloaded chapter from disk should take no observable time unless the chapter is large, because this requires at most one filesystem operation to read the chapter images from disk.
Actual behavior
Mihon uses an unusual library called UniFile for its filesystem operations. This is described as a fork of the Android library DocumentFile, which describes itself as follows:
The substantial overhead is difficult to overstate, it turns out, when dealing with directories that have a large number of files. Observe the following. When Mihon adds chapters to the download queue, it invokes the
Downloader.queueChapters
method, which uses theDownloadProvider.findChapterDir
method to filter out chapters that have already been downloaded:mihon/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt
Lines 276 to 277 in f27ca3b
The
DownloadProvider.findChapterDir
iterates through four possible filepath structures returned by theDownloadProvider.getValidChapterDirNames
method (handling both a.cbz
suffix and an underscore prefix), and invokes theUniFile.findFile
method for each one:mihon/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt
Lines 84 to 86 in f27ca3b
The intent of this method, which is documented upstream here, is to check for the existence of a file in a directory. Normally, this operation would be near-instantaneous, as checking for file existence is O(1). However, because the DocumentFile interface acts as an abstraction over a large number of virtual document store providers, some of which do not support random access, the implementation of
findFile
actually lists every child of the directory individually, fetches each of their attributes, and then iterates over the returned list to see if the desired file is found:https://github.com/tachiyomiorg/UniFile/blob/7c257e1c642caa5ec7a41c640d61cd096f7f5c2e/library/src/main/java/com/hippo/unifile/TreeDocumentFile.java#L236-L241
This has the consequence that the performance of the method degrades linearly according to how many files exist in the directory - and it is called four times per chapter. Thus, with 1,000 chapters already downloaded, and 100 requested by the user to download, only 100 (or 400, to handle backward compatibility) system calls should be needed to check each chapter. However, due to the
findFile
implementation, a total of 400,000 system calls are made instead, because each chapter traverses the entire download directory. In practice, this can mean a delay of 15 minutes or more, with heavy IO usage on the device which quickly drains battery life.Since the original issue and pull request tracker for Tachiyomi has been lost, it is unclear to me why this library was introduced in the first place, or if anybody else noticed this significant shortcoming. However, the problem completely blocks me from using the download system in Mihon at all for manga that have more than a thousand chapters, so I am willing to do the necessary work to identify an appropriate fix for this performance issue, with accompanying documentation and/or tests, that maintains an acceptable level of backwards compatibility.
Any insight from the maintainers would be appreciated, especially in terms of the rationale for the existing system around UniFile.
Crash logs
No response
Mihon version
f27ca3b
Android version
Android 14 (GrapheneOS)
Device
Google Pixel 6a
Other details
This issue causes #669.
I have verified through added logging and timing that the code path I mentioned is responsible for the performance issues around the download queue. I have not verified specifically that it is also responsible for the reader slowdown, but this seems almost certainly to be the case to me. (My testing is impaired by the fact that downloads are so slow I cannot complete them on the development version of the app.)
Acknowledgements
The text was updated successfully, but these errors were encountered: