libTorrent 调参


「P2P」 - libTorrent 调参


初步翻译,不是很到位,先备注一下,后面再完善修正。


日志信息收集

  • 1. 在 警报掩码(alert mask)中启用警报(alert)

settings_pack p;

//您将收到每个活动的torrent大约每秒一次的stats_alert。

//这些警报包含自上次该警报到现在所有统计计数器(statistics counters)。

p.set_int(settings_mask::alert_mask, alert::stats_notification);

ses.apply_settings(p);


  • 2. 然后将警报全部打印输出到一个文件:

std::vector<alert> alerts;/<alert>

ses.pop_alerts(&alerts);

for (auto* a : alerts) {

std::cout << a->message() << "\\n";

}

如果要将常规警报和会话统计信息(session stats)分离开来,

可用 alert::category()筛选。

带有数据的警报 有session_stats_alert,且系统启动时发出session_log_alert,他包含所有的metrics. 记录这一行将大大简化对输出的解释。


  • 3. 日志信息的图表化

tools/parse_session_stats.py 可以解析结果文件并生成相关统计数据的图表。它需要gnuplot。



减少内存占用

预设函数 settings_pack min_memory_usage() 中可以获得一些降低内存占用的设置。

注意: 减少内存使用会影响性能。 应充分分析测试权衡是否值得使用。

假设libtorrent设置缓存大小为256blocks(256*16kB=4mB), 单个下载 典型的缓存使用情况:

read cache: 128.6 (2058 kiB)

write cache: 103.5 (1656 kiB)

receive buffers: 7.3 (117 kiB) # 与连接数成正比,最终受总连接数限制(总连接数默认200)

send buffers: 4.8 (77 kiB) # 与会话运行的上载槽(upload slots)数量呈正比,默认根据观察到的上载速率自动配置

hash temp: 0.001 (19 Bytes) # 取决于是否针对速度和内存进行了优化,这儿是针对内存进行了优化。


  • 1. 禁用磁盘缓存

libtorrent使用大量的内存用于磁盘缓存。 缓存是客户端使用多少内存的主因。

为了缓存保证内存的最大值,可以设置settings_pack::cache_size为0来禁用缓存。

若要使用缓存,可以仅禁用读缓存,settings_pack::use_read_cache设置为false。

注意: 禁用缓存会降低性能。慎重权衡。

  • 2. 删除种子(保存在内存中的种子)

添加到libtorrent中的种子不管处于什么状态始终会消耗内存。被添加的种子都会将整个torrent文件保存在内存中,它还会记住获取到的所有peer列表(除非设置上限,否则可能很长)。

它还保留了关于磁盘上有哪些块(block)和片段(piece)的信息(对于包含许多片段的torrent是非常重要的)。

若想最小化内存,则考虑从会话中(session)删除而不是暂停它们,当然这对有大量种子的会话效果比较明显。

缺点: 被删除的将不再被自动管理(暂停却则可以再有需要的时候自动激活播种)

  • 3. 套接字缓存区大小(socket buffer sizes)

我们可以设置该参数:settings_pack::recv_socket_buffer_size 和 settings_pack::send_socket_buffer_size

若设置低则吞吐量降低,连接延迟。但当有大量peer连接时确实可以为每个连接节省不少内存。该内存在进程中不可见,作用于套接字的内核内存。

  • 4.对等列表大小(settings_pack::max_peerlist_size)

默认最大值为4000.

简单计算下 对等列表的内存:

a.每个IPv4对等端条目(peer)使用32字节,最终每个种子使用125 kB(32*4000),暂停状态的种子同样适应。

b.若播下4个流行种子,则需要500KB.

c.若播下1000个流行种子,则约122MB

c.若播下1w个流行种子,则约1.2GB

d.若播下30w个流行种子,则约31GB

这还仅仅只是 对等列表消耗的内存

如果你内存不足,建议设置小一些,500可能就够了。


同样需要针对暂停种子进行限制, 将settings_pack::max_paused_peerlist_size设置得低可能更具有意义,因为你只需要几个对等点(peer)来启动,同时等待tracker和DHT给你新的。


降低这个数字的缺点是,如果你最终处在一个tracker长时间处于关闭状态的位置,那么你找到活动的对等点(peer)的唯一希望就是查看你所见过的所有对等列表。

拥有一个大的对等列表也将有助于提高启动时的性能,因为种子在开始与对等连接的同时连接到跟踪器(tracker)。

  • 5. 发送缓冲区水位线(send buffer watermark)

控制libtorrent何时请求磁盘I/O线程从磁盘读取块,并将其附加到对等方的发送缓冲区。

当发送缓冲区的字节数小于或等于settings_pack::send_buffer_watermark时,对等方将要求磁盘I/O线程发送更多数据。这里的折衷方案是:在发送缓冲区中有太多数据,从而浪费内存;在等待磁盘读取操作完成时,耗尽套接字,从而降低发送速率。

如果仅关注内存使用但不关心能否实现高发送速率,则可以将它设置为9字节。这将保证对于所有对等点,每次在发送缓冲区上的块(16KB) <= 1,这是发送缓冲区可能的最小内存量。

调整此设置时,应以最大发送速率作为参考基准。如果你有一个非常快的磁盘,你就不太可能看到性能受到影响。


  • 6. 减小可执行文件大小

6.1 通过禁用异常(-fno exceptions on GCC),您可以将可执行文件的大小减少45%。为了构建无异常支持,您需要修补boost的一部分。

6.2 禁用未使用的代码。

有许多TORRENT_*宏可以控制libtorrent中包含的功能。若不需要则可以不编译相关代码。

还有一个宏TORRENT_NO_DEPRECATE,若定义,也可清除一些遗弃过时用不着的代码。



高性能播种

