sa3paleasm / libtorrent

Automatically exported from code.google.com/p/libtorrent
Other
0 stars 0 forks source link

torrent_handle::auto_managed is slow #696

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
In qbittorrent we've got a request from user that bulk pausing or resuming of 
many (>2000) torrents is slow.

As I've found out the main offender is function 
recalculate_auto_managed_torrents(), that is called once for every torrent 
paused/resumed. It is called because qbittorrent calls 
torrent_handle::auto_managed() for every paused/resumed torrent. Although the 
work is done not in UI thread, we enqueue to/wait for libtorrent thread so 
often that effectively if libtorrent thread is busy the UI thread hangs too.

For details you could visit: 
https://github.com/qbittorrent/qBittorrent/issues/1203

The question is could auto_managed() be speed up or could new API for bulk 
pause/resume added?

Original issue reported on code.google.com by vanya...@gmail.com on 16 Nov 2014 at 1:01

GoogleCodeExporter commented 8 years ago
one way to speed it up would be to consolidate all the slow calls to 
auto_managed(), make those calls back-to-back first, then make all the calls to 
change the state (which are non-blocking).

Is there any way to avoid the blocking call at all? If you're pausing all 
torrents for instance, I would imagine that you just want to call 
auto_managed(false) and pause().

I can imagine that if you just have a toggle function, you would need to query 
the current state first. Is that the case?

anyway, if you make all the non-blocking, state-changing calls back-to-back, 
libtorrent will be able to defer the call to 
recalculate_auto_managed_torrents() more more efficiently, to just call that 
once or twice.

Original comment by arvid.no...@gmail.com on 17 Nov 2014 at 6:15

GoogleCodeExporter commented 8 years ago
btw. I assumed that you are referring to the auto_managed() overload that does 
not take any arguments, which queries the current state of the torrent.

The overload that takes a bool and _sets_ the state, is still fast, right?

Original comment by arvid.no...@gmail.com on 17 Nov 2014 at 6:18

GoogleCodeExporter commented 8 years ago
> I assumed that you are referring to the auto_managed() overload that does not 
take any arguments, which queries the current state of the torrent.

No, sorry. I was talking about overload auto_managed(bool). This overload 
itself is fast, but it queues recalculate_auto_managed_torrents() which is 
slow. After every (fast) call auto_managed(bool), libtorrent does a (slow) 
recalculate_auto_managed_torrents process. And at that time if we do blocking 
call to libtorrent UI would hang.

We do blocking UI mostly because of issue #684. But even if we don't. Having 
libtorrent busy for a long time is bad. If libtorrent thread is busy, and if 
some action does a blocking call, UI will still hang.

I think there are two way to solve the problem:
1. Make recalculate_auto_managed_torrents incremental. So it does little work, 
if only one torrent is changed.
2. Coalesce many sequential recalculate_auto_managed_torrents into one. In that 
case perhaps we need a call to set auto_managed flag on many torrents at once.

I like the first solution more.

Original comment by vanya...@gmail.com on 17 Nov 2014 at 10:51

GoogleCodeExporter commented 8 years ago
right. currently libtorrent implements (2), so I guess there's a bug somewhere. 
There is a trade-off between coalescing more events and having low latency. 
Currently, latency is favored. When the network thread receives the command to 
pause a torrent it posts another message onto its queue which will trigger the 
recalculate auto-managed. Everything that's already on the queue will hence be 
executed first before recalculate is called.

However, if you have any ping-pong dependencies where you wait for libtorrent 
in between the calls, then you'll get poor performance.

Original comment by arvid.no...@gmail.com on 17 Nov 2014 at 5:39

GoogleCodeExporter commented 8 years ago
> if you have any ping-pong dependencies where you wait for libtorrent in 
between the calls, then you'll get poor performance.

That is the case. Looking at profile 
https://cloud.githubusercontent.com/assets/286877/5059717/bb905a06-6d37-11e4-809
f-86785bd9aa6a.png qbittorrent calls torrent_handle::info_hash() in between 
auto_managed() calls.

But even if there was no info_hash() calls. As I understand 
boost::asio::io_service is FIFO, so if there is queued call to 
session_impl::on_trigger_auto_manage(), torrent::auto_managed(bool) won't be 
executed before session_impl::on_trigger_auto_manage() finishes it work.

Original comment by vanya...@gmail.com on 18 Nov 2014 at 3:06

GoogleCodeExporter commented 8 years ago
I did commit that patch to make info_hash() fast. Do you know if there are any 
other blocking calls made in between the calls to  auto_managed(bool)?

What would be run on the network thread is probably:

auto_managed(bool)  -> post recalculate_auto_managed()
recalculate_auto_managed() (takes a long time, during this time the qbt thread 
posts all the remaining auto_managed(bool) calls)
auto_managed(bool) -> post recalculate_auto_managed(), but this is at the end 
of the queue, and won't get executed until all remaining auto_managed(bool) 
have been executed.

Original comment by arvid.no...@gmail.com on 18 Nov 2014 at 10:00

GoogleCodeExporter commented 8 years ago
> What would be run on the network thread is probably:

> auto_managed(bool)  -> post recalculate_auto_managed()
> recalculate_auto_managed() (takes a long time, during this time the qbt
> thread posts all the remaining auto_managed(bool) calls)
> auto_managed(bool) -> post recalculate_auto_managed(), but this is at
> the end of the queue, and won't get executed until all remaining
> auto_managed(bool) have been executed.

I thought about that a bit. Yes you are right this should help. I'll test this. 
Thank you.

Original comment by vanya...@gmail.com on 19 Nov 2014 at 8:57