朴素、Select、Poll和Epoll网络编程模型实现和分析——Poll、Epoll模型处理长连接性能比较
? ? ? ? 在《樸素、Select、Poll和Epoll網絡編程模型實現和分析——模型比較》一文中,我們分析了各種模型在處理短連接時的能力。本文我們將討論處理長連接時各個模型的性能。(轉載請指明出于breaksoftware的csdn博客)
? ? ? ? 我們可以想象下場景,如果我們使用樸素模型來處理長連接,則需要引入多線程來處理:主線程接收接入的Socket,子線程處理一個個Socket的讀寫。因為長連接需要客戶端和服務器之間維系一段時間,所以服務器子線程要一直同步等待socket的改變。如果socket長時間沒有變化,那么這個子線程就將長期處于閑置狀態,這無疑是對資源的一種浪費。然而此時Select、Poll和Epoll模型就能很好的解決這個問題,因為它是異步事件通知模式,所以不需要大量的線程同步等待,可以很好的利用線程資源。
? ? ? ? 因為我不想引入多線程協助處理,而樸素模型在處理長連接的情況下需要多線程協助,所以我沒有將樸素模型列入之后的對長連接支持的改造。還有我也不打算對Select模型代碼進行改造,因為我們之前已經分析過,Select模型只能處理一定數量的長連接,所以在高并發的場景下,它也沒法使用。于是我們需要改造的是Poll和Epoll模型的代碼,其實修改非常簡單,我們只要將之前寫完回包操作之后的關閉連接給去掉即可。
? ? ? ? 下一步,我們要對客戶端進行改造。為了讓客戶端足夠高效的利用線程資源,我們將客戶端也改成Epoll模型。
void* send_data(void* arg) {int wait_time;int epfd, nfds;int client_sock;struct epoll_event ev, events[SOCKET_LIST_COUNT];int in_events, out_events;int index;wait_time = *(int*)arg;in_events = EPOLLIN | EPOLLET;out_events = EPOLLOUT | EPOLLET;epfd = epoll_create1(0);for (index = 0; index < 100; index++) {client_sock = make_client_socket();connect_server(client_sock);set_nonblock(client_sock);request_add(1);ev.data.fd = client_sock;ev.events = in_events;epoll_ctl(epfd, EPOLL_CTL_ADD, client_sock, &ev);client_write(client_sock);ev.events = in_events;epoll_ctl(epfd, EPOLL_CTL_MOD, client_sock, &ev);} while (1) {nfds = epoll_wait(epfd, events, sizeof(events), 500);usleep(wait_time);for (index = 0; index < nfds; ++index) {int fd = events[index].data.fd;if (events[index].events & EPOLLIN) {if (0 != client_read(fd)) {close(fd);epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);}else {events[index].events = out_events;epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &events[index]);}}else if (events[index].events & EPOLLOUT) {client_write(fd);events[index].events = in_events;epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &events[index]);}}}close(epfd);
}
? ? ? ? 我們只對線程函數進行修改。讓一個線程新建100個socket去連接服務器,并使用Epoll模型去管理這些socket。
? ? ? ? 由于我的環境默認只能同時處理1024個連接,所以我使用ulimit -n 3000指令修改了該值,以讓我們服務器可以同時處理3000個連接(實際上不到3000個,因為系統還要占用幾個)。
? ? ? ? 最后在3個終端中使用./epoll_client 10 10指令啟動客戶端,讓客戶端啟動30個線程,共新建3000個長連接。這3000個長連接每隔10微秒向服務器發送和接收一次數據。我們看下Poll模型的穩定時的表現
? ? ? ? 平均每秒處理27222次“讀和寫”操作。我們再看下EPoll模型的表現
? ? ? ? 平均每秒處理18876次“讀和寫”操作。
? ? ? ? 我們非常驚訝的發現Epoll模型竟然比Poll模型差!我們使用varlgrind對兩種模型進行分析
? ? ? ? Poll模型的main函數自身耗時占比僅為12.11%。其主要是讀寫操作的占用。而其執行循環的次數是 10 113 131次,有效循環是8 374 693次,有效率是82.2%。這個比例還是不錯的。
? ? ? ? 我們再看Epoll模型的統計結果
? ? ? ? Epoll模型的main函數自身占用26.56%,這個比例是Poll模型的兩倍。其中循環次數和有效循環次數一致,故有效率是100%。
? ? ? ? 通過上面的對比,我們可以推定就是main函數自身的耗時影響了Epoll模型的效率。
? ? ? ? 和《樸素、Select、Poll和Epoll網絡編程模型實現和分析——模型比較》中對Poll和Epoll模型的性能統計分析,我們發現Poll模型的main函數自身耗時占比有明顯的變化,前文是46.93%,本文是12.11%。而Epoll模型的main函數自身耗時占比則比較穩定,前文是25.7%,本文是26.56%。
? ? ? ? 我們再看下CPU和內存的情況
? ? ? ? 可以發現當前測試環境下兩個模型的CPU和內存占用情況是相差不大的。
? ? ? ? 經過以上分析,我們可以發現在處理長連接時,如果同時連接數量不是很大,且長連接通信頻繁時,Poll模型的效率是比Epoll高的。
? ? ? ? 那么什么時候Epoll效率最高呢?對比Poll和Epoll模型,我們發現Poll模型中有效循環的比例會隨著連接數量上漲、通信頻度下降而下降。于是我們模擬大量長連接,通信頻度不高的場景。
? ? ? ? 為了能支持高并發,我們需要對我們的系統設置進行一些修改。我的測試環境是Ubuntu Server 14,網上有一篇博文《Ubuntu 12 ulimit 系統最大打開文件個數 設置》,我按此博文介紹將我的環境下的最大連接數改成了40960。這一步非常必要。
? ? ? ? 我們使用,/epoll_client 200 1000000指令啟動20個線程,供建立20000個連接,各個連接每一秒鐘發送一次請求。
? ? ? ? 我們看下Poll模型的表現
? ? ? ? Poll模型平均每秒完成6665次“讀和寫”操作,且CPU占用率高達65%。
? ? ? ? 再看下Epoll模型的表現
? ? ? ? Epoll模型平均每秒完成6871次“讀和寫”操作,而CPU占用率只有8.6%。
? ? ? ? 上述現象說明在大量長連接、不頻繁通信的情況下,Epoll模型比Poll模型在CPU消耗上要優秀非常多。
? ? ? ? 同樣我們使用varlgrind分析這種場景下Poll模型和Epoll模型的耗時情況
? ? ? ? ?首先看Poll模型
? ? ? ? Poll模型的main函數自身耗時占了64.65%。總循環次數445 973 878,有效循環次數2 135 891,有效率0.48%。
? ? ? ? 再看看Epoll模型的統計結果
??
? ? ? ? Epolll模型的main函數自身耗時還是穩定在26.57%。總循環次數和有效循環次數一致,故有效率100%。
? ? ? ? 網上還有很多關于Poll模型和Epoll模型在內核源碼級別的對比,并指出Epoll模型的源碼是多么的高效。但是使用varlgrind工具測試發現,其主要的性能差距是在無效循環的比例上。而內核源碼導致的性能進步卻沒有明顯體現。我們以strlen函數為參考進行對比就可以發現上述觀點(可以認為不同程序里對相同內容空間進行strlen操作耗時是一樣)。
? ? ? ? Poll模型的strlen函數和poll函數的耗時占比是1.21:0.01=121。而Epoll模型中則是9.38:(2.36+0.23)=3.63。
? ? ? ? 經過這一系列博文的分析,我們可以大致掌握樸素、Select、Poll和Epoll模型的編程方法,同時也對不同場景下選擇什么模型有了一定的認識。
? ? ? ? 最后附上代碼鏈接:http://pan.baidu.com/s/1bTP7MQ 密碼:0mc9
總結
以上是生活随笔為你收集整理的朴素、Select、Poll和Epoll网络编程模型实现和分析——Poll、Epoll模型处理长连接性能比较的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 朴素、Select、Poll和Epoll
- 下一篇: 跨平台PHP调试器设计及使用方法——立项