Cảnh báo về lò phản ứng chọn / thăm dò so với lò phản ứng kỷ nguyên trong Twisted


95

Mọi thứ tôi đã đọc và trải nghiệm (ứng dụng dựa trên Tornado) khiến tôi tin rằng ePoll là một sự thay thế tự nhiên cho mạng dựa trên Chọn và thăm dò, đặc biệt là với Twisted. Điều đó khiến tôi hoang tưởng, rất hiếm có một kỹ thuật hoặc phương pháp tốt hơn không đi kèm với một cái giá.

Đọc vài chục so sánh giữa kỷ nguyên và các lựa chọn thay thế cho thấy rằng kỷ nguyên rõ ràng là nhà vô địch về tốc độ và khả năng mở rộng, cụ thể là nó quy mô theo kiểu tuyến tính thật tuyệt vời. Điều đó nói rằng, những gì về sử dụng bộ xử lý và bộ nhớ, liệu epoll vẫn là nhà vô địch?

Câu trả lời:


190

Đối với số lượng rất nhỏ các ổ cắm (tất nhiên là thay đổi tùy thuộc vào phần cứng của bạn, nhưng chúng ta đang nói về thứ gì đó theo thứ tự 10 hoặc ít hơn), select có thể đánh bại kỷ nguyên sử dụng bộ nhớ và tốc độ thời gian chạy. Tất nhiên, đối với số lượng ổ cắm nhỏ như vậy, cả hai cơ chế đều nhanh đến mức bạn không thực sự quan tâm đến sự khác biệt này trong đại đa số trường hợp.

Tuy nhiên, một sự làm rõ. Cả select và epoll đều chia tỷ lệ tuyến tính. Tuy nhiên, một sự khác biệt lớn là các API hướng tới không gian người dùng có sự phức tạp dựa trên những thứ khác nhau. Chi phí của một selectcuộc gọi tương đương với giá trị của bộ mô tả tệp được đánh số cao nhất mà bạn chuyển nó. Nếu bạn chọn trên một fd duy nhất, 100, thì chi phí đó đắt gấp đôi so với chọn trên một fd duy nhất, 50. Thêm nhiều fd dưới mức cao nhất không hoàn toàn miễn phí, vì vậy nó phức tạp hơn một chút so với thực tế, nhưng điều này là một ước lượng đầu tiên tốt cho hầu hết các triển khai.

Chi phí của epoll gần với số lượng bộ mô tả tệp thực sự có các sự kiện trên chúng. Nếu bạn đang theo dõi 200 bộ mô tả tệp, nhưng chỉ 100 trong số chúng có các sự kiện trên đó, thì bạn (rất gần) chỉ trả tiền cho 100 bộ mô tả tệp đang hoạt động đó. Đây là lúc epoll có xu hướng cung cấp một trong những lợi thế chính của nó so với lựa chọn. Nếu bạn có một nghìn khách hàng chủ yếu là nhàn rỗi, thì khi bạn sử dụng select, bạn vẫn phải trả cho tất cả một nghìn khách hàng trong số họ. Tuy nhiên, với epoll, giống như bạn chỉ có một số ít - bạn chỉ trả tiền cho những cái đang hoạt động tại bất kỳ thời điểm nào.

Tất cả điều này có nghĩa là epoll sẽ dẫn đến việc sử dụng CPU ít hơn cho hầu hết các khối lượng công việc. Về mức độ sử dụng bộ nhớ, nó có một chút rắc rối. selectquản lý để đại diện cho tất cả các thông tin cần thiết theo cách rất nhỏ gọn (một bit cho mỗi bộ mô tả tệp). Và giới hạn FD_SETSIZE (thường là 1024) về số lượng bộ mô tả tệp bạn có thể sử dụng có selectnghĩa là bạn sẽ không bao giờ chi tiêu nhiều hơn 128 byte cho mỗi bộ ba fd mà bạn có thể sử dụngselect(đọc, ghi, ngoại lệ). So với 384 byte tối đa đó, epoll giống như một con lợn. Mỗi bộ mô tả tệp được biểu diễn bằng cấu trúc nhiều byte. Tuy nhiên, về mặt tuyệt đối, nó vẫn sẽ không sử dụng nhiều bộ nhớ. Bạn có thể đại diện cho một số lượng lớn các bộ mô tả tệp trong vài chục kilobyte (tôi nghĩ rằng khoảng 20k cho mỗi 1000 bộ mô tả tệp). Và bạn cũng có thể thực tế là bạn phải dành tất cả 384 byte trong số đó selectnếu bạn chỉ muốn theo dõi một bộ mô tả tệp nhưng giá trị của nó xảy ra là 1024, khi có epoll thì bạn chỉ tốn 20 byte. Tuy nhiên, tất cả những con số này đều khá nhỏ, vì vậy nó không tạo ra nhiều sự khác biệt.

