File
Blob: src/workerd/tests/bench-api-headers.c++
| 1 | // Copyright (c) 2023 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | |
| 5 | #include <workerd/api/http.h> |
| 6 | #include <workerd/tests/bench-tools.h> |
| 7 | #include <workerd/tests/test-fixture.h> |
| 8 | |
| 9 | // A benchmark for js Header class. |
| 10 | |
| 11 | namespace workerd { |
| 12 | namespace { |
| 13 | |
| 14 | struct ApiHeaders: public benchmark::Fixture { |
| 15 | virtual ~ApiHeaders() noexcept(true) {} |
| 16 | |
| 17 | struct Header { |
| 18 | bool append; |
| 19 | kj::StringPtr name; |
| 20 | kj::StringPtr value; |
| 21 | }; |
| 22 | |
| 23 | void SetUp(benchmark::State& state) noexcept(true) override { |
| 24 | fixture = kj::heap<TestFixture>(); |
| 25 | |
| 26 | kj::HttpHeaderTable::Builder builder; |
| 27 | builder.add("Host"); |
| 28 | builder.add("Accept"); |
| 29 | builder.add("Content-Type"); |
| 30 | builder.add("Last-Modified"); |
| 31 | table = builder.build(); |
| 32 | kjHeaders = kj::heap<kj::HttpHeaders>(*table); |
| 33 | auto in = kj::heapString( |
| 34 | "GET /favicon.ico HTTP/1.1\r\n" |
| 35 | "Host: 0.0.0.0=5000\r\n" |
| 36 | "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n" |
| 37 | "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" |
| 38 | "Accept-Language: en-us,en;q=0.5\r\n" |
| 39 | "Accept-Encoding: gzip,deflate\r\n" |
| 40 | "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" |
| 41 | "Keep-Alive: 300\r\n" |
| 42 | "Connection: keep-alive\r\n" |
| 43 | "\r\n"); |
| 44 | KJ_EXPECT(kjHeaders->tryParseRequest(in.asArray()).is<kj::HttpHeaders::Request>()); |
| 45 | original = kj::mv(in); |
| 46 | } |
| 47 | |
| 48 | void TearDown(benchmark::State& state) noexcept(true) override { |
| 49 | fixture = nullptr; |
| 50 | } |
| 51 | |
| 52 | kj::Maybe<kj::String> original; |
| 53 | kj::Own<TestFixture> fixture; |
| 54 | kj::Own<kj::HttpHeaderTable> table; |
| 55 | kj::Own<kj::HttpHeaders> kjHeaders; |
| 56 | Header kHeaders[13] = {Header{false, "Host"_kj, "example.com"_kj}, |
| 57 | Header{false, "User-Agent"_kj, |
| 58 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"_kj}, |
| 59 | Header{false, "Accept"_kj, |
| 60 | "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"_kj}, |
| 61 | Header{false, "Accept-Language"_kj, "en-US,en;q=0.9"_kj}, |
| 62 | Header{false, "Accept-Encoding"_kj, "gzip, deflate, br"_kj}, |
| 63 | Header{false, "Content-Type"_kj, "application/json; charset=utf-8"_kj}, |
| 64 | Header{false, "Authorization"_kj, |
| 65 | "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0"_kj}, |
| 66 | Header{false, "Cache-Control"_kj, "no-cache, no-store, must-revalidate"_kj}, |
| 67 | Header{false, "Content-Length"_kj, "1234"_kj}, |
| 68 | Header{false, "Referer"_kj, "https://www.example.com/page?query=value&other=param"_kj}, |
| 69 | Header{false, "X-Forwarded-For"_kj, "203.0.113.1, 198.51.100.17"_kj}, |
| 70 | Header{true, "Set-Cookie"_kj, "new_session=token123; Path=/; Secure; HttpOnly"_kj}, |
| 71 | Header{true, "Set-Cookie"_kj, "new_session=token124; Path=/abc; Secure; HttpOnly"_kj}}; |
| 72 | }; |
| 73 | |
| 74 | // initialization performs a lot of copying, benchmark it |
| 75 | BENCHMARK_F(ApiHeaders, constructor)(benchmark::State& state) { |
| 76 | fixture->runInIoContext([&](const TestFixture::Environment& env) { |
| 77 | auto& js = env.js; |
| 78 | for (auto _: state) { |
| 79 | for (size_t i = 0; i < 10000; ++i) { |
| 80 | benchmark::DoNotOptimize( |
| 81 | js.alloc<api::Headers>(js, *kjHeaders, api::Headers::Guard::REQUEST)); |
| 82 | benchmark::DoNotOptimize(i); |
| 83 | } |
| 84 | } |
| 85 | }); |
| 86 | } |
| 87 | |
| 88 | BENCHMARK_F(ApiHeaders, set_append)(benchmark::State& state) { |
| 89 | fixture->runInIoContext([&](const TestFixture::Environment& env) { |
| 90 | auto& js = env.js; |
| 91 | for (auto _: state) { |
| 92 | for (size_t i = 0; i < 1000; ++i) { |
| 93 | auto headers = js.alloc<api::Headers>(); |
| 94 | // Set common headers with various representative lengths |
| 95 | for (auto& h: kHeaders) { |
| 96 | if (h.append) { |
| 97 | headers->append(env.js, kj::str(h.name), kj::str(h.value)); |
| 98 | } else { |
| 99 | headers->set(env.js, kj::str(h.name), kj::str(h.value)); |
| 100 | } |
| 101 | } |
| 102 | benchmark::DoNotOptimize(i); |
| 103 | } |
| 104 | } |
| 105 | }); |
| 106 | } |
| 107 | } // namespace |
| 108 | } // namespace workerd |