frostwire / frostwire-jlibtorrent

A swig Java interface for libtorrent by the makers of FrostWire. Develop libtorrent based apps with the joy of coding in Java.
http://www.frostwire.com
MIT License
451 stars 138 forks source link

Question: What’s the best way to limit resource usage or decrease the priority of the torrent download thread? #291

Closed pvishnyakov closed 3 months ago

pvishnyakov commented 3 months ago

I have an app for Android which is using custom DataSource for Exoplayer and the stream is supplied from the file being downloaded in real time with jlibtorrent, device is Nvidia Shield Pro. For 1080p files the performance is ok, for 2160p (especially high bitrates) the playback is stuttering during the download but everything is fine after the file is downloaded and just seeding. Profiling doesn’t show anything special during the playback freeze - no CPU surge, no extra memory usage, all the same as before and after, just playback freezing for unknown reason. Seems that disk I/O is blocked but I don’t know how to check it. I can control it by limiting the number of connections allowed, but if set too low - the download speed is suffering.

Is it possible to control the download thread priority somehow? I’d like to set it to the lowest possible and see how it will affect the performance. Or maybe you have another idea how to keep the download speed but limit the device resource usage?

master255 commented 3 months ago

This problem has been solved in Media Library. You can find this and other applications in DHTMarket

pvishnyakov commented 3 months ago

I’m using the latest Media library. Yesterday checked, Exoplayer:Loader thread is conflicting with jlibtorrent download thread for disk I/O, this is the reason. Actually, sometimes the download thread is conflicting with itself when too many pieces are arriving in burst. Wrapping the download into external Thread and setting its priority to minimum doesn’t solve the problem. Need to find a way to distribute the writing, any ideas?

master255 commented 3 months ago

If you are using Media Library, this setting should help you

https://github.com/frostwire/frostwire-jlibtorrent/assets/5380115/9321c9f7-76b8-42db-8a96-74e245891b5f

pvishnyakov commented 3 months ago

I've found a solution, need to track the playback state (playing or buffering) and adjust settings_pack.aio_threads accordingly - set to 0 when playing and >0 when buffering (to load and fill the buffer faster). This way there is no competition for I/O between download thread(s) and Exoplayer Loader thread.

master255 commented 3 months ago

No. Not right.

gubatron commented 3 months ago

Seems that disk I/O is blocked but I don’t know how to check it. I can control it by limiting the number of connections allowed, but if set too low - the download speed is suffering. Is it possible to control the download thread priority somehow? I’d like to set it to the lowest possible and see how it will affect the performance.

Ticket has been closed, but would like to leave a few comments in case somebody wants to know how this actually works and what to do about it as jlibtorrent user.

short answer: Take a look at TorrentHandle which has several priority handling methods.

Like you said you can limit the number of connections/transfers with libtorrent's settings, each download (TorrentHandle) has to wait in line if you have more ongoing transfers than the set limit, so you can move them up and down in a priority queue.

You can also get more granular and determine the priority of the pieces being downloaded with prioritizePieces(Priority[] priorities). We've wrapped Priority as an enum, from the lowest IGNORE (0) to SEVEN (7). The Priority[] array corresponds to the number of pieces in the torrent handle. So if you're downloading/streaming video, you're probably going to want to prioritize the starting pieces of the file first and then have an algorithm which updates the priorities as the user is playing the file to move higher priority not too far ahead of the current playback piece.

threads

There's no such a thing as a "Download" thread per torrent handle. You won't be able to prioritize a "download thread" per se, dealing with the provided mechanisms by TorrentHandle is all you can do in this respect.

libtorrent's architecture instead works with the following threads, which work by handling alerts and/or asynchronous functions being sent to them:

Main Thread

The thread on which libtorrent runs to then launch the 2 other important threads, the network and disk i/o threads.

Network I/O thread

Handles all network communications, sockets, sending/receiving data packets, peer connections.

Disk I/O thread

Handles disk-related operations, writing to disk, flushing completed pieces.

pvishnyakov commented 3 months ago

Piece and File priorities are important to download pieces in the proper order but it has nothing to do with this particular case.

As I have noticed, the download performance and the corresponding I/O load are depending on many things, mostly on the number of the concurrent connections, especially for 4K media (somehow the bitrate is less important, small files with high resolution are affected while big FullHD files are ok).

The first task was to find the right balance between the download speed and disk utilization, so I'm adjusting the connections when switching between buffering and playback, in my case - setting connections_limit to 500 and 50 respectively, connections_slack to 200 and 50, connection_speed to 100 and 20. upload_rate_limit is also adjusted, unlimited when buffering (or paused), 512Kbps when playing. Encryption is forcibly disabled everywhere to offload the CPU.

But all abovementioned measures were not enough to get rid of playback stuttering so I continued to search. I have found that only download is causing the problem, seeding the file is ok. I have wrapped my downloader into the Thread with specific name and this allowed me to track its activity in the Profiler. There are always 2 + aio_treads number of threads that are doing some torrent-related tasks and it's visible when these threads are starting to compete with Exoplayer Loader thread, that's why my first idea was to adjust the priorities - I did it for the wrapping Thread but not sure if it has any effect.

Anyway, what I did with priorities were not enough, so I've tried to set the aio_threads to 0 to limit the number of concurrent torrent tasks, after that I have only 1 active torrent thread which is doing all the job (and 1 more which is always idle, according to the profiler). This finally helped, along with all other means, including priority adjustment. Now I'm adjusting aio_threads as well, setting it to number of CPU cores while buffering and to 0 when playing.

There are some freezing still, so I'll keep researching to improve the solution. For example, I have replaced the RandomAccessFile to BufferedInputStream in the DataSource, then implemented the memory-mapped file reading - improvement is obvious.

master255 commented 3 months ago

@pvishnyakov https://github.com/frostwire/frostwire-jlibtorrent/issues/291#issuecomment-2152248056

gubatron commented 3 months ago

if your goal is solely streaming and reducing or eliminating a disk i/o bottleneck, perhaps you want to look at implementing a custom disk storage which doesn't actually write to disk and instead writes to a "virtual" file in memory

pvishnyakov commented 3 months ago

if your goal is solely streaming and reducing or eliminating a disk i/o bottleneck, perhaps you want to look at implementing a custom disk storage which doesn't actually write to disk and instead writes to a "virtual" file in memory

Actually this is what I want to do since the very beginning, but in case of jlibtorrent I cannot do it in Java, I have to do it in C++/JNI - please correct me if I'm wrong. I have implemented the in-memory storage with another torrent engine (atomashpolsky/bt), now experimenting with it, but this engine is not so stable on Android because of guice

gubatron commented 3 months ago

Actually this is what I want to do since the very beginning, but in case of jlibtorrent I cannot do it in Java, I have to do it in C++/JNI

Just checked, you're right, we did not expose custom storage/temp storage apis