FlashProxy Logo

FlashProxy

ProxyCông nghệTin tứcHướng dẫnHướng dẫnThông báo

3× Nhiều Kết Nối Hơn. Cùng Core. Đằng Sau Nghiên Cứu Hiệu Năng về Accept Engine của FlashProxy

3× Nhiều Kết Nối Hơn. Cùng Core. Đằng Sau Nghiên Cứu Hiệu Năng về Accept Engine của FlashProxy

Cách FlashProxy sử dụng một bộ tối ưu hóa AI tự chủ để khám phá một TCP accept engine sử dụng 3× ít hướng dẫn CPU hơn Go. Mã nguồn mở và được cấp phép MIT.

Đ
Đội ngũ FlashProxy
21 tháng 6, 2026
12 phút đọc

Có một loại vấn đề hiệu năng chỉ xuất hiện ở quy mô lớn, và nó thực sự đáng lo ngại khi xảy ra. Bạn không bị tắc nghẽn bởi logic định tuyến. Không phải bởi mã hóa. Không phải bởi bất kỳ thứ gì mà ứng dụng của bạn thực sự cần làm. Bạn bị tắc nghẽn bởi cơ sở hạ tầng xung quanh nó. Bởi hệ thống ống dẫn.

Đó chính xác là những gì đã xảy ra với chúng tôi.

Vào khoảng 40 000 kết nối mỗi giây, proxy Go production của chúng tôi bắt đầu chạm giới hạn CPU trong đường dẫn accept. Thủ phạm là mô hình tiêu chuẩn của Go cho các dịch vụ mạng: một goroutine trên mỗi kết nối, một syscall trên mỗi thao tác. Mỗi kết nối ngắn (một health check, một load balancer probe, một redirect nhỏ) chạm vào kernel bốn lần. accept, read, write, close. Với lượng kết nối churn cao, bốn round-trip đó không còn là overhead nữa mà trở thành toàn bộ chi phí. Mã ứng dụng gần như không tốn gì. Đưa bytes vào và ra khỏi kernel thì không phải vậy.

Giải pháp mà mọi người đều hướng tới là io_uring, giao diện I/O bất đồng bộ của Linux cho phép gộp các thao tác kernel thành batch và giảm đáng kể số lần bạn phải vượt qua ranh giới user/kernel. Chúng tôi biết điều đó. Câu hỏi khó hơn là cách tinh chỉnh nó.

Tại Sao Chúng Tôi Không Chỉ Tự Tinh Chỉnh

io_uring có một danh sách dài các tối ưu hóa được ghi lại: multishot accept, registered file descriptors, DEFER_TASKRUN, completion chains, MSG_MORE coalescing. Vấn đề là những kỹ thuật này tương tác với nhau theo những cách thực sự khó dự đoán. Một số kết hợp cộng hưởng. Một số triệt tiêu lẫn nhau. Một số sụt giảm hoàn toàn vô hình cho đến khi bạn benchmark dưới tải thực tế trên phần cứng thực.

Một developer ngồi với perf stat có thể thử một số kết hợp trong một ngày. Nhưng không gian tìm kiếm thực sự, bao gồm các kết hợp, thứ tự, giá trị tham số và tương tác phiên bản kernel, lớn hơn nhiều so với vậy. Quan trọng hơn, trực giác con người là một điểm yếu ở đây. Chúng ta có xu hướng bám vào các lý thuyết trông đúng trên giấy, ngay cả khi dữ liệu nói ngược lại.

Vì vậy, chúng tôi đã xây dựng một vòng lặp đo lường khép kín và giao bài toán tìm kiếm cho một agent AI.

Cách Vòng Lặp Nghiên Cứu Hoạt Động

Chúng tôi thiết lập hai triển khai server chạy song song. Control là một server Go mô phỏng chính xác đường dẫn accept của proxy production của chúng tôi: goroutine per connection, SO_REUSEPORT fan-out. Nó được đóng băng trong suốt thử nghiệm. Treatment là một server C sử dụng liburing, bắt đầu từ một basic single-shot io_uring accept loop. Đó là đoạn mã duy nhất có thể được thay đổi.

