提高 Linux上socket 性能的技巧 為 Bandwidth Delay Product 調(diào)節(jié) TCP 窗口
本文內(nèi)容包括Linux下對于 Sockets API 的使用、兩個(gè)可以提高性能的 socket 選項(xiàng)以及 GNU/Linux 優(yōu)化。為了能夠開發(fā)性能卓越的應(yīng)用程序,請遵循以下技巧:最小化報(bào)文傳輸?shù)难訒r(shí);最小化系統(tǒng)調(diào)用的負(fù)載;為 Bandwidth Delay Product 調(diào)節(jié) TCP 窗口;動(dòng)態(tài)優(yōu)化 GNU/Linux TCP/IP 棧。
為 Bandwidth Delay Product 調(diào)節(jié) TCP 窗口:
TCP 的性能取決于幾個(gè)方面的因素。兩個(gè)最重要的因素是鏈接帶寬(link bandwidth)(報(bào)文在網(wǎng)絡(luò)上傳輸?shù)乃俾?和 往返時(shí)間(round-trip time) 或 RTT(發(fā)送報(bào)文與接收到另一端的響應(yīng)之間的延時(shí))。這兩個(gè)值確定了稱為 Bandwidth Delay Product(BDP)的內(nèi)容。
給定鏈接帶寬和 RTT 之后,您就可以計(jì)算出 BDP 的值了,不過這代表什么意義呢?BDP 給出了一種簡單的方法來計(jì)算理論上最優(yōu)的 TCP socket 緩沖區(qū)大小(其中保存了排隊(duì)等待傳輸和等待應(yīng)用程序接收的數(shù)據(jù))。如果緩沖區(qū)太小,那么 TCP 窗口就不能完全打開,這會(huì)對性能造成限制。如果緩沖區(qū)太大,那么寶貴的內(nèi)存資源就會(huì)造成浪費(fèi)。如果您設(shè)置的緩沖區(qū)大小正好合適,那么就可以完全利用可用的帶寬。下面我們來看一個(gè)例子:
BDP = link_bandwidth * RTT
如果應(yīng)用程序是通過一個(gè) 100Mbps 的局域網(wǎng)進(jìn)行通信,其 RRT 為 50 ms,那么 BDP 就是:
100MBps * 0.050 sec / 8 = 0.625MB = 625KB
注意:此處除以 8 是將位轉(zhuǎn)換成通信使用的字節(jié)。
因此,我們可以將 TCP 窗口設(shè)置為 BDP 或 1.25MB。但是在 Linux 2.6 上默認(rèn)的 TCP 窗口大小是 110KB,這會(huì)將連接的帶寬限制為 2.2MBps,計(jì)算方法如下:
throughput = window_size / RTT
110KB / 0.050 = 2.2MBps
如果使用上面計(jì)算的窗口大小,我們得到的帶寬就是 12.5MBps,計(jì)算方法如下:
625KB / 0.050 = 12.5MBps
差別的確很大,并且可以為 socket 提供更大的吞吐量。因此現(xiàn)在您就知道如何為您的 socket 計(jì)算最優(yōu)的緩沖區(qū)大小了。但是又該如何來改變呢?
解決方案
Sockets API 提供了幾個(gè) socket 選項(xiàng),其中兩個(gè)可以用于修改 socket 的發(fā)送和接收緩沖區(qū)的大小。清單 2 展示了如何使用 SO_SNDBUF 和 SO_RCVBUF 選項(xiàng)來調(diào)整發(fā)送和接收緩沖區(qū)的大小。
注意:盡管 socket 緩沖區(qū)的大小確定了通告 TCP 窗口的大小,但是 TCP 還在通告窗口內(nèi)維護(hù)了一個(gè)擁塞窗口。因此,由于這個(gè)擁塞窗口的存在,給定的 socket 可能永遠(yuǎn)都不會(huì)利用最大的通告窗口。
清單 2. 手動(dòng)設(shè)置發(fā)送和接收 socket 緩沖區(qū)大小
int ret, sock, sock_buf_size;
sock = socket( AF_INET, SOCK_STREAM, 0 );
sock_buf_size = BDP;
ret = setsockopt( sock, SOL_SOCKET, SO_SNDBUF,
(char *)&sock_buf_size, sizeof(sock_buf_size) );
ret = setsockopt( sock, SOL_SOCKET, SO_RCVBUF,
(char *)&sock_buf_size, sizeof(sock_buf_size) );
在 Linux 2.6 內(nèi)核中,發(fā)送緩沖區(qū)的大小是由調(diào)用用戶來定義的,但是接收緩沖區(qū)會(huì)自動(dòng)加倍。您可以進(jìn)行 getsockopt 調(diào)用來驗(yàn)證每個(gè)緩沖區(qū)的大小。
巨幀(jumbo frame)
我們還可以考慮將包的大小從 1,500 字節(jié)修改為 9,000 字節(jié)(稱為巨幀)。在本地網(wǎng)絡(luò)中可以通過設(shè)置最大傳輸單元(Maximum Transmit Unit,MTU)來設(shè)置巨幀,這可以極大地提高性能。
就 window scaling 來說,TCP 最初可以支持最大為 64KB 的窗口(使用 16 位的值來定義窗口的大小)。采用 window scaling(RFC 1323)擴(kuò)展之后,您就可以使用 32 位的值來表示窗口的大小了。GNU/Linux 中提供的 TCP/IP 棧可以支持這個(gè)選項(xiàng)(以及其他一些選項(xiàng))。
提示:Linux 內(nèi)核還包括了自動(dòng)對這些 socket 緩沖區(qū)進(jìn)行優(yōu)化的能力(請參閱下面 表 1 中的 tcp_rmem 和 tcp_wmem),不過這些選項(xiàng)會(huì)對整個(gè)棧造成影響。如果您只需要為一個(gè)連接或一類連接調(diào)節(jié)窗口的大小,那么這種機(jī)制也許不能滿足您的需要了。
使用 Sockets API,我們可以開發(fā)客戶機(jī)和服務(wù)器應(yīng)用程序,它們可以在本地網(wǎng)絡(luò)上進(jìn)行通信,也可以通過 Internet 在全球范圍內(nèi)進(jìn)行通信。與其他 API 一樣,您可以通過一些方法使用 Sockets API,從而提高 Socket 的性能,或者限制 Socket 的性能。
【編輯推薦】
- 2.1.3 Socket服務(wù)器
- TCP Socket之linux實(shí)現(xiàn)
- Linux操作系統(tǒng)下Socket編程地址結(jié)構(gòu)介紹
- 16.5.4 socket編程實(shí)例(2)
- 16.5.4 socket編程實(shí)例(1)