Run `findChapterDir` in parallel
Follow-up of #728
Any reason to not use async?
Any reason to not use
async?
It does use async under the hood.
https://github.com/arrow-kt/arrow/blob/2860c124f9628af4cdf1cb2bddae2f7484697cf4/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParMap.kt#L33
Yeah why not just use async directly?
Yeah why not just use
asyncdirectly?
I don't get what you mean, can you elaborate on that?
Yeah why not just use
asyncdirectly?I don't get what you mean, can you elaborate on that?
Why pull in arrow for a very simple function that is feasible to write/copy in Mihon with normal Kotlin coroutines?
Edit: it looks like it's literally just a map of asyncs that are awaited, and then notNull filtered
Yeah why not just use
asyncdirectly?I don't get what you mean, can you elaborate on that?
Why pull in arrow for a very simple function that is feasible to write/copy in Mihon with normal Kotlin coroutines?
Edit: it looks like it's literally just a map of asyncs that are awaited, and then notNull filtered
Why reinvent the wheel? And R8 will take care of the unused code.
Why reinvent the wheel?
Just because you have to write a little more to replicate the same thing as a helper method in a third-party library doesn't mean you have reinvented the wheel.
Why reinvent the wheel?
Just because you have to write a little more to replicate the same thing as a helper method in a third-party library doesn't mean you have reinvented the wheel.
w/e, this PR is just a POC and anyone can take the idea and reimplement it in their desired way if they want.
As requested in https://github.com/mihonapp/mihon/issues/705#issuecomment-2088015058, I have run a benchmark similar to the one in https://github.com/mihonapp/mihon/issues/705#issuecomment-2087787294 on the current main branch (including now https://github.com/mihonapp/mihon/pull/728) with and without this PR on top.
For downloading 50 new chapters with 1,000 chapters already downloaded, and measuring the time from clicking the download button (with 50 chapters selected) to the time that the first HTTP request is started:
- Without this PR: 5.29s
- With this PR: 2.18s
So it does seem to be a substantial performance improvement.
There may be a bug though, I opened the download queue while some downloads were executing and received the following crash which seems potentially related to the changes in this PR:
java.lang.Throwable: java.util.ConcurrentModificationException
at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:797)
at java.util.LinkedHashMap$LinkedKeyIterator.next(LinkedHashMap.java:819)
at kotlinx.serialization.internal.CollectionLikeSerializer.serialize(CollectionSerializers.kt:69)
at kotlinx.serialization.protobuf.internal.ProtobufEncoder.encodeSerializableValue(ProtobufEncoding.kt:138)
at kotlinx.serialization.protobuf.internal.ProtobufTaggedEncoder.encodeSerializableElement(ProtobufTaggedEncoder.kt:135)
at eu.kanade.tachiyomi.data.download.MangaDirectory.write$Self$app_devDebug(DownloadCache.kt:438)
at eu.kanade.tachiyomi.data.download.MangaDirectory$$serializer.serialize(DownloadCache.kt:438)
at eu.kanade.tachiyomi.data.download.MangaDirectory$$serializer.serialize(DownloadCache.kt:438)
at kotlinx.serialization.protobuf.internal.ProtobufEncoder.encodeSerializableValue(ProtobufEncoding.kt:138)
at kotlinx.serialization.protobuf.internal.ProtobufTaggedEncoder.encodeSerializableElement(ProtobufTaggedEncoder.kt:135)
at kotlinx.serialization.internal.KeyValueSerializer.serialize(Tuples.kt:31)
at kotlinx.serialization.protobuf.internal.ProtobufEncoder.encodeSerializableValue(ProtobufEncoding.kt:138)
at kotlinx.serialization.protobuf.internal.ProtobufTaggedEncoder.encodeSerializableElement(ProtobufTaggedEncoder.kt:135)
at kotlinx.serialization.internal.CollectionLikeSerializer.serialize(CollectionSerializers.kt:69)
at kotlinx.serialization.protobuf.internal.ProtobufEncoder.serializeMap(ProtobufEncoding.kt:155)
at kotlinx.serialization.protobuf.internal.ProtobufEncoder.encodeSerializableValue(ProtobufEncoding.kt:135)
at kotlinx.serialization.protobuf.internal.ProtobufTaggedEncoder.encodeSerializableElement(ProtobufTaggedEncoder.kt:135)
at eu.kanade.tachiyomi.data.download.SourceDirectory.write$Self$app_devDebug(DownloadCache.kt:428)
at eu.kanade.tachiyomi.data.download.SourceDirectory$$serializer.serialize(DownloadCache.kt:428)
at eu.kanade.tachiyomi.data.download.SourceDirectory$$serializer.serialize(DownloadCache.kt:428)
at kotlinx.serialization.protobuf.internal.ProtobufEncoder.encodeSerializableValue(ProtobufEncoding.kt:138)
at kotlinx.serialization.protobuf.internal.ProtobufTaggedEncoder.encodeSerializableElement(ProtobufTaggedEncoder.kt:135)
at kotlinx.serialization.internal.KeyValueSerializer.serialize(Tuples.kt:31)
at kotlinx.serialization.protobuf.internal.ProtobufEncoder.encodeSerializableValue(ProtobufEncoding.kt:138)
at kotlinx.serialization.protobuf.internal.ProtobufTaggedEncoder.encodeSerializableElement(ProtobufTaggedEncoder.kt:135)
at kotlinx.serialization.internal.CollectionLikeSerializer.serialize(CollectionSerializers.kt:69)
at kotlinx.serialization.protobuf.internal.ProtobufEncoder.serializeMap(ProtobufEncoding.kt:155)
at kotlinx.serialization.protobuf.internal.ProtobufEncoder.encodeSerializableValue(ProtobufEncoding.kt:135)
at kotlinx.serialization.protobuf.internal.ProtobufTaggedEncoder.encodeSerializableElement(ProtobufTaggedEncoder.kt:135)
at eu.kanade.tachiyomi.data.download.RootDirectory.write$Self$app_devDebug(DownloadCache.kt:418)
at eu.kanade.tachiyomi.data.download.RootDirectory$$serializer.serialize(DownloadCache.kt:418)
at eu.kanade.tachiyomi.data.download.RootDirectory$$serializer.serialize(DownloadCache.kt:418)
at kotlinx.serialization.protobuf.internal.ProtobufEncoder.encodeSerializableValue(ProtobufEncoding.kt:138)
at kotlinx.serialization.protobuf.ProtoBuf.encodeToByteArray(ProtoBuf.kt:137)
at eu.kanade.tachiyomi.data.download.DownloadCache$updateDiskCache$1.invokeSuspend(DownloadCache.kt:465)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:111)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:99)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:585)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:802)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:706)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:693)
Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@1a39b91, Dispatchers.IO]
There may be a bug though, I opened the download queue while some downloads were executing and received the following crash which seems potentially related to the changes in this PR:
Interesting, but that does not seem to be caused by this PR, we don't update download cache here.
rootDownloadsDirLock should be acquired here https://github.com/mihonapp/mihon/blob/dbcc4a7d714cf36b48a41d6074ef671f3c039823/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt#L400