Cả hai server đều thực hiện cùng một hợp đồng: accept a connection, read request bytes, write a fixed HTTP 200 response, close. Cùng phần cứng, cùng kernel, cùng tải. Biến số duy nhất là cách chúng xử lý nó.

Claude Code chạy headless với vai trò bộ tối ưu hóa. Mỗi lần lặp, nó đọc champion hiện tại, toàn bộ lịch sử các mutation trước đó được lưu dưới dạng git commits, một knowledge base gồm các bài học tích lũy qua các lần chạy, và một database của profiling data. Nó đưa ra một giả thuyết duy nhất và thực hiện chỉnh sửa của mình. Sau đó nó bàn giao hoàn toàn.

Một bash harness riêng biệt, mà AI không thể can thiệp, build mutation, pin nó vào một CPU core cô lập, chạy load generator, và chấm điểm kết quả. Công thức chấm điểm là:

score = 1 000 000 000/mean(instructions per connection)

Số lượng lệnh CPU là một phép đo phần cứng chính xác. Không giống như các con số thông lượng, chúng không bị ảnh hưởng bởi thermal throttling và biến động tần số xung nhịp. Chúng cho bạn biết chính xác CPU đang thực hiện bao nhiêu công việc trên mỗi kết nối, điều thực sự quyết định công suất ở quy mô lớn.

Nếu một mutation cải thiện điểm số hơn 3 % so với champion hiện tại, nó được thăng hạng. Nếu không, nó bị xóa bằng git reset --hard và vòng lặp tiếp tục. AI viết các giả thuyết. Harness đưa ra mọi quyết định giữ lại hay revert. Hai bên không can thiệp vào công việc của nhau.

Để ngăn bộ tối ưu hóa gian lận benchmark, mọi lần chạy được chấm điểm đều được xác thực: các byte reply phải hoàn toàn chính xác, các kết nối phải hoàn tất end-to-end, và tỷ lệ lỗi phải dưới 0,01 %. Bất kỳ vi phạm nào sẽ bị chấm 0 điểm.

Những Gì Chúng Tôi Phát Hiện

Bộ tối ưu hóa chạy trong hai ngày và hội tụ về sáu thay đổi mà tổng hợp lại chiếm toàn bộ khoảng cách hiệu năng.

  • DEFER_TASKRUN và SINGLE_ISSUER ring flags. Những cờ này chuyển completion task-work vào vòng lặp sự kiện của chính worker thread, loại bỏ cross-CPU wakeups. Đây là bước nhảy đơn lẻ lớn nhất trong toàn bộ quá trình chạy, một sự giảm trực tiếp chi phí CPU của kernel trên mỗi thao tác, không phải một throughput trick.

  • Registered file descriptors. Với direct descriptors, các kết nối được chấp nhận tồn tại trong bảng riêng của ring thay vì bảng file descriptor của process. Điều này bỏ qua việc cài đặt fd-table khi accept và tra cứu trong mọi thao tác tiếp theo. Bộ tối ưu hóa đã thử nhiều kích thước bảng và phát hiện rằng 4 096 entries là cấu hình tối ưu.

  • Multishot accept. Thay vì re-arming accept operation sau mỗi kết nối, bạn arm nó một lần và kernel tự động gửi một completion cho mỗi kết nối mới. Điều này cắt giảm io_uring_enter calls per connection xuống còn 0,34.

  • Per-worker connection freelist. Pre-allocating 128 connection objects per worker loại bỏ hoàn toàn malloc trên hot path. Tỷ lệ đo được của các lệnh CPU dành cho libc giảm từ 1,32 % xuống 0,92 %.

  • MSG_MORE reply và FIN fusion. Gửi reply với MSG_MORE giữ nó trong TCP write queue để FIN của kết nối có thể piggyback lên đó, gửi cả hai dưới dạng một single TCP segment. Một NIC doorbell thay vì hai. Bộ tối ưu hóa phát hiện điều này khi nhận thấy rằng một low-level kernel write function đang tiêu thụ gấp đôi tỷ lệ lệnh dự kiến và truy ngược lại việc phân tách segment không cần thiết.

  • Batched completions với CQE_SKIP_SUCCESS. Gắn thẻ các thao tác send và close để chúng không tạo completion events khi thành công có nghĩa là chỉ accept và receive mới tạo ra completions, khoảng hai lần per connection thay vì bốn. Một submit-and-wait call điều khiển nhiều kết nối đồng thời.