在有海量种子的情况下,有两个主要问题: 性能(高发送速率) 和 可扩展性(低内存和CPU使用率的对等连接)。

  • 1. 文件池(settings_pack::file_pool_size)

高性能种子,文件打开/关闭的频率可能非常高,因此会造成很大的成本。因此,允许大文件描述符缓存可能是一个好主意。

且在您的进程中 设置可打开文件的数量的最大值(rlimit),要设置得尽可能大,以保持所有连接和文件处于打开状态。

struct rlimit limit;

getrlimit(RLIMIT_NOFILE,&limit);

printf("current file limit is %d\\n",limit.rlim_cur);

printf("hard file limit is %d\\n",limit.rlim_max);

limit.rlim_max = 50000;

setrlimit(RLIMIT_NOFILE,&limit);

printf("hard file limit is %d\\n",limit.rlim_max);


  • 2. 磁盘缓存

通常需要将缓存大小设置为尽可能高。settings_pack::cache_size 以block(16KB)为单位。

settings_pack::use_read_cache设置为true,cache_size才会生效。

为了增加读缓存命中率,请将设置_pack::cache_expiry设置为大的值。这不会降低任何东西,只要客户端只是种子,而不是下载任何种子。

“导向性读缓存”(settings_pack::guided_read_cache):存储在缓存中的读缓存线的大小取决于触发读操作的对等方的上载速率,基于该观点 慢的对等点不会占用缓存中不相称的空间。


如果假设缓存仅用作预读,并且其它对等点在缓存中时不会请求相同的块,则可以将读缓存设置为易失性。

这意味着从读缓存中请求的每个块都会立即被删除。这节省了大量的缓存空间,可以用作其他对等机的预读。settings_pack::volatile_read_cache设置为true。

  • 3. uTP-TCP混合模式

libtorrent支持uTP,uTP有一个基于延迟的拥塞控制器。为了避免单个TCP bt连接完全耗尽任何uTP连接,有一种混合模式算法。这将尝试检测uTP对等点上的拥塞,并限制TCP以避免它占用所有带宽。这平衡了两个协议之间的带宽资源。当在一个带宽非常丰富的网络上运行时,它实际上是无限的,这个算法不再是必要的,甚至可能对吞吐量有害。建议尝试session_setting::mixed_mode_算法,将其设置为settings_pack::prefer_tcp。此设置完全禁用平衡并取消限制所有连接。在典型的家庭连接上,这意味着uTP的任何好处都不会保留(调制解调器的发送缓冲区将始终是满的),而uTP连接在很大程度上会被TCP流量压扁。

  • 4.发送缓冲区低水位线

libtorrent对发送缓冲区使用低水位线来确定何时应从磁盘I/O子系统请求新的块,并将其追加到发送缓冲区。根据套接字的发送速率确定低水位线。

它需要足够大,以便在磁盘操作完成之前不耗尽套接字的发送缓冲区。

水位线被绑定到最大值,以避免缓冲区大小失控。

默认的最大发送缓冲区大小可能不足以维持很高的上载速率,您可能需要增加它。它是在settings_pack::send_buffer_watermark中以字节为单位指定的。

  • 5.对等点

首先,为了允许多个连接,请将全局连接限制设置为“高”,即“settings_pack::connections_limit”。还将上载速率限制设置为无穷大,settings_pack::upload_rate_limit,0表示无穷大。

在处理大量对等方时,稍微严格一点的超时可能是一个好主意,以便尽快摆脱挥之不去的连接。

一组相关设置: settings_pack::request_timeout、settings_pack::peer_timeout和settings_pack::inactivity_timeout。

对于传送系统至关重要的种子,您很可能希望允许来自同一IP的多个连接。这样,同一NAT后面的两个人可以同时使用该服务。settings_pack::allow_multiple_connections_per_ip.

要始终不阻塞(unchoke)对等方,请通过将settings_pack::choking_algorithm设置为fixed_slot_choker关闭自动不阻塞(unchoke),并通过settings_pack::unchoke_slots_limit将上载槽数设置为大值,或使用-1(这意味着无限)。

  • 6.种子限制

若要数千种子做种,您需要增加settings_pack::active_limit和settings_pack::active_seeds。

  • 7.SHA-1 哈希

当以非常高的速率下载时,CPU可能成为通过SHA-1传递每个下载字节的瓶颈。

为了能够并行计算SHA-1散列,在多核系统上,将settings_pack::aio_threads设置为libtorrent应该执行I/O和进行SHA-1散列的线程数。

有当线程接近饱和一个核心时,增加线程数才有意义。



延伸

为了在同时运行大量种子时更有效地使用libtorrent接口,可以将session::get_torrent_status()调用与session::post_torrent_updates()一起使用。

请记住,每次调用libtorrent并返回一些值时,都必须在向主网络线程发送消息时阻塞线程,然后等待响应。不返回任何数据的调用只会发送消息,然后立即返回,异步执行工作。一旦你到达几百个torrent,所需的时间可能会变得非常重要,这取决于你给每个种子进行多少次调用以及频率。

session::get_torrent_status()允许您在单个调用中查询所有torrent的状态。这实际上将遍历所有种子并运行提供的函数(其参数),以确定是否将其包含在返回的vector数组中。

若要使用session::post_torrent_updates()发布种子,需要设置标志flag_update_subscribe 。调用post_torrent_updates()时,state_update_alert警报将被发布,其中包含自上次调用此函数以来已更新的所有种子。客户端必须保留其所有种子的状态,并根据此警报进行更新。



基准化分析法

有一堆内置的libtorrent工具可以用来洞察它在做什么以及表现如何。在编译时定义预处理器符号来启用这些工具。

还有许多脚本可以解析日志文件并生成图形(需要gnuplot和python)。


分享到:


相關文章: