Skip to content
File

Blob: src/workerd/tests/bench-response.c++

3.7 KB
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/api/streams/readable-source.h>
7#include <workerd/tests/bench-tools.h>
8#include <workerd/tests/test-fixture.h>
9 
10// A benchmark for Response object construction to identify performance bottlenecks.
11 
12namespace workerd {
13namespace {
14 
15struct Response: public benchmark::Fixture {
16 virtual ~Response() noexcept(true) {}
17 
18 void SetUp(benchmark::State& state) noexcept(true) override {
19 fixture = kj::heap<TestFixture>();
20 }
21 
22 void TearDown(benchmark::State& state) noexcept(true) override {
23 fixture = nullptr;
24 }
25 
26 kj::Own<TestFixture> fixture;
27};
28 
29// Benchmark: Simple string body Response (most common case)
30// Pattern: new Response("Hello World")
31BENCHMARK_F(Response, simpleStringBody)(benchmark::State& state) {
32 fixture->runInIoContext([&](const TestFixture::Environment& env) {
33 auto& js = env.js;
34 for (auto _: state) {
35 auto body = api::Body::Initializer(kj::str("Hello World"));
36 benchmark::DoNotOptimize(api::Response::constructor(js, kj::mv(body), kj::none));
37 }
38 });
39}
40 
41// Benchmark: Response with empty body (null)
42// Pattern: new Response(null, {status: 404})
43BENCHMARK_F(Response, nullBodyWithStatus)(benchmark::State& state) {
44 fixture->runInIoContext([&](const TestFixture::Environment& env) {
45 auto& js = env.js;
46 for (auto _: state) {
47 api::Response::InitializerDict init;
48 init.status = 404;
49 benchmark::DoNotOptimize(api::Response::constructor(js, kj::none, kj::mv(init)));
50 }
51 });
52}
53 
54// Benchmark: Response with headers
55// Pattern: new Response("body", {headers: {"Content-Type": "text/html"}})
56BENCHMARK_F(Response, bodyWithHeaders)(benchmark::State& state) {
57 fixture->runInIoContext([&](const TestFixture::Environment& env) {
58 auto& js = env.js;
59 for (auto _: state) {
60 api::Response::InitializerDict init;
61 jsg::Dict<kj::String, kj::String> headersDict;
62 headersDict.fields = kj::heapArray<jsg::Dict<kj::String, kj::String>::Field>(1);
63 headersDict.fields[0].name = kj::str("Content-Type");
64 headersDict.fields[0].value = kj::str("text/html");
65 init.headers = kj::mv(headersDict);
66 
67 auto body = api::Body::Initializer(kj::str("Hello World"));
68 benchmark::DoNotOptimize(api::Response::constructor(js, kj::mv(body), kj::mv(init)));
69 }
70 });
71}
72 
73// Benchmark: Response with ArrayBuffer body
74// Pattern: new Response(arrayBuffer)
75BENCHMARK_F(Response, arrayBufferBody)(benchmark::State& state) {
76 fixture->runInIoContext([&](const TestFixture::Environment& env) {
77 auto& js = env.js;
78 for (auto _: state) {
79 auto bytes = kj::heapArray<byte>(11);
80 memcpy(bytes.begin(), "Hello World", 11);
81 auto view = bytes.asPtr();
82 auto rs = api::streams::newMemorySource(view, kj::heap(kj::mv(bytes)));
83 auto body =
84 api::Body::Initializer(env.js.alloc<api::ReadableStream>(env.context, kj::mv(rs)));
85 benchmark::DoNotOptimize(api::Response::constructor(js, kj::mv(body), kj::none));
86 }
87 });
88}
89 
90// Benchmark: Response.json()
91// Pattern: Response.json({key: "value"})
92BENCHMARK_F(Response, jsonResponse)(benchmark::State& state) {
93 fixture->runInIoContext([&](const TestFixture::Environment& env) {
94 auto& js = env.js;
95 // Prepare object to serialize. Do this outside the loop to avoid measuring its repeated
96 // construction cost. What we want to measure is just the cost of the api::Response::json_ call.
97 auto obj = js.obj();
98 obj.set(js, "key"_kj, js.str("value"_kj));
99 for (auto _: state) {
100 benchmark::DoNotOptimize(api::Response::json_(js, obj, kj::none));
101 }
102 });
103}
104 
105} // namespace
106} // namespace workerd