Sự Thất Bại Dạy Nhiều Nhất

Tại một thời điểm, bộ tối ưu hóa đã thử liên kết các thao tác receive, send và close thành một chain, một kỹ thuật khiến mỗi thao tác tự động kích hoạt khi thao tác trước hoàn tất. Mục tiêu là giảm kernel round-trips, và nó đã hoạt động: enter calls per connection giảm từ 1,90 xuống 1,40.

Điểm số giảm 34 %. Reverted immediately.

Bài học đưa vào knowledge base: minimizing kernel entries không phải là đòn bẩy then chốt. Chi phí của chain serialization lớn hơn số entries mà nó tiết kiệm.

Đây chính xác là loại kết quả phá vỡ trực giác con người. Metric trông như bottleneck không phải là bottleneck thực sự. Một engineer có thể đã bảo vệ tối ưu hóa đó lâu hơn. Vòng lặp đo lường nó, loại bỏ nó, và tiếp tục.

Nơi Quá Trình Tìm Kiếm Dừng Lại

Sau sáu chiến thắng, bộ tối ưu hóa đã khám phá thêm khoảng một chục ứng viên. Tất cả đều bị revert, không phải vì chúng sụt giảm, mà vì nhiễu đo lường vượt quá ngưỡng thăng hạng 3 %. Không còn gì để tìm.

Ở cấu hình champion, khoảng 94 % CPU còn lại thuộc về TCP stack của Linux kernel. Khoảng 1 % là application code. Khoảng 4 % là leveraging internals. Không còn user-space code nào để tối ưu hóa một cách có ý nghĩa. Nghiên cứu đã xác định chính xác ngưỡng sàn và dừng lại ở đó.

Kết Quả

Trên một pinned core, CPU-bound, với 512 fixed in-flight connections trên loopback:

  • Go goroutine-per-connection: 83 250 instructions per connection

  • Vanilla io_uring starting baseline: 59 931 instructions per connection

  • FlashProxy's accept engine: 27 363 instructions per connection

Đó là 3,04× ít lệnh CPU hơn trên mỗi kết nối so với Go, và 2,19× ít hơn so với io_uring baseline. Trên một core bão hòa duy nhất, điều đó tương đương với khoảng gấp sáu lần thông lượng kết nối so với mô hình goroutine.

Đây là các loopback benchmark được thiết kế để cô lập chi phí CPU một cách rõ ràng. Các tỷ lệ mới là điều quan trọng, không phải các con số tuyệt đối. Các kỹ thuật cơ bản (multishot accept, DEFER_TASKRUN, registered descriptors) được ghi lại trong các idiom của io_uring. Điều mà nghiên cứu tạo ra là bằng chứng về những kết hợp nào thực sự hoạt động cùng nhau, và những kết hợp nào trông tốt trên giấy nhưng lại tốn kém trong thực tế.

Thư Viện Mã Nguồn Mở

Thiết kế chiến thắng hiện đã trở thành flashaccept, một thư viện C mã nguồn mở đóng gói cả sáu tối ưu hóa phía sau một API đơn giản. Bạn cung cấp cho nó một port và một request handler. Nó tự động chạy một optimized io_uring accept loop per core.