Và cũng có lợi ích khác của epoll, mà có lẽ bạn đã biết, rằng nó không giới hạn ở các bộ mô tả tệp FD_SETSIZE. Bạn có thể sử dụng nó để theo dõi bao nhiêu bộ mô tả tệp mà bạn có. Và nếu bạn chỉ có một bộ mô tả tệp, nhưng giá trị của nó lớn hơn FD_SETSIZE, epoll cũng hoạt động với điều đó, nhưng selectkhông.

Ngẫu nhiên, gần đây tôi cũng đã phát hiện ra một nhược điểm nhỏ epollso với selecthoặc poll. Mặc dù không có API nào trong số ba API này hỗ trợ tệp bình thường (tức là tệp trên hệ thống tệp) selectpollthể hiện sự thiếu hỗ trợ này khi báo cáo các bộ mô tả luôn có thể đọc được và luôn có thể ghi. Điều này làm cho chúng không phù hợp với bất kỳ loại I / O hệ thống tệp không chặn có ý nghĩa nào, một chương trình sử dụng selecthoặc pollvà tình cờ gặp phải bộ mô tả tệp từ hệ thống tệp ít nhất sẽ tiếp tục hoạt động (hoặc nếu nó không thành công, nó sẽ không phải là do của selecthoặc poll), mặc dù nó có lẽ không phải với hiệu suất tốt nhất.

Mặt khác, epollsẽ nhanh chóng thất bại với một lỗi ( EPERMdường như) khi được yêu cầu giám sát một bộ mô tả tệp như vậy. Nói một cách chính xác, điều này hầu như không chính xác. Nó chỉ đơn thuần báo hiệu sự thiếu hỗ trợ của nó một cách rõ ràng. Thông thường tôi sẽ hoan nghênh các điều kiện lỗi rõ ràng, nhưng điều này không có tài liệu (theo như tôi có thể nói) và dẫn đến một ứng dụng bị hỏng hoàn toàn, thay vì chỉ hoạt động với hiệu suất có khả năng bị suy giảm.

Trong thực tế, nơi duy nhất tôi thấy điều này xuất hiện là khi tương tác với stdio. Người dùng có thể chuyển hướng stdin hoặc stdout từ / đến một tệp bình thường. Trong khi trước đây stdin và stdout sẽ là một đường ống - được epoll hỗ trợ tốt - sau đó nó trở thành một tệp bình thường và epoll bị lỗi lớn, phá vỡ ứng dụng.


Câu trả lời rất hay. Cân nhắc việc rõ ràng về hành vi của pollcho đầy đủ?
quark

6
Hai xu của tôi về hành vi đọc từ các tệp thông thường: Tôi thường thích hoàn toàn không làm giảm hiệu suất. Lý do là nó có nhiều khả năng bị phát hiện hơn trong quá trình phát triển và do đó hoạt động bình thường (giả sử có một phương pháp thay thế để thực hiện I / O cho các tệp thực tế). Tất nhiên, YMMV: có thể không có sự chậm lại đáng chú ý, trong trường hợp thất bại không tốt hơn. Nhưng sự chậm lại đáng kinh ngạc chỉ xảy ra trong những trường hợp đặc biệt có thể rất khó bắt được trong quá trình phát triển, khiến nó như một quả bom hẹn giờ khi được triển khai thực sự.
quark

1
Chỉ cần đọc hoàn toàn chỉnh sửa của bạn. Ở một khía cạnh nào đó, tôi đồng ý rằng có lẽ không đúng để kỷ nguyên không bắt chước những người tiền nhiệm của nó nhưng sau đó một lần nữa tôi có thể tưởng tượng nhà phát triển đã triển khai lỗi EPERM nghĩ rằng "Chỉ vì nó luôn bị hỏng, nên việc phá vỡ của tôi là không đúng. tốt." Và một lập luận phản bác khác, tôi là một lập trình viên bảo vệ bất cứ điều gì trong quá khứ 1 + 1 đều bị nghi ngờ và tôi viết mã theo cách như vậy để cho phép những thất bại duyên dáng. Việc kernel kích hoạt một lỗi ngoài mong đợi không phải là điều tốt hay đáng quan tâm.
David

