<p>These are things you can do to reduce the memory footprint of libtorrent. You get
some of this by basing your default <ttclass="docutils literal"><spanclass="pre">session_settings</span></tt> on the <ttclass="docutils literal"><spanclass="pre">min_memory_usage()</span></tt>
setting preset function.</p>
<p>Keep in mind that lowering memory usage will affect performance, always profile
and benchmark your settings to determine if it's worth the trade-off.</p>
<p>The typical buffer usage of libtorrent, for a single download, with the cache
size set to 256 blocks (256 * 16 kiB = 4 MiB) is:</p>
<p>In order to increase the disk cache hit rate, you can enable suggest messages based on
what's in the read cache. To do this, set <ttclass="docutils literal"><spanclass="pre">session_settings::suggest_mode</span></tt> to
<ttclass="docutils literal"><spanclass="pre">session_settings::suggest_read_cache</span></tt>. This will send suggest messages to peers
for the most recently used pieces in the read cache. This is especially useful if you
also enable explicit read cache, by settings <ttclass="docutils literal"><spanclass="pre">session_settings::explicit_read_cache</span></tt>
to the number of pieces to keep in the cache. The explicit read cache will make the
disk read cache stick, and not be evicted by cache misses. The explicit read cache
will automatically pull in the rarest pieces in the read cache.</p>
<p>Assuming that you seed much more data than you can keep in the cache, to a large
numbers of peers (so that the read cache wouldn't be useful anyway), this may be a
good idea.</p>
<p>When peers first connect, libtorrent will send them a number of allow-fast messages,
which lets the peers download certain pieces even when they are choked, since peers
are choked by default, this often triggers immediate requests for those pieces. In the
case of using explicit read cache and suggesting those pieces, allowing fast pieces
should be disabled, to not systematically trigger requests for pieces that are not cached
for all peers. You can turn off allow-fast by settings <ttclass="docutils literal"><spanclass="pre">session_settings::allowed_fast_set_size</span></tt>
to 0.</p>
<p>As an alternative to the explicit cache and suggest messages, there's a <em>guided cache</em>
mode. This means the size of the read cache line that's stored in the cache is determined
based on the upload rate to the peer that triggered the read operation. The idea being
that slow peers don't use up a disproportional amount of space in the cache. This
is enabled through <ttclass="docutils literal"><spanclass="pre">session_settings::guided_read_cache</span></tt>.</p>
<p>In cases where the assumption is that the cache is only used as a read-ahead, and that no
other peer will ever request the same block while it's still in the cache, the read
cache can be set to be <em>volatile</em>. This means that every block that is requested out of
the read cache is removed immediately. This saves a significant amount of cache space
which can be used as read-ahead for other peers. This mode should <strong>never</strong> be combined
with either <ttclass="docutils literal"><spanclass="pre">explicit_read_cache</span></tt> or <ttclass="docutils literal"><spanclass="pre">suggest_read_cache</span></tt>, since those uses opposite
strategies for the read cache. You don't want to on one hand attract peers to request
the same pieces, and on the other hand assume that they won't request the same pieces
and drop them when the first peer requests it. To enable volatile read cache, set
<ttclass="docutils literal"><spanclass="pre">session_settings::volatile_read_cache</span></tt> to true.</p>
<p>libtorrent supports <aclass="reference external"href="utp.html">uTP</a>, which has a delay based congestion controller. In order to
avoid having a single TCP bittorrent connection completely starve out any uTP connection,
there is a mixed mode algorithm. This attempts to detect congestion on the uTP peers and
throttle TCP to avoid it taking over all bandwidth. This balances the bandwidth resources
between the two protocols. When running on a network where the bandwidth is in such an
abundance that it's virtually infinite, this algorithm is no longer necessary, and might
even be harmful to throughput. It is adviced to experiment with the
<ttclass="docutils literal"><spanclass="pre">session_setting::mixed_mode_algorithm</span></tt>, setting it to <ttclass="docutils literal"><spanclass="pre">session_settings::prefer_tcp</span></tt>.
This setting entirely disables the balancing and unthrottles all connections. On a typical
home connection, this would mean that none of the benefits of uTP would be preserved
(the modem's send buffer would be full at all times) and uTP connections would for the most
<p>First of all, in order to allow many connections, set the global connection limit
high, <ttclass="docutils literal"><spanclass="pre">session::set_max_connections()</span></tt>. Also set the upload rate limit to
infinite, <ttclass="docutils literal"><spanclass="pre">session::set_upload_rate_limit()</span></tt>, passing 0 means infinite.</p>
<p>When dealing with a large number of peers, it might be a good idea to have slightly
stricter timeouts, to get rid of lingering connections as soon as possible.</p>
<p>There are a couple of relevant settings: <ttclass="docutils literal"><spanclass="pre">session_settings::request_timeout</span></tt>,
<ttclass="docutils literal"><spanclass="pre">session_settings::peer_timeout</span></tt> and <ttclass="docutils literal"><spanclass="pre">session_settings::inactivity_timeout</span></tt>.</p>
<p>For seeds that are critical for a delivery system, you most likely want to allow
multiple connections from the same IP. That way two people from behind the same NAT
can use the service simultaneously. This is controlled by
<td>This is a low level log of read and write operations, with
timestamps and file offsets. The file offsets are byte
offsets in the torrent (not in any particular file, in the
case of a multi-file torrent). This can be used as an
estimate of the physical drive location. The purpose of
this log is to identify the amount of seeking the drive has
to do.</td>
</tr>
</tbody>
</table>
<divclass="section"id="disk-io-thread-log">
<h3>disk_io_thread.log</h3>
<p>The structure of this log is simple. For each line, there are two columns, a timestamp and
the operation that was started. There is a special operation called <ttclass="docutils literal"><spanclass="pre">idle</span></tt> which means
it looped back to the top and started waiting for new jobs. If there are more jobs to
handle immediately, the <ttclass="docutils literal"><spanclass="pre">idle</span></tt> state is still there, but the timestamp is the same as the
next job that is handled.</p>
<p>Some operations have a 3:rd column with an optional parameter. <ttclass="docutils literal"><spanclass="pre">read</span></tt> and <ttclass="docutils literal"><spanclass="pre">write</span></tt> tells
you the number of bytes that were requested to be read or written. <ttclass="docutils literal"><spanclass="pre">flushing</span></tt> tells you
the number of bytes that were flushed from the disk cache.</p>
<p>This is an example excerpt from a log:</p>
<preclass="literal-block">
3702 idle
3706 check_fastresume
3707 idle
4708 save_resume_data
4708 idle
8230 read 16384
8255 idle
8431 read 16384
</pre>
<p>The script to parse this log and generate a graph is called <ttclass="docutils literal"><spanclass="pre">parse_disk_log.py</span></tt>. It takes
the log file as the first command line argument, and produces a file: <ttclass="docutils literal"><spanclass="pre">disk_io.png</span></tt>.
The time stamp is in milliseconds since start.</p>
<p>You can pass in a second, optional, argument to specify the window size it will average
the time measurements over. The default is 5 seconds. For long test runs, it might be interesting
to increase that number. It is specified as a number of seconds.</p>
<imgalt="disk_io.png"src="disk_io.png"/>
<p>This is an example graph generated by the parse script.</p>
</div>
<divclass="section"id="disk-buffers-log">
<h3>disk_buffers.log</h3>
<p>The disk buffer log tells you where the buffer memory is used. The log format has a time stamp,
the name of the buffer usage which use-count changed, colon, and the new number of blocks that are
in use for this particular key. For example:</p>
<preclass="literal-block">
23671 write cache: 18
23671 receive buffer: 3
24153 receive buffer: 2
24153 write cache: 19
24154 receive buffer: 3
24198 receive buffer: 2
24198 write cache: 20
24202 receive buffer: 3
24305 send buffer: 0
24305 send buffer: 1
24909 receive buffer: 2
24909 write cache: 21
24910 receive buffer: 3
</pre>
<p>The time stamp is in milliseconds since start.</p>
<p>To generate a graph, use <ttclass="docutils literal"><spanclass="pre">parse_disk_buffer_log.py</span></tt>. It takes the log file as the first
command line argument. It generates <ttclass="docutils literal"><spanclass="pre">disk_buffer.png</span></tt>.</p>
<p>This is an example graph generated by the parse script.</p>
</div>
<divclass="section"id="disk-access-log">
<h3>disk_access.log</h3>
<p>The disc access log has three fields. The timestamp (milliseconds since start), operation
and offset. The offset is the absolute offset within the torrent (not within a file). This
log is only useful when you're downloading a single torrent, otherwise the offsets will not
be unique.</p>
<p>In order to easily plot this directly in gnuplot, without parsing it, there are two lines
associated with each read or write operation. The first one is the offset where the operation
started, and the second one is where the operation ended.</p>
<p>Example:</p>
<preclass="literal-block">
15437 read 301187072
15437 read_end 301203456
16651 read 213385216
16680 read_end 213647360
25879 write 249036800
25879 write_end 249298944
26811 read 325582848
26943 read_end 325844992
36736 read 367001600
36766 read_end 367263744
</pre>
<p>The disk access log does not have any good visualization tool yet. There is however a gnuplot
file, <ttclass="docutils literal"><spanclass="pre">disk_access.gnuplot</span></tt> which assumes <ttclass="docutils literal"><spanclass="pre">disk_access.log</span></tt> is in the current directory.</p>
<imgalt="disk_access.png"src="disk_access.png"/>
<p>The density of the disk seeks tells you how hard the drive has to work.</p>
<p>By defining <ttclass="docutils literal"><spanclass="pre">TORRENT_STATS</span></tt> libtorrent will write a log file called <ttclass="docutils literal"><spanclass="pre">session_stats/<pid>.<sequence>.log</span></tt> which
is in a format ready to be passed directly into gnuplot. The parser script <ttclass="docutils literal"><spanclass="pre">parse_session_stats.py</span></tt>
<p>All disk operations are funneled through a separate thread, referred to as the disk thread.
The main interface to the disk thread is a queue where disk jobs are posted, and the results
of these jobs are then posted back on the main thread's io_service.</p>
<p>A disk job is essentially one of:</p>
<olclass="arabic simple">
<li>write this block to disk, i.e. a write job. For the most part this is just a matter of sticking the block in the disk cache, but if we've run out of cache space or completed a whole piece, we'll also flush blocks to disk. This is typically very fast, since the OS just sticks these buffers in its write cache which will be flushed at a later time, presumably when the drive head will pass the place on the platter where the blocks go.</li>
<li>read this block from disk. The first thing that happens is we look in the cache to see if the block is already in RAM. If it is, we'll return immediately with this block. If it's a cache miss, we'll have to hit the disk. Here we decide to defer this job. We find the physical offset on the drive for this block and insert the job in an ordere queue, sorted by the physical location. At a later time, once we don't have any more non-read jobs left in the queue, we pick one read job out of the ordered queue and service it. The order we pick jobs out of the queue is according to an elevator cursor moving up and down along the ordered queue of read jobs. If we have enough space in the cache we'll read read_cache_line_size number of blocks and stick those in the cache. This defaults to 32 blocks.</li>
</ol>
<p>Other disk job consist of operations that needs to be synchronized with the disk I/O, like renaming files, closing files, flushing the cache, updating the settings etc. These are relatively rare though.</p>