Nó yêu cầu Linux và liburing 2,3 hoặc mới hơn. Trên các kernel cũ hơn, nó giảm cấp một cách graceful. Trường hợp sử dụng dự kiến là các kết nối high-churn, short-lived: health checks, redirectors, load balancer probes, small RPC responses. Toàn bộ research rig, bao gồm baseline, harness, optimizer configuration và full accumulated knowledge base, đều được đưa vào và hoàn toàn có thể tái tạo.

MIT licensed. Available now at github.com/thealonlevi/flashaccept.

Nghiên cứu này xuất phát trực tiếp từ quá trình xây dựng cơ sở hạ tầng proxy của FlashProxy. Nếu bạn đang chạy một dịch vụ Linux high-churn và accept path là bottleneck của bạn, chúng tôi đã xây dựng thư viện này chính xác cho vấn đề đó. Nếu bạn muốn mở rộng nó hoặc tự chạy research rig, repository có mọi thứ bạn cần.

FAQ

Flashaccept là gì?

Flashaccept là một thư viện C mã nguồn mở được xây dựng bởi FlashProxy, cung cấp một TCP accept engine hiệu năng cao cho Linux. Nó sử dụng io_uring bên dưới và chấp nhận các kết nối với 3,04× ít lệnh CPU hơn so với một standard Go goroutine-per-connection server. Nó được cấp phép MIT và có sẵn trên GitHub.

Nó được thiết kế cho những workload nào?

Flashaccept được xây dựng cho các kết nối high-churn, short-lived nơi chu kỳ request-reply-close xảy ra với volume lớn: health check endpoints, load balancer probes, HTTP redirectors và small RPC responses. Nó không được thiết kế cho keep-alive connections hoặc multi-exchange sessions trong v1.

Tôi cần phiên bản Linux nào và phiên bản liburing nào?

Bạn cần Linux với liburing phiên bản 2,3 hoặc mới hơn, đi kèm với Ubuntu 24,04 trở lên. Fast path (multishot accept, direct descriptors) yêu cầu kernel 5,19 hoặc mới hơn. Trên các kernel cũ hơn, thư viện giảm cấp một cách graceful về single-shot accept và regular file descriptors.

io_uring là gì và tại sao nó quan trọng với hiệu năng proxy?

io_uring là một giao diện Linux kernel được giới thiệu trong kernel 5,1, cho phép các ứng dụng submit và receive các thao tác I/O một cách bất đồng bộ bằng cách sử dụng shared memory ring buffers, giảm đáng kể số lượng system call cần thiết. Đối với một proxy infrastructure xử lý hàng chục nghìn short-lived connections mỗi giây, chi phí của việc liên tục vượt qua ranh giới user/kernel trở thành khoản chi phí CPU chủ yếu. io_uring giảm đáng kể chi phí đó.

flashaccept có phải là thứ FlashProxy chạy trong production không?

Nghiên cứu xuất phát trực tiếp từ công việc scaling production của chúng tôi. Proxy infrastructure tại FlashProxy hoạt động trên 190+ quốc gia và xử lý lượng kết nối đáng kể. Tối ưu hóa accept path là một yêu cầu kỹ thuật thực tế, không phải một bài tập nghiên cứu. flashaccept là kết quả đúc kết từ công việc đó, được mã nguồn mở để những người khác có thể sử dụng.

Tôi có thể sử dụng flashaccept ngay hôm nay không?

Có. Đây là phiên bản 1,0.1, được cấp phép MIT, và có sẵn qua GitHub, vcpkg, Conan và Arch AUR. Thư viện đã được kiểm thử dưới AddressSanitizer và UBSan trên 400 000+ kết nối trên tất cả bốn đường dẫn cấu hình.

Tôi có thể tìm hiểu thêm về cơ sở hạ tầng của FlashProxy ở đâu?

Bạn có thể đọc thêm về cách chúng tôi xây dựng và mở rộng proxy infrastructure trên FlashProxy blog hoặc khám phá trực tiếp mạng lưới proxy của chúng tôi.


flashacceptFlashProxymáy chủ proxy tăng cường hiệu suấttăng cường máy chủtăng cường máy chủ proxy