epoll的邊沿觸發模式(ET)真的比水平觸發模式(LT)快嗎?(當然LT模式也使用非阻塞IO,重點是要求ET模式下的代碼不能造成飢餓)


1 早期只有ET模式,即使是遠古的那些大牛,ET模式也用不好(當然,那時ET模式本身可能也有一些bug)

2 ET模式需要配合應用層的robin list,以避免飢餓,而應用層的這種設計,千差萬別,無法評估,且跟具體的應用相關,nginx的代碼可以好好的作為ET的案例研究研究

3 要知道,當前比較好用的內存緩存redis只支持LT模式喔,簡單高效。搞那麼複雜幹嘛。。。

4 網上關於ET模式與LT模式的中文例子基本都有問題,各種問題,無力吐槽


ET本身並不會造成飢餓,由於事件只通知一次,開發者一不小心就容易遺漏了待處理的數據,像是飢餓,實質是bug

使用ET模式,特定場景下會比LT更快,因為它可以便捷的處理EPOLLOUT事件,省去打開與關閉EPOLLOUT的epoll_ctl(EPOLL_CTL_MOD)調用。從而有可能讓你的性能得到一定的提升。

例如你需要寫出1M的數據,寫出到socket 256k時,返回了EAGAIN,ET模式下,當再次epoll返回EPOLLOUT事件時,繼續寫出待寫出的數據,當沒有數據需要寫出時,不處理直接略過即可。而LT模式則需要先打開EPOLLOUT,當沒有數據需要寫出時,再關閉EPOLLOUT(否則會一直返回EPOLLOUT事件)

總體來說,ET處理EPOLLOUT方便高效些,LT不容易遺漏事件、不易產生bug

如果server的響應通常較小,不會觸發EPOLLOUT,那麼適合使用LT,例如redis等。而nginx作為高性能的通用伺服器,網路流量可以跑滿達到1G,這種情況下很容易觸發EPOLLOUT,則使用ET。

關於某些場景下ET模式比LT模式效率更好,我有篇文章進行了詳細的解釋與測試,參看

epoll LT/ET 深入剖析

這裡有兩個例子,分別演示了LT與ET兩種工作模式

handy/epoll-et.cc at master · yedf/handy · GitHub

handy/epoll.cc at master · yedf/handy · GitHub


目前沒有up to date的測試表明ET更快。


ET理論上可以比LT少帶來一些系統調用,所以更省一些。具體的性能提高有多少,要看應用場景。不過絕大部分場景下,LT是足夠的。


在eventloop類型(包括各類fiber/coroutine)的程序中, 處理邏輯和epoll_wait都在一個線程, ET相比LT沒有太大的差別. 反而由於LT醒的更頻繁, 可能時效性更好些. 在老式的多線程RPC實現中, 消息的讀取分割和epoll_wait在同一個線程中運行, 類似上面的原因, ET和LT的區別不大.

但在更高並發的RPC實現中, 為了對大消息的反序列化也可以並行, 消息的讀取和分割可能運行和epoll_wait不同的線程中, 這時ET是必須的, 否則在讀完數據前, epoll_wait會不停地無謂醒來.


曾經由於LT方式過多的epoll_ctl調用,而想嘗試下ET,但是代碼邏輯更複雜,最後效果反而不如LT的好


答案:否。

原因

1:回答說寫操作可以省掉epoll+喚醒過程的,請問你epoll前沒有嘗試寫嗎?(寫了個殘包數據才去epoll 吧, epoll是幹啥的,不就是「當條件不滿足時等系統告訴我條件滿足」嗎?)正常lt 也應該先寫 寫到eagin或者ewouldblock或者直接寫完吧?不明白樓上說可以減少ctl out操作 是哪裡呢?


  1. 在epoll_wait的時候,阻塞等待事件發生, 事件發生時通過回調掛到ready list鏈表中
  2. epoll_wait返回, 處理ready list, 返回事件給調用者
  3. 此時ET模式已經將事件從ready list中刪除,LT模式中還存在
  4. 此時假設應用程序處理完了事件, 再次epoll_wait. ET模式繼續阻塞
  5. LT模式由於ready list中依然存在事件則不會阻塞, 對這些socket調用poll方法獲取最新的事件信息,如果確認沒事件了才會刪除。

對於連接數少的時候沒什麼事情,當高並發場景下,步驟5遍歷ready list將會有一定開銷


我寫trading system,告訴你,真的快,在大並發的時候事件變少了,所以不用花那麼多判斷循環去處理那些事件了。

正常情況LT/ET沒什麼差別。


這跟應用場景和實際需求有關係,沒有說哪種方式一定好,只有在特定條件盒應用場景下才能夠說哪個好啊


為什麼我用epoll ET的時候,在server端只讀數據,不寫數據, 在客戶端寫了個簡單的socket連續兩次write之後close掉socket, 在server端:

有時只監聽到一次epollin事件(把兩次write的內容連在一起讀出來了,沒有close),

有時收到兩次epollin,第一個epollin 是前兩次write連在一起,第二次epollin是close的事件,

很少收到三次epollin事件的情況,但也有,

我的理解是在之前緩衝區已經清空的情況下,有新數據到達就會觸發epollin事件;從抓包來看,兩次write和close都是分次到達的,為什麼read會一次都讀出來呢? 查了一些資料,設置TCP_NODELAY和TCP_QUICKACK看起來都沒效果。

我用tcpdump查看數據,沒有發現粘包現象(還是我對粘包理解的不對),哪位能幫忙看下,可能是哪兒有問題,或者在ET模式下有什麼好的解決方案(在我的場景里,兩次write連在一起讀沒關係,但close事件檢測不到就很麻煩了)? 謝謝

tcpdump 抓到的內容:

21:48:15.403364 IP localhost.58953 &> localhost.18001: Flags [P.], seq 1:6, ack 1, win 342, options [nop,nop,TS val 43240 ecr 43240], length 5

21:48:15.403410 IP localhost.18001 &> localhost.58953: Flags [.], ack 6, win 342, options [nop,nop,TS val 43240 ecr 43240], length 0

21:48:15.403443 IP localhost.58953 &> localhost.18001: Flags [P.], seq 6:11, ack 1, win 342, options [nop,nop,TS val 43240 ecr 43240], length 5

21:48:15.403475 IP localhost.18001 &> localhost.58953: Flags [.], ack 11, win 342, options [nop,nop,TS val 43240 ecr 43240], length 0

21:48:15.403554 IP localhost.58953 &> localhost.18001: Flags [F.], seq 11, ack 1, win 342, options [nop,nop,TS val 43240 ecr 43240], length 0

21:48:15.403649 IP localhost.18001 &> localhost.58953: Flags [F.], seq 1, ack 12, win 342, options [nop,nop,TS val 43240 ecr 43240], length 0

21:48:15.403682 IP localhost.58953 &> localhost.18001: Flags [.], ack 2, win 342, options [nop,nop,TS val 43240 ecr 43240], length 0


LT比ET有更好的魯棒性,如果ET不比LT快,那麼ET就沒有存在的意義了


推薦閱讀:

既然有 UNIX 哲學,那有沒有 Windows 哲學呢?
為什麼微軟,谷歌要另立操作系統山頭,而不是像蘋果一樣,在最正統的 Unix 基礎上開發?
posix和system v 信號量哪個更快?
同為開源的類unix系統,為什麼FreeBSD遠沒有Linux流行?
除了Windows和UNIX內核兩大桌面操作系統外,歷史上是否曾出現其他具有一定實用價值的操作系統?

TAG:編程 | Unix | Linux開發 |