1
@ Jean-Paul, bạn cũng có thể thêm một số giải thích về kqueue được không?
Good Person

Đặt hiệu suất sang một bên, có vấn đề gì xuất phát từ điều này (từ man select) Nhân Linux không áp đặt giới hạn cố định, nhưng việc triển khai glibc làm cho fd_set trở thành loại kích thước cố định, với FD_SETSIZE được định nghĩa là 1024 và macro FD _ * () hoạt động theo giới hạn đó. Để theo dõi các bộ mô tả tệp lớn hơn 1023, hãy sử dụng thăm dò ý kiến ​​(2) để thay thế. Trên CentOS 7, tôi đã gặp sự cố trong đó mã của riêng tôi không chọn được () vì hạt nhân trả về một trình xử lý tệp> 1023 và tôi hiện đang xem xét một vấn đề có vẻ như nó có thể bị xoắn gây ra cùng một vấn đề.
Paul D Smith

4

Trong các thử nghiệm tại công ty của tôi, một vấn đề với epoll () đã xuất hiện, do đó, một chi phí duy nhất so với select.

Khi cố gắng đọc từ mạng có thời gian chờ, việc tạo epoll_fd (thay vì FD_SET) và thêm fd vào epoll_fd, sẽ đắt hơn nhiều so với việc tạo FD_SET (là một malloc đơn giản).

Theo câu trả lời trước, khi số lượng FD trong quá trình trở nên lớn, chi phí của select () sẽ trở nên cao hơn, nhưng trong thử nghiệm của chúng tôi, ngay cả với các giá trị fd trong 10.000, select vẫn là người chiến thắng. Đây là những trường hợp chỉ có một fd mà một luồng đang chờ và chỉ cần cố gắng khắc phục thực tế là mạng đọc và ghi mạng không hết thời gian chờ khi sử dụng mô hình luồng chặn. Tất nhiên, các mô hình luồng chặn có hiệu suất thấp so với các hệ thống lò phản ứng không chặn, nhưng có những trường hợp, để tích hợp với một cơ sở mã kế thừa cụ thể, nó là bắt buộc.

Loại trường hợp sử dụng này rất hiếm trong các ứng dụng hiệu suất cao, bởi vì mô hình lò phản ứng không cần tạo epoll_fd mới mỗi lần. Đối với mô hình mà epoll_fd tồn tại lâu dài --- được ưu tiên rõ ràng cho bất kỳ thiết kế máy chủ hiệu suất cao nào --- epoll là người chiến thắng rõ ràng về mọi mặt.


5
Nhưng bạn thậm chí không thể sử dụng select()nếu bạn có giá trị bộ mô tả tệp trong phạm vi 10k + - trừ khi bạn biên dịch lại một nửa hệ thống của mình để thay đổi FD_SETSIZE - vì vậy tôi tự hỏi chiến lược này hoạt động như thế nào. Đối với kịch bản bạn đã mô tả, tôi có thể sẽ xem xét cái poll()nào giống select()nhiều hơn là giống epoll()- nhưng loại bỏ giới hạn FD_SETSIZE.
Jean-Paul Calderone

Bạn có thể sử dụng select () nếu bạn có giá trị bộ mô tả tệp trong phạm vi 10K, bởi vì bạn có thể malloc () một FD_SET. Trên thực tế, vì FD_SETSIZE là thời gian biên dịch và giới hạn fd thực tế là trong thời gian chạy, việc sử dụng an toàn DUY NHẤT của FD_SET kiểm tra số lượng bộ mô tả tệp so với kích thước của FD_SET và thực hiện một malloc (hoặc tương đương đạo đức) nếu FD_SET là quá nhỏ. Tôi đã bị sốc khi nhìn thấy điều này trong quá trình sản xuất với một khách hàng. Sau khi lập trình các socket trong 20 năm, tất cả các đoạn mã tôi đã từng viết - và hầu hết các hướng dẫn trên web - đều không an toàn.
Brian Bulkowski

5
Điều này không đúng, theo như tôi biết, trên bất kỳ nền tảng phổ biến nào. FD_SETSIZElà một hằng số thời gian biên dịch được đặt khi thư viện C của bạn được biên dịch. Nếu bạn xác định nó thành một giá trị khác khi bạn xây dựng ứng dụng của mình thì ứng dụng của bạn và thư viện C sẽ không đồng ý và mọi thứ sẽ diễn ra kém. Nếu bạn có tài liệu tham khảo cho rằng có thể xác định lại một cách an toàn, FD_SETSIZEtôi muốn xem chúng.
Jean-Paul Calderone
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.