{"id":11324,"date":"2020-06-01T06:00:52","date_gmt":"2020-06-01T10:00:52","guid":{"rendered":"https:\/\/blog.litespeedtech.com\/?p=11324"},"modified":"2020-06-01T06:00:52","modified_gmt":"2020-06-01T10:00:52","slug":"performance-comparison-quic-udp-xdp","status":"publish","type":"post","link":"https:\/\/lswp.store\/index.php\/2020\/06\/01\/performance-comparison-quic-udp-xdp\/","title":{"rendered":"Performance Comparison of QUIC with UDP and XDP"},"content":{"rendered":"<p><img fetchpriority=\"high\" decoding=\"async\" class=\"aligncenter size-large wp-image-11326\" src=\"https:\/\/lswp.store\/wp-content\/uploads\/2020\/05\/QUIC-UDP-XDP-1024x512.png\" alt=\"Compare Performance QUIC UDP XDP\" width=\"1024\" height=\"512\" srcset=\"https:\/\/lswp.store\/wp-content\/uploads\/2020\/05\/QUIC-UDP-XDP-1024x512.png 1024w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/05\/QUIC-UDP-XDP-300x150.png 300w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/05\/QUIC-UDP-XDP-768x384.png 768w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/05\/QUIC-UDP-XDP-1536x768.png 1536w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/05\/QUIC-UDP-XDP-2048x1024.png 2048w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/05\/QUIC-UDP-XDP-1320x660.png 1320w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/05\/QUIC-UDP-XDP-600x300.png 600w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/p>\n<h2>Goal: Determine whether any performance gain can be achieved with XDP UDP versus kernel UDP<\/h2>\n<p>HTTP\/3 uses QUIC which in turn uses UDP (User Datagram Protocol) rather than TCP\/IP for communications. <a href=\"http:\/\/vger.kernel.org\/lpc_net2018_talks\/lpc18_paper_af_xdp_perf-v2.pdf\">XDP<\/a> (eXpress Data Path) has been added recently to several operating systems to provide User Space applications performance comparable to direct kernel applications.<\/p>\n<p>The goal of this blog is to determine if QUIC can gain significant enough performance to justify implementing XDP where it is available. Understanding performance gains usually requires detail so besides a simple test, a profiler will be used to see exactly where performance gains occur.<\/p>\n<p>XDP can be run with hardware support or can be run entirely in software. The goal is to have an XDP program be usable in the widest possible range of computing environments. <a href=\"https:\/\/arxiv.org\/pdf\/1901.10664.pdf#page=11\">This is best accomplished by targeting virtual machines<\/a>.<\/p>\n<h2>The Test Environment<\/h2>\n<h3>LSQUIC<\/h3>\n<p>The QUIC engine used was the popular open-source LSQUIC library available from LiteSpeed Technologies. Only small modifications were made to the base library to support XDP. The XDP functionality was provided by a separate module, which implemented LSQUIC callbacks to send, receive, and allocate packets. Testing was performed using the <code>http_server<\/code> and <code>http_client<\/code> programs provided by LSQUIC, modified as necessary.<\/p>\n<p>The client machine ran a released version of the <code>http_client<\/code> program from the LSQUIC library and was always the receiver. It was specified to use the Q046 QUIC protocol version.<\/p>\n<p>The server machine ran the modified version of the <code>http_server<\/code> program from the LSQUIC library and was run with command line parameters to use either the native UDP or the XDP UDP. It ran in Interop mode, which is a mode of the program where data is transmitted without having to be read from disk.<\/p>\n<p>Simple volume tests of 2GB were executed to show performance differences between native UDP and XDP UDP. A profiler was used to examine the effect of XDP UDP versus native UDP in the program to help identify specific bottlenecks.<\/p>\n<h3>Hardware and Software<\/h3>\n<h4>Server machine:<\/h4>\n<ul>\n<li>OS: Lubuntu v19.04<\/li>\n<li>CPU: Xeon E7 2.4 Ghz<\/li>\n<li>Host: Proxmox 6.1<\/li>\n<li>Host NIC: Intel X540-AT2 10Gb dual port NIC.<\/li>\n<li>VM type: KVM<\/li>\n<li>VM Memory: 8 GB<\/li>\n<li>VM NIC: virtio_net driver (it has specific performance improvements for XDP built in)<\/li>\n<\/ul>\n<h4>Client machine:<\/h4>\n<ul>\n<li>OS: Lubuntu v19.04<\/li>\n<li>CPU: Xeon E5 a 3.6 Ghz<\/li>\n<li>VM type: None.<\/li>\n<li>Memory: 8 GB<\/li>\n<li>NIC: Intel X540-AT2 10Gb dual port NIC.<\/li>\n<\/ul>\n<p>NOTE: A non-VM machine was chosen with a high speed data path so as to best simulate access to the wider internet in a controlled environment. Other works showing performance include <a href=\"https:\/\/github.com\/tohojo\/xdp-paper\/blob\/master\/benchmarks\/bench01_baseline.org#initial-data-from-jespers-runs\">this one<\/a>, which shows runs simply tossing away or moving data without any real involvement. In contrast, we believe our comparison is the first where a real application could be used.<\/p>\n<h2>The Tests and Results<\/h2>\n<p>3 runs in a row were performed by the client machine pointing to the server machine which was configured to run in native UDP mode. Results reported were:<\/p>\n<ul>\n<li>Test 1: 111,036,768 bytes\/sec<\/li>\n<li>Test 2: 98,336,240 bytes\/sec<\/li>\n<li>Test 3: 96,860,121 bytes\/sec<\/li>\n<\/ul>\n<p>Resulting in an average of 102,077,709 bytes per second.<\/p>\n<p>The server was restarted to run in XDP UDP mode and the client tests were repeated:<\/p>\n<ul>\n<li>Test 1: 152,644,777 bytes\/sec<\/li>\n<li>Test 2: 147,426,653 bytes\/sec<\/li>\n<li>Test 3: 138,277,563 bytes\/sec<\/li>\n<\/ul>\n<p>Resulting in an average of 146,116,331 bytes per second.<\/p>\n<h3>XDP Gets the Win<\/h3>\n<p>This is a 43% performance improvement by changing from native UDP sends to XDP UDP sends.<\/p>\n<p>By examining the <code>top<\/code> program on all runs we could see that the server program was running at 100% or nearly so. On the client machine the CPU differences were significant. When running in XDP UDP mode the CPU utilization was close to or exceeding 70%. When running in native UDP mode the CPU utilization was less than 60%. This implies that the limit was in the CPU on the server system, which is where the UDP transmissions were occurring.<\/p>\n<h2>Flame Graph from perf Profiling<\/h2>\n<p>Since there\u2019s such a significant difference in performance a profiler was used to see exactly where the program was spending its time. In particular the profiler <code>perf<\/code> was used as it provides great detail.<\/p>\n<h3>Flame Graph for native UDP<\/h3>\n<p>Processing the perf output with FlameGraph produces:<\/p>\n<p><a href=\"https:\/\/lswp.store\/wp-content\/uploads\/2020\/05\/UDP.svg\"><img decoding=\"async\" class=\"aligncenter wp-image-11769 size-large\" src=\"https:\/\/lswp.store\/wp-content\/uploads\/2020\/06\/UDP-1024x1015.png\" alt=\"\" width=\"1024\" height=\"1015\" srcset=\"https:\/\/lswp.store\/wp-content\/uploads\/2020\/06\/UDP-1024x1015.png 1024w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/06\/UDP-300x298.png 300w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/06\/UDP-150x150.png 150w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/06\/UDP-768x762.png 768w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/06\/UDP-600x595.png 600w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/06\/UDP-100x100.png 100w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/06\/UDP.png 1200w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/p>\n<p>In a <a href=\"https:\/\/lswp.store\/wp-content\/uploads\/2020\/05\/UDP.svg\">flamegraph<\/a> the widest (horizontal) bars take the most CPU. As you go up the graph, you go deeper into the call stack. At the bottom is <code>http_server<\/code>. As you go up, you go into the program. The bar stays very wide until <code>send_packets_out<\/code>, an LSQUIC function that has two expensive children, <code>gquic_encrypt_packet<\/code> and <code>send_batch<\/code>, the latter being the more expensive of the two. Following <code>send_batch<\/code> it continues staying very wide into <code>__libc_sendmsg<\/code> where it leaves the program entirely and is routed into the kernel. From this we can determine that the kernel overhead is by far the most CPU intensive cost.<\/p>\n<h3>Flame Graph for XDP<\/h3>\n<p>Running the same perf run and FlameGraph with XDP results in:<\/p>\n<p><a href=\"https:\/\/lswp.store\/wp-content\/uploads\/2020\/05\/XDP.svg\"><img decoding=\"async\" class=\"aligncenter wp-image-11771 size-large\" src=\"https:\/\/lswp.store\/wp-content\/uploads\/2020\/06\/XDP-576x1024.png\" alt=\"QUIC with XDP UDP\" width=\"576\" height=\"1024\" srcset=\"https:\/\/lswp.store\/wp-content\/uploads\/2020\/06\/XDP-576x1024.png 576w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/06\/XDP-169x300.png 169w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/06\/XDP-768x1366.png 768w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/06\/XDP-864x1536.png 864w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/06\/XDP-1152x2048.png 1152w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/06\/XDP-600x1067.png 600w, https:\/\/lswp.store\/wp-content\/uploads\/2020\/06\/XDP.png 1200w\" sizes=\"(max-width: 576px) 100vw, 576px\" \/><\/a><\/p>\n<p>This <a href=\"https:\/\/lswp.store\/wp-content\/uploads\/2020\/05\/XDP.svg\">flamegraph<\/a> has a much faster separation into narrower functions indicating that there are more contributors to CPU cost, in other words, not a single bottleneck. As above the widest row stays wide going into the program until <code>send_packets<\/code> out where it breaks into <code>gquic_encrypt_packet<\/code> and <code>send_batch<\/code>, where <code>send_batch<\/code> is the slightly wider function. The entry point into the kernel is <code>__libc_sendto<\/code> where it is barely the widest row at that depth indicating that CPU overhead of encryption is nearly as expensive as transmission.<\/p>\n<p>This gives us an indication that the kernel overhead is substantially reduced in XDP and explains the performance improvements.<\/p>\n<h2>Conclusions<\/h2>\n<p>The goal of this blog post was to determine where the bottlenecks in QUIC and HTTP\/3 are and to see if the XDP UDP interface could help in improving the overall performance. The results of the tests show quite convincingly that XDP reduces the kernel penalty of native UDP.<\/p>\n<p>While XDP UDP is not a perfect solution for many environments as it is Linux specific it appears that for Linux web servers implementing XDP will result in higher overall performance.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Goal: Determine whether any performance gain can be achieved with XDP UDP versus kernel UDP HTTP\/3 uses QUIC which in turn uses UDP (User Datagram Protocol) rather than TCP\/IP for communications. XDP (eXpress Data Path) has been added recently to several operating systems to provide User Space applications performance comparable to direct kernel applications. The [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":11326,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[20],"tags":[171,238,289,322],"class_list":["post-11324","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-performance","tag-lsquic","tag-quic","tag-udp","tag-xdp"],"jetpack_featured_media_url":"https:\/\/lswp.store\/wp-content\/uploads\/2020\/05\/QUIC-UDP-XDP.png","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/posts\/11324","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/comments?post=11324"}],"version-history":[{"count":0,"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/posts\/11324\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/media\/11326"}],"wp:attachment":[{"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/media?parent=11324"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/categories?post=11324"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lswp.store\/index.php\/wp-json\/wp\/v2\/tags?post=11324"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}